/*
 * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it would be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * Further, this software is distributed without any warranty that it is
 * free of the rightful claim of any third person regarding infringement
 * or the like.  Any license provided herein, whether implied or
 * otherwise, applies only to this software file.  Patent licenses, if
 * any, provided herein do not apply to combinations of this program with
 * other software, or any other product whatsoever.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
 * 
 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
 * Mountain View, CA  94043, or:
 * 
 * http://www.sgi.com 
 * 
 * For further information regarding this notice, see: 
 * 
 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "fl.h"
#include "flfreetype.h"

#include FT_OUTLINE_H

typedef unsigned short SbUCS2;


#ifndef PI
#define PI			3.141592654
#endif

#define RADIAN(deg)		((deg)/180.0*PI)

#define KLUDGE_FACTOR		80.0
#define PIXEL_ROW_ALIGNMENT	4

/* Bitmap precisions */
#define BM_PRECISION_BITS	6
#define BM_PRECISION		(1 << BM_PRECISION_BITS)
#define BM_PRECISION_MASK	((~0UL) << BM_PRECISION_BITS)

#define BM_FLOOR(x)		((x) & BM_PRECISION_MASK)
#define BM_CEILING(x)		(((x) + BM_PRECISION - 1) & BM_PRECISION_MASK)
#define BM_TRUNC(x)		((signed long)(x) >> BM_PRECISION_BITS)

static FT_Library		library;

GLboolean
_flFTInitialize(void)
{
  FT_Error error;

  error = FT_Init_FreeType(&library);
  if (error) {
    fprintf(stderr, "_flFTInitialize: initialise FreeType failed\n");
    return FALSE;
  }

  return TRUE;	/* OK */
}

void
_flFTShutdown(void)
{
  /* TODO */
}

FLfontStruct *
_flFTCreateFont(const GLubyte *fontName,
		GLfloat	       mat[2][2],
		GLint	       charNameCount,
		GLubyte **     charNameVector)
{
  FLfontStruct *fs;
  char *	path;
  FT_Face	face;
  FT_Error	error;

  if (! (path = _flSearchFont(fontName)))
    return NULL;

  error = FT_New_Face(library, path, 0, &face);

  free(path);

  if (! error) {
    int px = (mat[0][0] == 1.0) ? 32 : mat[0][0];
    int py = (mat[1][1] == 1.0) ? 32 : mat[1][1];

    error = FT_Set_Char_Size(face, px << 6, py << 6, mat[0][1], mat[1][0]);

    if (! error) {
      fs = (FLfontStruct *) _flFTNewGlyphFont(fontName, face);
      return fs;
    }
  }

  return NULL;
}

void
_flFTDestroyFont(FLfontStruct *fs)
{
  FLFreeTypeFontStruct *fontStruct = (FLFreeTypeFontStruct *) fs;
  FT_Face face = fontStruct->face;
  FT_Done_Face(face);
}

FLscalableBitmap *
_flFTGetScalableBitmap(FLfontStruct *_fs, GLuint c)
{
  FLscalableBitmap *bit3;

  TRACE(("unfinished code called"));

  /* TODO */
  bit3 = NULL;

  return bit3;
}

static void
bitmap_convert(int width, int height, int pitch, int pitch2, char *src, char *dst)
{
  int i, j, c;
  unsigned char bit;
  char *p;

  for (j = 0; j < height; j++) {
    bit = 0x80;
    p = src;
    c = *p;
    for (i = 0; i < width; i++) {
      if ((c & bit))
	dst[(height - 1 - j) * pitch2 + i / 8] |= (1 << (7 - (i & 7)));

      bit >>= 1;
      if (! bit) {
	bit = 0x80;
	p++;
	c = *p;
      }
    }

    src += pitch;
  }
}

FLbitmap *
_flFTGetBitmap(FLfontStruct *_fs, GLuint c)
{
  FLFreeTypeFontStruct *fs = (FLFreeTypeFontStruct *) _fs;
  FLFreeTypeOutline *outline = (FLFreeTypeOutline *) _flFTGetOutline(_fs, c);
  FT_Face face = fs->face;
  FT_GlyphSlot glyph = outline->glyph;
  FT_Bitmap  bit2;
  FLbitmap *bit3;
  FT_Error error;
  int width, height, pitch, size, xadvance;
  FT_Pos left, right, top, bottom;
  FT_Pos bbox_width, bbox_height;
  FT_Pos bearing_x, bearing_y;
  int pitch2, size2;

  TRACE(("_flFTGetBitmap: '%c'(0x%04x)\n", c, c));

  /* See if it is already created */
  bit3 = &outline->bitmap;
  if (bit3->bitmap)
    return bit3;

  /* No, proceed to create it */
  left        = BM_FLOOR(glyph->metrics.horiBearingX);
  right       = BM_CEILING(glyph->metrics.horiBearingX + glyph->metrics.width);
  width       = BM_TRUNC(right);
  xadvance    = BM_TRUNC(glyph->metrics.horiAdvance);
    
  top         = BM_CEILING(glyph->metrics.horiBearingY);
  bottom      = BM_FLOOR(glyph->metrics.horiBearingY - glyph->metrics.height);
  height      = BM_TRUNC(top - bottom);

  bearing_x   = BM_TRUNC(glyph->metrics.horiBearingX);
  bearing_y   = BM_TRUNC(glyph->metrics.horiBearingY);

  bbox_width  = BM_TRUNC(face->bbox.xMax - face->bbox.xMin);
  bbox_height = BM_TRUNC(face->bbox.yMax - face->bbox.yMin);

  if (glyph->format == ft_glyph_format_outline) {    
    TRACE(("  creating bitmap from outline glyph\n"));

    pitch     = ((((width + 7) >> 3) + 1) >> 1) << 1; /* has to be even */
    size      = pitch * height;
    pitch2    = ((width + (PIXEL_ROW_ALIGNMENT << 3) - 1) >> 5) << 2;
    size2     = pitch2 * height;

    bit2.width      = width;
    bit2.rows       = height;
    bit2.pitch      = pitch;
    bit2.pixel_mode = ft_pixel_mode_mono;
    bit2.buffer     = (GLubyte *) malloc(size * sizeof(GLubyte));

    bit3->width     = bit2.width;
    bit3->height    = bit2.rows;
    bit3->xorig     = bearing_x;
    bit3->yorig     = height - bearing_y;
    bit3->xmove     = xadvance > 0 ? xadvance : (bbox_width / 2.0);
    bit3->ymove     = 0.0;
    bit3->bitmap    = (GLubyte *) malloc(size2 * sizeof(GLubyte));

    FT_Outline_Translate(&glyph->outline, 0, -bottom);
    memset(bit2.buffer, 0, size);
    memset(bit3->bitmap, 0, size2);

    if (fs->lowPrec)
      glyph->outline.flags &= ~ft_outline_high_precision;
      
    error = FT_Outline_Get_Bitmap(library, &glyph->outline, &bit2);

    bitmap_convert(width, height, pitch, pitch2, bit2.buffer, bit3->bitmap);

    free(bit2.buffer);
  }
  else {
    TRACE(("  creating bitmap from non-outline glyph\n"));
    bit3->width     = glyph->bitmap.width;
    bit3->height    = glyph->bitmap.rows;
    bit3->xorig     = bearing_x;
    bit3->yorig     = height - bearing_y;
    bit3->xmove     = bit3->width > 0 ? bit3->width : (bbox_width / 2.0);
    bit3->ymove     = 0.0;
    bit3->bitmap    = glyph->bitmap.buffer;
  }

  /* debugging added by Steve, 2001-08-14 */
  TRACE(("  glyph->metrics.horiBearingX = %ld\n", glyph->metrics.horiBearingX));
  TRACE(("  glyph->metrics.width = %ld\n", glyph->metrics.width ));
  TRACE(("  left = %ld\n", left ));
  TRACE(("  right = %ld\n", right ));
  TRACE(("  width = %d\n", width ));
  TRACE(("  height = %d\n", height ));
  TRACE(("  face->bbox.xMax = %ld\n", face->bbox.xMax ));
  TRACE(("  face->bbox.xMin = %ld\n", face->bbox.xMin ));
  TRACE(("  bit3->xmove = %f\n", bit3->xmove ));
  TRACE(("  bbox_width = %ld\n", bbox_width ));

  return bit3;
}

FLoutline *
_flFTGetOutline(FLfontStruct *_fs, GLuint c)
{
  FLFreeTypeFontStruct *fs = (FLFreeTypeFontStruct *) _fs;
  FLFreeTypeOutline *outline;
  int start, end, num, code, i;

  TRACE(("_flFTGetOutline: '%c'(0x%04x)\n", c, c));

  // Search for the code point c in the 8 bit range
  if (c < 256) {
    outline = fs->char8[c];
    if (! outline)
      fs->char8[c] = outline = _flFTLoadChar(fs, c);
    return (FLoutline *) outline;
  }

  // Search for the code point c in the 16 bit range
  start = 0;
  end   = fs->num16 - 1;

  for (i = end >> 1; start < end; i = (start + end) >> 1) {
    code = fs->index16[i];
    if (c < code)
      end = i - 1;
    else if (c > code)
      start = i + 1;
    else
      return (FLoutline *) fs->char16[i];
  }

  // Cannot find code point c, load the glyph char
  outline = _flFTLoadChar(fs, c);
  if (outline) {
    FLchar *newIndex16;
    FLFreeTypeOutline **newChar16;

    fs->num16++;
    newIndex16 = (FLchar *) malloc(fs->num16 * sizeof(FLchar));
    newChar16  = (FLFreeTypeOutline **) malloc(fs->num16 * sizeof(FLFreeTypeOutline *));

    if (fs->index16) {
      if (start > 0) {
	memcpy(newIndex16, fs->index16, sizeof(FLchar) * start);
	memcpy(newChar16, fs->char16, sizeof(FLFreeTypeOutline *) * start);
      }

      if ((num = fs->num16 - start - 1) > 0) {
	memcpy(newIndex16 + start + 1, fs->index16 + start, sizeof(FLchar) * num);
	memcpy(newChar16 + start  + 1, fs->char16 + start, sizeof(FLFreeTypeOutline *) * num);
      }

      free(fs->index16);
      free(fs->char16);
    }

    fs->index16 = newIndex16;
    fs->char16  = newChar16;

    fs->index16[start] = c;
    fs->char16[start]  = outline;
  }
    
  return (FLoutline *) outline;
}

FLbitmap *
_flFTUniGetBitmap(FLfontStruct **fsList, GLubyte *UCS2)
{
  FLfontStruct *fs;
  FLbitmap *bitmap;
  GLuint c = (UCS2[0] << 8) | UCS2[1];

  TRACE(("_flFTUniGetBitmap: 0x%04x\n", c));
  while ((fs = *fsList++))
    if ((bitmap = _flFTGetBitmap(fs, c)))
      return bitmap;

  return NULL;
}

FLoutline *
_flFTUniGetOutline(FLfontStruct **fsList, GLubyte *UCS2)
{
  FLfontStruct *fs;
  FLoutline *outline;
  GLuint c = (UCS2[0] << 8) | UCS2[1];

  TRACE(("_flFTUniGetOutline: 0x%04x\n", c));
  while ((fs = *fsList++))
    if ((outline = _flFTGetOutline(fs, c)))
      return outline;

  return NULL;
}

/*------------------------------------------------------------------*/

FLFreeTypeOutline *
_flFTNewGlyphChar(FLFreeTypeFontStruct *fs, FLchar c, FT_GlyphSlot slot)
{
  FLFreeTypeOutline *outline;
  FLbitmap *bitmap;

  TRACE(("_flFTNewGlyphChar: '%c'(0x%x), slot=%p\n",
	 c < 128 ? c : '?', c, slot));

  if (! (outline = (FLFreeTypeOutline *) malloc(sizeof(FLFreeTypeOutline))))
    return NULL;

  outline->ch          = c;
  outline->size.x      = 0.0;
  outline->size.y      = 0.0;
  outline->advance     = 0.0;
  outline->indexes     = NULL;
  outline->tessIndexes = NULL;
  outline->numVertices = 0;
  outline->vertices    = NULL;
  outline->glyph       = slot;

  _flFTGenerateGlyph(outline);

  bitmap               = &outline->bitmap;
  bitmap->width        = 0;
  bitmap->height       = 0;
  bitmap->xorig        = 0.0;
  bitmap->yorig        = 0.0;
  bitmap->xmove        = 0.0;
  bitmap->ymove        = 0.0;
  bitmap->bitmap       = NULL;

  return outline;
}

void
_flFTDeleteGlyphChar(FLFreeTypeOutline *outline)
{
  TRACE(("_flFTDeleteGlyphChar: outline=%p\n", outline));

  if (outline) {
    // should be freed automatically together with font
    //FT_Done_GlyphSlot(outline->glyph);
  }
}

short
_flFTGetNumVertices(FLFreeTypeOutline *outline)
{
  if (outline) {
    if (! outline->indexes)
      ; /*  _flFTGenerateGlyph(); */
    return outline->numVertices;
  }

  return 0;
}

short *
_flFTGetIndices(FLFreeTypeOutline *outline)
{
  if (outline) {
    if (! outline->indexes)
      ; /*  _flFTGenerateGlyph(); */
    return outline->indexes;
  }

  return NULL;
}

FLpt2 *
_flFTGetVertices(FLFreeTypeOutline *outline)
{
  if (outline) {
    if (! outline->indexes)
      ; /* _flFTGenerateGlyph(); */
    return outline->vertices;
  }

  return NULL;
}

GLboolean *
_flFTGetPolygonFlags(FLFreeTypeOutline *outline)
{
  if (outline) {
    if (! outline->indexes)
      ; /* _flFTGenerateGlyph(); */
    return outline->polygons;
  }

  return NULL;
}

/*------------------------------------------------------------------*/

FLFreeTypeFontStruct *
_flFTNewGlyphFont(const char *fontName, FT_Face newFace)
{
  FLFreeTypeFontStruct *fs;

  if (! (fs = (FLFreeTypeFontStruct *) malloc(sizeof(FLFreeTypeFontStruct))))
    return NULL;

  fs->char8      = (FLFreeTypeOutline **) malloc(256 * sizeof(FLFreeTypeOutline *));
  fs->char16     = NULL;
  fs->index16    = NULL;
  fs->num16      = 0;
  memset(fs->char8, 0, 256 * sizeof(FLFreeTypeOutline *));

  fs->name       = strdup(fontName);
  fs->index16    = NULL;
  fs->face       = newFace;
  fs->hint       = TRUE;
  fs->grayRender = TRUE;
  fs->lowPrec    = FALSE;

  fs->bound.x = (newFace->bbox.xMax - newFace->bbox.xMin) >> 5;
  fs->bound.y = (newFace->bbox.yMax - newFace->bbox.yMin) >> 5;

  fs->bound.x /= KLUDGE_FACTOR;
  fs->bound.y /= KLUDGE_FACTOR;
  if (fs->bound.y < 0.6)
    fs->bound.y = 1.2;

#if 0
  {
    int i;

    printf(">>> num_charmaps=%d\n", newFace->num_charmaps);
    for (i = 0; i < newFace->num_charmaps; i++) {
      char *s = "???";
      switch (newFace->charmaps[i]->encoding) {
      case ft_encoding_symbol:	       s = "symbol"; break;
      case ft_encoding_unicode:	       s = "unicode"; break;
      case ft_encoding_latin_2:	       s = "latin_2"; break;
      case ft_encoding_sjis:	       s = "sjis"; break;
      case ft_encoding_big5:	       s = "big5"; break;
      case ft_encoding_adobe_standard: s = "adobe_standard"; break;
      case ft_encoding_adobe_expert:   s = "adobe_expert"; break;
      case ft_encoding_adobe_custom:   s = "adobe_custom"; break;
      case ft_encoding_apple_roman:    s = "apple_roman"; break;
      }
      printf(">>>   %d. encoding=%s, platform_id=%d, encoding_id=%d\n",
	     i, s, newFace->charmaps[i]->platform_id, newFace->charmaps[i]->encoding_id);
    }
  }
#endif

  return fs;
}

void
_flFTDeleteGlyphFont(FLFreeTypeFontStruct *fs)
{
  if (fs) {
    if (fs->name) free(fs->name);
    if (fs->face) FT_Done_Face(fs->face);

    free(fs);
  }
}

FLFreeTypeOutline *
_flFTLoadChar(FLFreeTypeFontStruct *fs, FLchar c)
{
  FLFreeTypeOutline *ch;
  FLoutline *outline;
  FT_UInt i;
  FT_Error error;
  GLshort *vertexcount;
  FLpt2 **vertex, *v;
  short *index, *start, *end;
  int flags, j;

  flags = FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP;
  if (! fs->hint)
    flags |= FT_LOAD_NO_HINTING;

  i = FT_Get_Char_Index(fs->face, c);
  error = FT_Load_Glyph(fs->face, i, flags);

  ch = error ? NULL : _flFTNewGlyphChar(fs, c, fs->face->glyph);
  if (! ch) {
    TRACE(("_flFTLoadChar: c='%c'(%d) failed, error=%d\n", c, c, error));
    return NULL;
  }

  /* Fix character advancement */
  if (ch->size.x == 0.0)
    ch->advance = (fs->face->max_advance_width >> 6) / KLUDGE_FACTOR;

  TRACE(("_flFTLoadChar: c='%c'(%d), numVertices=%d\n",
	 c, c, ch->numVertices));

  /* Fill up FL outline struct */
  outline = &ch->outline;
  outline->outlinecount = 0;
  outline->xadvance = ch->advance;
  outline->yadvance = 0.0;

  if (ch->numVertices == 0) {
    outline->vertexcount = NULL;
    outline->vertex = NULL;
    return ch;
  }

  /* Count the number of outline contours */
  for (index = ch->indexes ; *index != -1; index++, outline->outlinecount++)
    for (index++; *index != -1; index++)
      ;

  /* Create the vertex count and vertex arrays */
  vertexcount = (GLshort *) malloc(outline->outlinecount * sizeof(GLshort));
  vertex = (FLpt2 **) malloc(outline->outlinecount * sizeof(FLpt2 *));
  outline->vertexcount = vertexcount;
  outline->vertex = vertex;

  for (index = ch->indexes ; *(start = index) != -1; index++) {
    for (index++; *index != -1; index++)
      ;
    *vertexcount = index - start;
    *vertex = (FLpt2 *) malloc(*vertexcount * sizeof(FLpt2));
    v = *vertex;

    end = index--;
    while (index >= start) {
      j = *index--;
      v->x = ch->vertices[j].x;
      v->y = ch->vertices[j].y;
      v++;
    }
    index = end;

    vertexcount++;
    vertex++;
  }

  return ch;
}

/*------------------------------------------------------------------*/

/*

  The following code was adapted from FreeType2's scan convertion
  code in rasterization routines (ftraster.c).

  The function FT_Outline_Decompose() is called to decompose the
  outlines into series of Move_To, Line_To, Conic_To, and Cubic_To
  functions. In Conic_To and Cubic_To functions, the given curve is
  repeatedly split into halves with Split_Conic and Splic_Cubic,
  respcetively, until they get segments straight enough to be
  approximated with lines, ie. until the angles of a segment's
  bounding triangle or quadrangle corresponding to its endpoints are
  small enough (plus there should be some additional criterion for 
  cubic curves with an inflexion (ie. S-shaped curves)).

*/

#define FT_RASTER_CONSTANT_PRECISION
/* #define FT_DYNAMIC_BEZIER_STEPS */
#define FT_PRECISION_BITS    8

#undef  FAILURE
#define FAILURE  TRUE

#undef  SUCCESS
#define SUCCESS  FALSE

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

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

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

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

/* Error codes returned by the scan-line converter/raster. */
#define ErrRaster_Ok                     0
#define ErrRaster_Uninitialized_Object   1
#define ErrRaster_Overflow               2
#define ErrRaster_Negative_Height        3
#define ErrRaster_Invalid_Outline        4
#define ErrRaster_Invalid_Map            5
#define ErrRaster_AntiAlias_Unsupported  6
#define ErrRaster_Invalid_Pool           7
#define ErrRaster_Unimplemented          8
#define ErrRaster_Bad_Palette_Count      9

#define Flow_Up     1
#define Flow_Down  -1

#define SET_High_Precision(p)	Set_High_Precision (RAS_VAR_  p)

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

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

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

/* The lower and upper thresholds for determining whether to continue
 * splitting line segments and whether to reject a line segment as being
 * too straight.
 */
#define LOWER_THRESHOLD		RADIAN(160.0)
#define UPPER_THRESHOLD		RADIAN(170.0)

/* Some common types */
typedef unsigned char Byte, *PByte;
typedef int           TResult;

/* The type of the pixel coordinates used within the render pool during
 * scan-line conversion.  We use longs to store either 26.6 or 22.10
 * fixed float values, depending on the `precision' we want to use
 * (i.e., low resp. high precision).  These are ideals in order to
 * subdivise Bezier arcs in halves by simple additions and shifts.
 *
 * Note that this is an 8-bytes integer on 64 bits systems.
 */
typedef long TPos, *PPos;

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

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

/* States and directions of each line, arc, and face. */
typedef enum TDirection {
  Unknown,
  Ascending,
  Descending,
  Flat
} TDirection;

/* The `master' structure used for decomposing outlines. */
typedef struct TFace {
  struct TFace * link;	/* link to next face - various purpose */
  TPoint *offset;	/* start of face's data in render pool */
  int	  n_points;	/* number of points */
  int     flow;		/* Face orientation: Asc/Descending */
  struct TFace * next;	/* next face in same contour, used */
			/* during drop-out control */
} TFace;

/* The size in _TPos_ of a face record in the render pool. */
#define AlignFaceSize  \
  ((sizeof(TFace) + sizeof(TPos) - 1) / sizeof(TPos))

#ifdef FT_RASTER_CONSTANT_PRECISION

#define PRECISION_BITS    FT_PRECISION_BITS
#define PRECISION         (1 << PRECISION_BITS)
#define PRECISION_MASK    ((~0UL) << PRECISION_BITS)
#define PRECISION_HALF    (PRECISION >> 1)
#define PRECISION_JITTER  (PRECISION >> 5)
#define PRECISION_STEP    PRECISION_HALF

#else

#define PRECISION_BITS    ras.precision_bits
#define PRECISION         ras.precision
#define PRECISION_MASK    ras.precision_mask
#define PRECISION_HALF    ras.precision_half
#define PRECISION_JITTER  ras.precision_jitter
#define PRECISION_STEP    ras.precision_step

#endif

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

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

/* Get integer coordinate of a given 26.6 or 22.10 `x' coordinate -- no
 * rounding.
 */
#define TRUNC(x)	((signed long)(x) >> PRECISION_BITS)

/* Get the fractional part of a given coordinate. */
#define FRAC(x)		((x) & (PRECISION-1))

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

/* Show command for debugging. */
#if 0
#define SHOW_COMMAND(cmd, px, py) printf("%s %ld,%ld\n", cmd, px, py)
#else
#define SHOW_COMMAND(cmd, px, py)
#endif

/* Records a generated point */
#define ENTER_POINT(top, px, py, cmd) do {			\
    SHOW_COMMAND(cmd, px, py);					\
    top->x = px;						\
    top->y = py;						\
    top++;							\
  } while (0)

#define ENTER_POINT_CHECK(top, px, py, cmd) do {		\
    if (px != (top - 1)->x && py != (top - 1)->y) {		\
      ENTER_POINT(top, px, py, cmd);				\
    }								\
  } while (0)

/* The most used variables are at the beginning of the structure.  Thus,
 * their offset can be coded with less opcodes which results in a
 * smaller executable.
 */
typedef struct FT_RasterRec_ {
  TPoint *    cursor;		/* Current cursor in render pool */
  TPoint *    pool;		/* The render pool base address */
  TPoint *    pool_size;	/* The render pool's size */
  TPoint *    pool_limit;	/* Limit of faces zone in pool */

  TPoint      last;
  long        minY, maxY;
  long        minX, maxX;

  int         error;

#ifndef FT_RASTER_CONSTANT_PRECISION
  int         precision_bits;	/* precision related variables */
  int         precision;
  int         precision_half;
  long        precision_mask;
  int         precision_shift;
  int         precision_step;
  int         precision_jitter;
#endif

  FT_Outline *outline;

  int         n_points;		/* number of points in current glyph */
  int         n_contours;	/* number of contours in current glyph */
  int         n_extrema;	/* number of `extrema' scanlines */

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

  int         num_faces;	/* current number of faces */

  char        fresh;		/* signals a fresh new face which */
				/* `start' field must be completed */
  char        joint;		/* signals that the last arc ended */
				/* exactly on a scanline.  Allows */
				/* removal of doublets */
  TFace *     cur_face;		/* current face */
  TFace *     start_face;	/* head of linked list of faces */
  TFace *     first_face;	/* contour's first face in case */
				/* of impact */
  TDirection  state;	/* rendering state */

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

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

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


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

/*
 * Sets precision variables according to the parameter flag.
 */
static void
Set_High_Precision (RAS_ARG_ char  High)
{
#ifdef FT_RASTER_CONSTANT_PRECISION
  (void)High;
  (void)&ras;
#else
  if (High) {
    ras.precision_bits   = 10;
    ras.precision_step   = 128;
    ras.precision_jitter = 24;
  }
  else {
    ras.precision_bits   = 6;
    ras.precision_step   = 32;
    ras.precision_jitter = 2;
  }

  ras.precision       = 1L << ras.precision_bits;
  ras.precision_half  = ras.precision / 2;
  ras.precision_shift = ras.precision_bits - INPUT_BITS;
  ras.precision_mask  = -ras.precision;
#endif
}

/*
 * Creates a new Face in the render pool.
 */
static TResult
New_Face (RAS_ARG_ TDirection direction)
{
  if (ras.start_face == NULL) {
    ras.cur_face   = (TFace *)ras.cursor;	/* current face */
    ras.start_face = ras.cur_face;		/* first face in pool */
    ras.cursor    += AlignFaceSize;		/* record face in buffer */
  }

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

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

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

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

  /* initialize a few fields */
  {
    TFace *  cur = ras.cur_face;

    cur->offset = ras.cursor;	/* address of first coordinate */
    cur->link   = (TFace *)0;	/* link to next face in pool */
    cur->next   = (TFace *)0;	/* link to next face in contour */
  }

  /* record the first face in a contour */
  if (ras.first_face == NULL)
    ras.first_face = ras.cur_face;

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

  return SUCCESS;
}

/*
 * Finalizes the current Face and computes its height.  If it is
 * not 0, the face's fields are updated and a new face is
 * pushed on top of its coordinates.  Otherwise the current face
 * is kept and the recording of intersections is restarted.
 */
static TResult
End_Face (RAS_ARG)
{
  int  n;

  n = ras.cursor - ras.cur_face->offset;

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

  if (n > 0) {
    TFace *oldp, *newp;

    /* record scanline height in current face, create a new one
       and set a link from the old one to it */
    oldp           = ras.cur_face;
    oldp->n_points = n;
    ras.cur_face   = newp = (TFace *)ras.cursor;

    ras.cursor    += AlignFaceSize;

    newp->n_points = 0;
    newp->offset   = ras.cursor;
    oldp->next     = newp;

    ras.num_faces++;
  }

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

  ras.joint = FALSE;

  return SUCCESS;
}

#if 0
/*
 * Records that a given scanline contains at least one local
 * extremum.  The table of extrema is placed at the end of the render
 * pool and grows downwards.  It is used during the sweep phase.
 */
static TResult
Insert_Extrema (RAS_ARG_ TScan y)
{
  TPoint *extrema;
  TScan  y2;
  int    n;

  n       = ras.n_extrema - 1;
  extrema = ras.pool_size - ras.n_extrema;

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

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

  if (n < 0) {
    ras.pool_limit--;
    ras.n_extrema++;
    ras.pool_size[-ras.n_extrema] = y;

    if (ras.pool_limit <= ras.cursor) {
      ras.error = ErrRaster_Overflow;
      return FAILURE;
    }
  }
  return SUCCESS;
}
#endif /* 0 */

static int
Compute_Orientation (int n_points, TPoint *pt, FT_BBox *bbox)
{
  FLpt2 v1, v2;
  TPoint *p[3];
  int n;
  
  for (n = 0; n_points--; pt++)
    if (pt->x == bbox->xMin || pt->x == bbox->xMax ||
        pt->y == bbox->yMin || pt->y == bbox->yMax) {
      p[n++] = pt;
      
      if (n == 3)
        break;
    }

  v1.x = p[1]->x - p[0]->x;
  v1.y = p[1]->y - p[0]->y;
  v2.x = p[2]->x - p[1]->x;
  v2.y = p[2]->y - p[1]->y;
  return (v1.x * v2.y - v1.y * v2.x) > 0.0 ? 1 : -1;
}

/*
 * Adjusts all links in the faces list.  Called when the outline
 * parsing is done.
 */
int
_flFTFinalizeFaceTable(  FLFreeTypeOutline *ch, RAS_ARG)
{
  TFace *	p, *start;
  short *	index;
  GLboolean *	flag;
  FLpt2 *	vertex;
  FLpt2		vmin, vmax, v1;
  FT_BBox	bbox, lastBBox;
  GLboolean	isPolygon;
  int		direction, lastDirection;
  int		n, i, j;

  n = ras.num_faces;
  start = ras.start_face;

  if (n > 1) {
    p = ras.start_face;
    while (n > 0) {
      if (n > 1)
	p->link = (TFace *) (p->offset + p->n_points);
      else
	p->link = NULL;

#if 0
      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_Extrema (RAS_VAR_ bottom) ||
	  Insert_Extrema (RAS_VAR_ top+1))
	return FAILURE;
#endif

      p = p->link;
      n--;
    }
  }
  else
    ras.start_face = NULL;

  /* Compute the size require for indexes array. */
  ch->numVertices = 0;
  for (p = start, n = ras.num_faces; n--; p = p->link)
    ch->numVertices += p->n_points;

  index  = (short *) malloc ((ch->numVertices + ras.num_faces + 1) * sizeof(short));
  flag   = (GLboolean *) malloc (ras.num_faces * sizeof(GLboolean));
  vertex = (FLpt2 *) malloc (ch->numVertices * sizeof(FLpt2));

  /* Build up indexes and vertices from points, compute the size and
     character advance as well */
  ch->indexes = index;
  ch->polygons = flag;
  ch->vertices = vertex;
  lastDirection = 0;
  j = 0;

  vmin.x = vmin.y = 0.0;
  vmax.x = vmax.y = 0.0;

  for (p = start, n = ras.num_faces; n--; p = p->link) {
    TPoint *p0 = &p->offset[0];
    bbox.xMin = bbox.xMax = p0->x;
    bbox.yMin = bbox.yMax = p0->y;

    /* Store scaled points and assign indexs to each point */
    for (i = 0; i < p->n_points; i++) {
      TPoint *pt = &p->offset[i];
      
      /* Compute the bounding box of the contour */
      if (pt->x < bbox.xMin)
	bbox.xMin = pt->x;
      else if (pt->x > bbox.xMax)
	bbox.xMax = pt->x;

      if (pt->y < bbox.yMin)
	bbox.yMin = pt->y;
      else if (pt->y > bbox.yMax)
	bbox.yMax = pt->y;

      /* Scale the point */
      v1.x = pt->x >> 6;
      v1.y = pt->y >> 6;
      v1.x /= KLUDGE_FACTOR;
      v1.y /= KLUDGE_FACTOR;

      /* Compute the overall bounding box of the glyph */
      if (v1.x < vmin.x)
	vmin.x = v1.x;
      else if (v1.x > vmax.x)
	vmax.x = v1.x;

      if (v1.y < vmin.y)
	vmin.y = v1.y;
      else if (v1.y > vmax.y)
	vmax.y = v1.y;

      *vertex++ = v1;
      *index++ = j++;
    }
    *index++ = -1;

    /* Compute the orientation of the contour by looking at its normal */
    direction = Compute_Orientation(p->n_points, p->offset, &bbox);

    if (lastDirection)
      isPolygon = (direction == lastDirection || 
		   !(lastBBox.xMin < bbox.xMin && lastBBox.xMax > bbox.xMax &&
		     lastBBox.yMin < bbox.yMin && lastBBox.yMax > bbox.yMax));
    else
      isPolygon = TRUE;

    *flag++ = isPolygon;

    if (isPolygon) {
      lastDirection = direction;
      lastBBox = bbox;
    }
  }
  *index = -1;

  ch->size.x  = vmax.x - vmin.x;
  ch->size.y  = vmax.y - vmin.y;
  ch->advance = ch->size.x + 0.1;

  return SUCCESS;
}

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

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

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

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

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

  /* now compute the second and third order error values */

  wide_x = arc[0].x + arc[1].x - arc[2].x*2;
  wide_y = arc[0].y + arc[1].y - arc[2].y*2;

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

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

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

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

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

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

  if (threshold < PRECISION_STEP)
    threshold = PRECISION_STEP;

  return threshold;
}
#endif

static double
Bounding_Triangle_Angle(TPos x1, TPos y1, 
			TPos x,  TPos y,
			TPos x2, TPos y2)
{
  FLpt2 a, b;
  GLfloat a_len, b_len, len2;

  a.x = x1 - x;
  a.y = y1 - y;
  b.x = x2 - x;
  b.y = y2 - y;
  a_len = (GLfloat) sqrt(a.x * a.x + a.y * a.y);
  b_len = (GLfloat) sqrt(b.x * b.x + b.y * b.y);
  len2 = a_len * b_len;
  return len2 == 0.0 ? 2.0 * PI : acos((a.x * b.x + a.y * b.y) / len2);
}

static TResult
Bezier_Up_Sign(RAS_ARG_ int degree,
	       TSplitter  splitter,
	       TPos       miny,
	       TPos       maxy,
	       TPos	  sign)
{
  TPos  x1, y1, x2, y2, x, y, e0, e2;
  double angle, lower_threshold, upper_threshold;

  TPoint*  arc;
  TPoint*  start_arc;

  TPoint * top;

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

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

#ifdef FT_DYNAMIC_BEZIER_STEPS
  /* compute dynamic bezier step threshold */
  /*threshold = Dynamic_Bezier_Threshold (RAS_VAR_ degree, arc);*/
  lower_threshold = LOWER_THRESHOLD;
  upper_threshold = UPPER_THRESHOLD;
#else
  lower_threshold = LOWER_THRESHOLD;
  upper_threshold = UPPER_THRESHOLD;
#endif

  start_arc = arc;

  /* loop while there is still an arc on the bezier stack */
  while (arc >= start_arc) {
    ras.joint = FALSE;

    x1 = arc[degree].x;	/* start of top-most arc */
    y1 = arc[degree].y;
    x2 = arc[0].x;	/* final of the top-most arc */
    y2 = arc[0].y;

    e2 = FLOOR (y2);	/* integer end y */
    if (e2 > maxy)
      e2 = FLOOR (maxy);
      
    e0 = CEILING (y1);
    if (e0 < miny)
      e0 = CEILING (miny);

    y  = (e0 + e2) / 2;

    if (e2 == e0) {
      ENTER_POINT(top, x2, sign * y2, "approx");
	
      arc -= degree;	/* pop the arc */
    }
    else {
      if (x2 < x1) {
	if (sign > 0)
	  x  = (x1 - FMulDiv (x2 - x1, y - e0, e2 - e0));
	else
	  x  = (x1 + FMulDiv (x1 - x2, y - e0, e2 - e0));
      }
      else {
	if (sign > 0)
	  x  = (x2 + FMulDiv (x2 - x1, y - e0, e2 - e0));
	else
	  x  = (x2 - FMulDiv (x1 - x2, y - e0, e2 - e0));
      }

      angle = Bounding_Triangle_Angle(x1, e0, x, y, x2, e2);

      if (labs(x2 - x) >= 64 && e2 - y >= 64 && angle < lower_threshold) {
	/* if the arc's bounding triangle's angle is too great, split it */
	splitter(arc);
	arc += degree;
      }
      
      else {
	/* otherwise, approximate it as a segment and compute
	   its intersection with the current scanline */
	if (((x2 < x && x < x1) || (x1 < x && x < x2)) &&
	    angle < upper_threshold) 
	  ENTER_POINT(top, x, sign * y, "approx");
	
	arc -= degree;     /* pop the arc */
      }
    }
  }

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

static TResult
Bezier_Up(RAS_ARG_ int degree,
	  TSplitter  splitter,
	  TPos       miny,
	  TPos       maxy)
{
  return Bezier_Up_Sign(RAS_VAR_ degree, splitter, miny, maxy, 1);
}

/*
 * Computes the scan-line intersections of a descending second-order
 * Bezier arc and stores them in the render pool.  The arc is taken
 * from the top of the stack.
 */
static TResult
Bezier_Down(RAS_ARG_ int degree,
	    TSplitter  splitter,
	    TPos       miny,
	    TPos       maxy)
{
  TPoint*  arc = ras.arc;
  TResult  result, fresh;

  arc[0].y = -arc[0].y;
  arc[1].y = -arc[1].y;
  arc[2].y = -arc[2].y;
  if (degree > 2)
    arc[3].y = -arc[3].y;

  fresh = ras.fresh;

  result = Bezier_Up_Sign(RAS_VAR_ degree, splitter, -maxy, -miny, -1);

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

static TResult
Bezier_Right_Sign(RAS_ARG_ int degree,
		  TSplitter  splitter,
		  TPos       minx,
		  TPos       maxx,
		  TPos	     sign)
{
  TPos  x1, y1, x2, y2, x, y, e0, e2;
  double angle, lower_threshold, upper_threshold;

  TPoint*  arc;
  TPoint*  start_arc;

  TPoint * top;

  arc = ras.arc;
  x1  = arc[degree].x;
  x2  = arc[0].x;
  top = ras.cursor;

  if (x1 < minx || x2 > maxx)
    goto Fin;

#ifdef FT_DYNAMIC_BEZIER_STEPS
  /* compute dynamic bezier step threshold */
  /*threshold = Dynamic_Bezier_Threshold (RAS_VAR_ degree, arc);*/
  lower_threshold = LOWER_THRESHOLD;
  upper_threshold = UPPER_THRESHOLD;
#else
  lower_threshold = LOWER_THRESHOLD;
  upper_threshold = UPPER_THRESHOLD;
#endif

  start_arc = arc;

  /* loop while there is still an arc on the bezier stack */
  while (arc >= start_arc) {
    ras.joint = FALSE;

    x1 = arc[degree].x;	/* start of right-most arc */
    y1 = arc[degree].y;
    x2 = arc[0].x;	/* final of the right-most arc */
    y2 = arc[0].y;

    e2 = FLOOR (x2);	/* integer end x */
    if (e2 > maxx)
      e2 = FLOOR (maxx);
    
    e0 = CEILING (x1);
    if (e0 < minx)
      e0 = CEILING (minx);
      
    x  = (e0 + e2) / 2;

    if (e2 == e0) {
      ENTER_POINT(top, sign * x2, y2, "approx");
      
      arc -= degree;	/* pop the arc */
    }
    else {
      if (y2 < y1) {
	if (sign > 0)
	  y  = (y1 - FMulDiv (y2 - y1, x - e0, e2 - e0));
	else
	  y  = (y1 + FMulDiv (y1 - y2, x - e0, e2 - e0));
      }
      else {
	if (sign > 0)
	  y  = (y2 + FMulDiv (y2 - y1, x - e0, e2 - e0));
	else
	  y  = (y2 - FMulDiv (y1 - y2, x - e0, e2 - e0));
      }

      angle = Bounding_Triangle_Angle(e0, y1, x, y, e2, y2);
      
      if (e2 - x >= 64 && labs(y2 - y) >= 64 && angle < lower_threshold) {
	/* if the arc's bounding triangle's angle is too great, split it */
	splitter(arc);
	arc += degree;
      }
      
      else {
	/* otherwise, approximate it as a segment and compute
	   its intersection with the current scanline */
	if (((y2 < y && y < y1) || (y1 < y && y < y2)) &&
	    angle < upper_threshold) 
	  ENTER_POINT(top, sign * x, y, "approx");
	
	arc -= degree;     /* pop the arc */
      }
    }
  }

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

static TResult
Bezier_Right(RAS_ARG_ int degree,
	     TSplitter  splitter,
	     TPos       minx,
	     TPos       maxx)
{
  return Bezier_Right_Sign(RAS_VAR_ degree, splitter, minx, maxx, 1);
}

/*
 * Computes the scan-line intersections of a descending second-order
 * Bezier arc and stores them in the render pool.  The arc is taken
 * from the top of the stack.
 */
static TResult
Bezier_Left(RAS_ARG_ int degree,
	    TSplitter  splitter,
	    TPos       minx,
	    TPos       maxx)
{
  TPoint*  arc = ras.arc;
  TResult  result, fresh;

  arc[0].x = -arc[0].x;
  arc[1].x = -arc[1].x;
  arc[2].x = -arc[2].x;
  if (degree > 2)
    arc[3].x = -arc[3].x;

  fresh = ras.fresh;

  result = Bezier_Right_Sign(RAS_VAR_ degree, splitter, -maxx, -minx, -1);

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

/*
 * Subdivides one second-order Bezier arc into two joint sub-arcs in
 * the Bezier stack.
 */
static void
Split_Conic (TPoint *base)
{
  TPos  a, b;

  base[4].x = base[2].x;
  b = base[1].x;
  a = base[3].x = (base[2].x + b + 1) >> 1;
  b = base[1].x = (base[0].x + b + 1) >> 1;
  base[2].x = (a + b + 1) >> 1;

  base[4].y = base[2].y;
  b = base[1].y;
  a = base[3].y = (base[2].y + b + 1) >> 1;
  b = base[1].y = (base[0].y + b + 1) >> 1;
  base[2].y = (a + b) / 2;
}

/*
 * Clears the Bezier stack and pushes a new arc on top of it.
 */
static void
Push_Conic (RAS_ARG_ FT_Vector *p2, FT_Vector *p3)
{
#undef  STORE
#define STORE(_arc, point) {					\
    _arc.x = SCALED (point->x);					\
    _arc.y = SCALED (point->y);					\
  }

  TPoint*  arc;

  ras.arc = arc = ras.arcs;

  arc[2] = ras.last;
  STORE (arc[1], p2);
  STORE (arc[0], p3);
#undef  STORE
}

/*
 * Subdivides a third-order Bezier arc into two joint sub-arcs in
 * the Bezier stack.
 */
static void
Split_Cubic (TPoint *base)
{
  TPos   a, b, c, d;

  base[6].x = base[3].x;
  c = base[1].x;
  d = base[2].x;
  base[1].x = a = (base[0].x + c + 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;
}

/*
 * Clears the Bezier stack and pushes a new third-order Bezier arc on
 * top of it.
 */
static void
Push_Cubic (RAS_ARG_ FT_Vector *p2, FT_Vector *p3, FT_Vector *p4)
{
#undef  STORE
#define STORE(_arc, point) {					\
    _arc.x = SCALED (point->x);					\
    _arc.y = SCALED (point->y);					\
  }

  TPoint*  arc;
  ras.arc = arc = ras.arcs;

  arc[3] = ras.last;
  STORE (arc[2], p2);
  STORE (arc[1], p3);
  STORE (arc[0], p4);

#undef STORE
}

static TResult
Check_Contour (RAS_ARG)
{
  TFace *  lastFace;

  /* Sometimes, the first and last face in a contour join on
     an integer scan-line; we must then remove the last intersection
     from the last face to get rid of doublets */
  if ((FRAC (ras.last.y) == 0 &&
       ras.last.y >= ras.minY &&
       ras.last.y <= ras.maxY))
  {
    if (ras.first_face && ras.first_face->flow == ras.cur_face->flow)
      ras.cursor--;
  }

  lastFace = ras.cur_face;
  if (End_Face (RAS_VAR))
    return FAILURE;

  /* close the `next face in contour' linked list */
  lastFace->next = ras.first_face;

  return SUCCESS;
}

/*
 * This function injects a new contour in the render pool.
 */
static int
Move_To (FT_Vector *to, FT_Raster raster)
{
  TPoint *top;

  /* if there was already a contour being built, perform some checks */
  if (ras.start_face)
    if (Check_Contour (RAS_VAR ))
      goto Fail;

  if (New_Face (RAS_VAR_ Ascending))
    goto Fail;

  /* set the `current last point' */
  ras.last.x = SCALED (to->x);
  ras.last.y = SCALED (to->y);

  ras.state      = Unknown;
  ras.first_face = NULL;

  top = ras.cursor;

  ENTER_POINT(top, ras.last.x, ras.last.y, "move");

  ras.cursor = top;

  return SUCCESS;

Fail:
  return FAILURE;
}

/*
 * This function injects a new line segment in the render pool and
 * adjusts the faces list accordingly.
 */
static int
Line_To (FT_Vector *to, FT_Raster raster)
{
  TPoint *top;
  TPos  x, y;

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

  ras.last.x = x;
  ras.last.y = y;

  top = ras.cursor;

  ENTER_POINT(top, ras.last.x, ras.last.y, "line");

  ras.cursor = top;

  return SUCCESS;
}

//
// Injects a new conic Bezier arc and adjusts the face list
// accordingly.
//
static int
Conic_To (FT_Vector *control,
	  FT_Vector *to,
	  FT_Raster  raster)
{
  TPoint *    top;
  TPos        y1, y2, y3, ymin, ymax;
  TPos	      x1, x2, x3, xmin, xmax;
  TDirection  state_bez;

  Push_Conic (RAS_VAR_  control, to);

  do {
    y1 = ras.arc[2].y;
    y2 = ras.arc[1].y;
    y3 = ras.arc[0].y;
    x1 = ras.arc[2].x;
    x2 = ras.arc[1].x;
    x3 = ras.arc[0].x;

    /* first, categorize the Bezier arc */

    if ( y1 <= y3 ) {
      ymin = y1;
      ymax = y3;
    }
    else {
      ymin = y3;
      ymax = y1;
    }

    if ( x1 <= x3 ) {
      xmin = x1;
      xmax = x3;
    }
    else {
      xmin = x3;
      xmax = x1;
    }

    if ((ymax - ymin) >= (xmax - xmin)) {
      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;
	
	/* 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;
      }
    }

    else {
      if (x2 < xmin || x2 > xmax) {
	/* this arc has no given direction, split it !! */
	Split_Conic (ras.arc);
	ras.arc += 2;
      }
      else if (x1 == x3) {
	/* 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 = x1 < x3 ? Ascending : Descending;

	/* now call the appropriate routine */
	if (state_bez == Ascending) {
	  if (Bezier_Right (RAS_VAR_  2, Split_Conic, ras.minX, ras.maxX))
	    goto Fail;
	}
	else
	  if (Bezier_Left (RAS_VAR_  2, Split_Conic, ras.minX, ras.maxX))
	    goto Fail;
      }
    }

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

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

  top = ras.cursor;

  ENTER_POINT_CHECK(top, ras.last.x, ras.last.y, "conic");

  ras.cursor = top;

  return SUCCESS;

Fail:
  return FAILURE;
}

/*
 * Injects a new cubic Bezier arc and adjusts the face list
 * accordingly.
 */
static int
Cubic_To (FT_Vector *control1,
	  FT_Vector *control2,
	  FT_Vector *to,
	  FT_Raster  raster)
{
  TPoint *    top;
  TPos        y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
  TDirection  state_bez;

  Push_Cubic (RAS_VAR_  control1, control2, to);

  do {
    y1 = ras.arc[3].y;
    y2 = ras.arc[2].y;
    y3 = ras.arc[1].y;
    y4 = ras.arc[0].y;
    x4 = ras.arc[0].x;

    /* first, categorize the Bezier arc */

    if ( y1 <= y4 ) {
      ymin1 = y1;
      ymax1 = y4;
    }
    else {
      ymin1 = y4;
      ymax1 = y1;
    }

    if ( y2 <= y3 ) {
      ymin2 = y2;
      ymax2 = y3;
    }
    else {
      ymin2 = y3;
      ymax2 = y2;
    }

    if (ymin2 < ymin1 || ymax2 > ymax1) {
      // this arc has no given direction, split it!
      Split_Cubic (ras.arc);
      ras.arc += 3;
    }
    else if (y1 == y4) {
      // this arc is flat, ignore it and pop it from the bezier stack
      ras.arc -= 3;
    }
    else {
      state_bez = (y1 <= y4) ? Ascending : Descending;

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

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

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

  top = ras.cursor;

  ENTER_POINT_CHECK(top, ras.last.x, ras.last.y, "cubic");

  ras.cursor = top;

  return SUCCESS;

Fail:
  return FAILURE;
}

/*
 * Converts a glyph into a series of segments and arcs and makes a
 * faces list with them.
 */
int
_flFTConvertGlyph(FLFreeTypeOutline *ch, RAS_ARG_ FT_Outline *outline)
{
  static FT_Outline_Funcs interface_table = {
    (FT_Outline_MoveTo_Func)Move_To,
    (FT_Outline_LineTo_Func)Line_To,
    (FT_Outline_ConicTo_Func)Conic_To,
    (FT_Outline_CubicTo_Func)Cubic_To
  };
  int error;

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

  ras.pool_limit = ras.pool_size - AlignFaceSize;

  ras.n_extrema = 0;

  ras.cur_face         = (TFace *)ras.cursor;
  ras.cur_face->offset = ras.cursor;
  ras.num_faces        = 0;

  /* Now decompose curve */
  error = FT_Outline_Decompose(outline, &interface_table, &ras);
  if (error)
    return FAILURE;

  /* XXX: the error condition is in ras.error */

  /* Check the last contour if needed */
  if (Check_Contour(RAS_VAR))
    return FAILURE;

  /* Finalize faces list */
  return _flFTFinalizeFaceTable(ch, RAS_VAR);
}

int
_flFTGenerateGlyph(FLFreeTypeOutline *ch)
{
  Byte pool[4096];
  FT_RasterRec_ rec;
  FT_Raster	raster = &rec;
  FT_Outline *	outline = &ch->glyph->outline;
  FT_Pos		top, bottom, y_min, y_max;
  FT_Pos		left, right, x_min, x_max;
  FT_Error		error;

  ras.cursor = ras.pool = (TPoint *) pool;
  ras.pool_size = (TPoint *) pool + sizeof(pool);

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

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

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

  ras.outline  = outline;

  SET_High_Precision((char)((outline->flags & ft_outline_high_precision)!= 0));

  ras.scale_shift  = PRECISION_BITS - INPUT_BITS;
  ras.scale_delta  = PRECISION_HALF;

  top      = BM_CEILING(ch->glyph->metrics.horiBearingY);
  bottom   = BM_FLOOR(ch->glyph->metrics.horiBearingY - ch->glyph->metrics.height);
  left     = BM_CEILING(ch->glyph->metrics.horiBearingX);
  right    = BM_FLOOR(ch->glyph->metrics.horiBearingX + ch->glyph->metrics.width);
  y_min    = 0;
  y_max    = BM_TRUNC(top - bottom);
  x_min    = 0;
  x_max    = BM_TRUNC(right - left);

  ras.maxY = ((long)y_max << PRECISION_BITS) - 1;
  ras.minY =  (long)y_min << PRECISION_BITS;
  ras.maxX = ((long)x_max << PRECISION_BITS) - 1;
  ras.minX =  (long)x_min << PRECISION_BITS;

  error = _flFTConvertGlyph(ch, RAS_VAR_  ras.outline);
  if (error)
    return error;

  return 0;
}
