text: /* Copyright (C) 2000-2012 by George Williams */
  /*
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
  
   * Redistributions of source code must retain the above copyright notice, this
   * list of conditions and the following disclaimer.
  
   * Redistributions in binary form must reproduce the above copyright notice,
   * this list of conditions and the following disclaimer in the documentation
   * and/or other materials provided with the distribution.
  
   * The name of the author may not be used to endorse or promote products
   * derived from this software without specific prior written permission.
  
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
   * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  #include "fontforge.h"
  #include <stdio.h>
  #include <string.h>
  #include <ustring.h>
  #include <math.h>
  #include "gdraw.h"
  #include "splinefont.h"
  #include "edgelist.h"
  
  Color default_background = 0xffffff;		/* white */
  
  static void HintsFree(Hints *h) {
      Hints *hnext;
      for ( ; h!=NULL; h = hnext ) {
  	hnext = h->next;
  	free(h);
      }
  }
  
  static void _FreeEdgeList(EdgeList *es) {
      int i;
  
      /* edges will be NULL if the user tries to make an enormous bitmap */
      /*  if the linear size is bigger than several thousand, we just */
      /*  ignore the request */
      if ( es->edges!=NULL ) {
  	for ( i=0; i<es->cnt; ++i ) {
  	    Edge *e, *next;
  	    for ( e = es->edges[i]; e!=NULL; e = next ) {
  		next = e->esnext;
  		free(e);
  	    }
  	    es->edges[i] = NULL;
  	}
      }
  }
  
  void FreeEdges(EdgeList *es) {
      _FreeEdgeList(es);
      free(es->edges);
      free(es->interesting);
      HintsFree(es->hhints);
      HintsFree(es->vhints);
  }
  
  bigreal TOfNextMajor(Edge *e, EdgeList *es, bigreal sought_m ) {
      /* We want to find t so that Mspline(t) = sought_m */
      /*  the curve is monotonic */
      Spline1D *msp = &e->spline->splines[es->major];
      bigreal new_t;
  
      if ( es->is_overlap ) {
  
  	/* if we've adjusted the height then we won't be able to find it restricting */
  	/*  t between [0,1] as we do. So it's a special case. (this is to handle */
  	/*  hstem hints) */
  	if ( e->max_adjusted && sought_m==e->mmax ) {
  	    e->m_cur = sought_m;
  return( e->up?1.0:0.0 );
  	}
  
  	new_t = IterateSplineSolve(msp,e->t_mmin,e->t_mmax,(sought_m+es->mmin)/es->scale);
  	if ( new_t==-1 )
  	    IError( "No Solution");
  	e->m_cur = (((msp->a*new_t + msp->b)*new_t+msp->c)*new_t + msp->d)*es->scale - es->mmin;
  return( new_t );
      } else {
  	Spline *sp = e->spline;
  
  	if ( sp->islinear ) {
  	    new_t = e->t_cur + (sought_m-e->m_cur)/(es->scale * msp->c);
  	    e->m_cur = (msp->c*new_t + msp->d)*es->scale - es->mmin;
  return( new_t );
  	}
  	/* if we have a spline that is nearly horizontal at its max. endpoint */
  	/*  then finding A value of t for which y has the right value isn't good */
  	/*  enough (at least not when finding intersections) */
  	if ( sought_m+1>e->mmax ) {
  	    e->m_cur = e->mmax;
  return( e->t_mmax );
  	}
  
  	/* if we've adjusted the height then we won't be able to find it restricting */
  	/*  t between [0,1] as we do. So it's a special case. (this is to handle */
  	/*  hstem hints) */
  	if ( e->max_adjusted && sought_m==e->mmax ) {
  	    e->m_cur = sought_m;
  return( e->up?1.0:0.0 );
  	}
  	new_t = IterateSplineSolve(msp,e->t_mmin,e->t_mmax,(sought_m+es->mmin)/es->scale);
  	if ( new_t==-1 )
  	    IError( "No Solution");
  	e->m_cur = (((msp->a*new_t + msp->b)*new_t+msp->c)*new_t + msp->d)*es->scale - es->mmin;
  return( new_t );
      }
  }
  
  static int SlopeLess(Edge *e, Edge *p, int other) {
      Spline1D *osp = &e->spline->splines[other];
      Spline1D *psp = &p->spline->splines[other];
      Spline1D *msp = &e->spline->splines[!other];
      Spline1D *qsp = &p->spline->splines[!other];
      real os = (3*osp->a*e->t_cur+2*osp->b)*e->t_cur+osp->c,
  	   ps = (3*psp->a*p->t_cur+2*psp->b)*p->t_cur+psp->c;
      real ms = (3*msp->a*e->t_cur+2*msp->b)*e->t_cur+msp->c,
  	   qs = (3*qsp->a*p->t_cur+2*qsp->b)*p->t_cur+qsp->c;
      if ( ms<.0001 && ms>-.0001 ) ms = 0;
      if ( qs<.0001 && qs>-.0001 ) qs = 0;
      if ( qs==0 ) {
  	if ( p->t_cur==1 ) {
  	   qs = (3*qsp->a*.9999+2*qsp->b)*.9999+qsp->c;
  	   ps = (3*psp->a*.9999+2*psp->b)*.9999+psp->c;
         } else {
  	   qs = (3*qsp->a*(p->t_cur+.0001)+2*qsp->b)*(p->t_cur+.0001)+qsp->c;
  	   ps = (3*psp->a*(p->t_cur+.0001)+2*psp->b)*(p->t_cur+.0001)+psp->c;
         }
      }
      if ( ms==0 ) {
  	if ( e->t_cur==1 ) {
  	    ms = (3*msp->a*.9999+2*msp->b)*.9999+msp->c;
  	    os = (3*osp->a*.9999+2*osp->b)*.9999+osp->c;
  	} else {
  	    ms = (3*msp->a*(e->t_cur+.0001)+2*msp->b)*(e->t_cur+.0001)+msp->c;
  	    os = (3*osp->a*(e->t_cur+.0001)+2*osp->b)*(e->t_cur+.0001)+osp->c;
  	}
      }
      if ( e->t_cur-e->tmin > e->tmax-e->t_cur ) { os = -os; ms = -ms; }
      if ( p->t_cur-p->tmin > p->tmax-p->t_cur ) { ps = -ps; qs = -qs; }
      if ( ms!=0 && qs!=0 ) { os /= ms; ps /= qs; }
      else if ( ms==0 && qs==0 ) /* Do Nothing */;
      else if ( (ms==0 && os>0) || (qs==0 && ps<0) )		/* Does this make sense? */
  return( false );
      else if ( (ms==0 && os<0) || (qs==0 && ps>0) )		/* Does this make sense? */
  return( true );
  
      if ( os==ps || ms==0 || qs==0 )
  return( e->o_mmax<p->o_mmax );
  
  return( os<ps );
  }
  
  static void AddEdge(EdgeList *es, Spline *sp, real tmin, real tmax ) {
      Edge *e, *pr;
      real m1, m2;
      int mpos;
      Hints *hint;
      Spline1D *msp = &sp->splines[es->major], *osp = &sp->splines[es->other];
  
      e = gcalloc(1,sizeof(Edge));
      e->spline = sp;
  
      m1 = ( ((msp->a*tmin+msp->b)*tmin+msp->c)*tmin + msp->d ) * es->scale;
      m2 = ( ((msp->a*tmax+msp->b)*tmax+msp->c)*tmax + msp->d ) * es->scale;
      if ( m1>m2 ) {
  	e->mmin = m2;
  	e->t_mmin = tmax;
  	e->mmax = m1;
  	e->t_mmax = tmin;
  	e->up = false;
      } else {
  	e->mmax = m2;
  	e->t_mmax = tmax;
  	e->mmin = m1;
  	e->t_mmin = tmin;
  	e->up = true;
      }
      if ( RealNear(e->mmin,es->mmin)) e->mmin = es->mmin;
      e->o_mmin = ( ((osp->a*e->t_mmin+osp->b)*e->t_mmin+osp->c)*e->t_mmin + osp->d ) * es->scale;
      e->o_mmax = ( ((osp->a*e->t_mmax+osp->b)*e->t_mmax+osp->c)*e->t_mmax + osp->d ) * es->scale;
      e->mmin -= es->mmin; e->mmax -= es->mmin;
      e->t_cur = e->t_mmin;
      e->o_cur = e->o_mmin;
      e->m_cur = e->mmin;
      e->last_opos = e->last_mpos = -2;
      e->tmin = tmin; e->tmax = tmax;
  
      if ( e->mmin<0 || e->mmin>=e->mmax ) {
  	/*IError("Probably not serious, but we've got a zero length spline in AddEdge in %s",es->sc==NULL?<nameless>:es->sc->name);*/
  	free(e);
  return;
      }
  
      if ( es->sc!=NULL ) for ( hint=es->hhints; hint!=NULL; hint=hint->next ) {
  	if ( hint->adjustb ) {
  	    if ( e->m_cur>hint->b1 && e->m_cur<hint->b2 ) {
  		e->m_cur = e->mmin = hint->ab;
  		e->min_adjusted = true;
  	    } else if ( e->mmax>hint->b1 && e->mmax<hint->b2 ) {
  		e->mmax = hint->ab;
  		e->max_adjusted = true;
  	    }
  	} else if ( hint->adjuste ) {
  	    if ( e->m_cur>hint->e1 && e->m_cur<hint->e2 ) {
  		e->m_cur = e->mmin = hint->ae;
  		e->min_adjusted = true;
  	    } else if ( e->mmax>hint->e1 && e->mmax<hint->e2 ) {
  		e->mmax = hint->ae;
  		e->max_adjusted = true;
  	    }
  	}
      }
  
      mpos = (int) ceil(e->m_cur);
      if ( mpos>e->mmax || mpos>=es->cnt ) {
  	free(e);
  return;
      }
  
      if ( e->m_cur!=ceil(e->m_cur) ) {
  	/* bring the new edge up to its first scan line */
  	e->t_cur = TOfNextMajor(e,es,ceil(e->m_cur));
  	e->o_cur = ( ((osp->a*e->t_cur+osp->b)*e->t_cur+osp->c)*e->t_cur + osp->d ) * es->scale;
      }
  
      e->before = es->last;
      if ( es->last!=NULL )
  	es->last->after = e;
      if ( es->last==NULL )
  	es->splinesetfirst = e;
      es->last = e;
  
      if ( es->edges[mpos]==NULL || e->o_cur<es->edges[mpos]->o_cur ||
  	    (e->o_cur==es->edges[mpos]->o_cur && SlopeLess(e,es->edges[mpos],es->other))) {
  	e->esnext = es->edges[mpos];
  	es->edges[mpos] = e;
      } else {
  	for ( pr=es->edges[mpos]; pr->esnext!=NULL && pr->esnext->o_cur<e->o_cur ;
  		pr = pr->esnext );
  	/* When two splines share a vertex which is a local minimum, then */
  	/*  o_cur will be equal for both (to the vertex's o value) and so */
  	/*  the above code randomly picked one to go first. That screws up */
  	/*  the overlap code, which wants them properly ordered from the */
  	/*  start. so look at the end point, nope the end point isn't always */
  	/*  meaningful, look at the slope... */
  	if ( pr->esnext!=NULL && pr->esnext->o_cur==e->o_cur &&
  		SlopeLess(e,pr->esnext,es->other)) {
  	    pr = pr->esnext;
  	}
  	e->esnext = pr->esnext;
  	pr->esnext = e;
      }
      if ( es->interesting ) {
  	/* Mark the other end of the spline as interesting */
  	es->interesting[(int) ceil(e->mmax)]=1;
      }
  }
  
  static void AddMajorEdge(EdgeList *es, Spline *sp) {
      Edge *e, *pr;
      real m1;
      Spline1D *msp = &sp->splines[es->major], *osp = &sp->splines[es->other];
  
      e = gcalloc(1,sizeof(Edge));
      e->spline = sp;
  
      e->mmin = e->mmax = m1 = msp->d * es->scale - es->mmin;
      e->t_mmin = 0;
      e->t_mmax = 1;
      e->up = false;
      e->o_mmin = osp->d * es->scale;
      e->o_mmax = ( osp->a + osp->b + osp->c + osp->d ) * es->scale;
      if ( e->o_mmin == e->o_mmax ) {	/* Just a point? */
  	free(e);
  return;
      }
      if ( e->mmin<0 )
  	IError("Grg!");
  
      if ( ceil(e->m_cur)>e->mmax ) {
  	free(e);
  return;
      }
  
      if ( es->majors==NULL || es->majors->mmin>=m1 ) {
  	e->esnext = es->majors;
  	es->majors = e;
      } else {
  	for ( pr=es->majors; pr->esnext!=NULL && pr->esnext->mmin<m1; pr = pr->esnext );
  	e->esnext = pr->esnext;
  	pr->esnext = e;
      }
  }
  
  static void AddSpline(EdgeList *es, Spline *sp ) {
      real t1=2, t2=2, t;
      real b2_fourac;
      real fm, tm;
      Spline1D *msp = &sp->splines[es->major], *osp = &sp->splines[es->other];
  
      /* Find the points of extrema on the curve discribing y behavior */
      if ( !RealNear(msp->a,0) ) {
  	/* cubic, possibly 2 extrema (possibly none) */
  	b2_fourac = 4*msp->b*msp->b - 12*msp->a*msp->c;
  	if ( b2_fourac>=0 ) {
  	    b2_fourac = sqrt(b2_fourac);
  	    t1 = (-2*msp->b - b2_fourac) / (6*msp->a);
  	    t2 = (-2*msp->b + b2_fourac) / (6*msp->a);
  	    t1 = CheckExtremaForSingleBitErrors(msp,t1,t2);
  	    t2 = CheckExtremaForSingleBitErrors(msp,t2,t1);
  	    if ( t1>t2 ) { real temp = t1; t1 = t2; t2 = temp; }
  	    else if ( t1==t2 ) t2 = 2.0;
  
  	    /* check for curves which have such a small slope they might */
  	    /*  as well be horizontal */
  	    fm = es->major==1?sp->from->me.y:sp->from->me.x;
  	    tm = es->major==1?sp->to->me.y:sp->to->me.x;
  	    if ( fm==tm ) {
  		real m1, m2, d1, d2;
  		m1 = m2 = fm;
  		if ( t1>0 && t1<1 )
  		    m1 = ((msp->a*t1+msp->b)*t1+msp->c)*t1 + msp->d;
  		if ( t2>0 && t2<1 )
  		    m2 = ((msp->a*t2+msp->b)*t2+msp->c)*t2 + msp->d;
  		d1 = (m1-fm)*es->scale;
  		d2 = (m2-fm)*es->scale;
  		if ( d1>-.5 && d1<.5 && d2>-.5 && d2<.5 ) {
  		    sp->ishorvert = true;
  		    if ( es->genmajoredges )
  			AddMajorEdge(es,sp);
  return;		/* Pretend it's horizontal, ignore it */
  		}
  	    }
  	}
      } else if ( !RealNear(msp->b,0) ) {
  	/* Quadratic, at most one extremum */
  	t1 = -msp->c/(2.0*msp->b);
      } else if ( !RealNear(msp->c,0) ) {
  	/* linear, no points of extrema */
      } else {
  	sp->ishorvert = true;
  	if ( es->genmajoredges )
  	    AddMajorEdge(es,sp);
  return;		/* Horizontal line, ignore it */
      }
  
      if ( RealNear(t1,0)) t1=0;
      if ( RealNear(t1,1)) t1=1;
      if ( RealNear(t2,0)) t2=0;
      if ( RealNear(t2,1)) t2=1;
      if ( RealNear(t1,t2)) t2=2;
      t=0;
      if ( t1>0 && t1<1 ) {
  	AddEdge(es,sp,0,t1);
  	t = t1;
      }
      if ( t2>0 && t2<1 ) {
  	AddEdge(es,sp,t,t2);
  	t = t2;
      }
      AddEdge(es,sp,t,1.0);
      if ( es->interesting ) {
  	/* Also store up points of extrema in X as interesting (we got the endpoints, just internals now)*/
  	extended ot1, ot2;
  	int mpos;
  	SplineFindExtrema(osp,&ot1,&ot2);
  	if ( ot1>0 && ot1<1 ) {
  	    mpos = (int) ceil( ( ((msp->a*ot1+msp->b)*ot1+msp->c)*ot1+msp->d )*es->scale-es->mmin );
  	    es->interesting[mpos] = 1;
  	}
  	if ( ot2>0 && ot2<1 ) {
  	    mpos = (int) ceil( ( ((msp->a*ot2+msp->b)*ot2+msp->c)*ot2+msp->d )*es->scale-es->mmin );
  	    es->interesting[mpos] = 1;
  	}
      }
  }
  
  void FindEdgesSplineSet(SplinePointList *spl, EdgeList *es, int ignore_clip) {
      Spline *spline, *first;
  
      for ( ; spl!=NULL; spl = spl->next ) {
  	if ( spl->first->prev!=NULL && spl->first->prev->from!=spl->first &&
  		(!ignore_clip || (ignore_clip==1 && !spl->is_clip_path) || (ignore_clip==2 && spl->is_clip_path))) {
  	    first = NULL;
  	    es->last = es->splinesetfirst = NULL;
  	    /* Set so there is no previous point!!! */
  	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
  		AddSpline(es,spline);
  		if ( first==NULL ) first = spline;
  	    }
  	    if ( es->last!=NULL ) {
  		es->splinesetfirst->before = es->last;
  		es->last->after = es->splinesetfirst;
  	    }
  	}
      }
  }
  
  static void FindEdges(SplineChar *sc, EdgeList *es) {
      RefChar *rf;
  
      for ( rf=sc->layers[es->layer].refs; rf!=NULL; rf = rf->next )
  	FindEdgesSplineSet(rf->layers[0].splines,es,false);
  
      FindEdgesSplineSet(sc->layers[es->layer].splines,es,false);
  }
  
  Edge *ActiveEdgesInsertNew(EdgeList *es, Edge *active,int i) {
      Edge *apt, *pr, *npt;
  
      for ( pr=NULL, apt=active, npt=es->edges[(int) i]; apt!=NULL && npt!=NULL; ) {
  	if ( npt->o_cur<apt->o_cur ) {
  	    npt->aenext = apt;
  	    if ( pr==NULL )
  		active = npt;
  	    else
  		pr->aenext = npt;
  	    pr = npt;
  	    npt = npt->esnext;
  	} else {
  	    pr = apt;
  	    apt = apt->aenext;
  	}
      }
      while ( npt!=NULL ) {
  	npt->aenext = NULL;
  	if ( pr==NULL )
  	    active = npt;
  	else
  	    pr->aenext = npt;
  	pr = npt;
  	npt = npt->esnext;
      }
  return( active );
  }
  
  Edge *ActiveEdgesRefigure(EdgeList *es, Edge *active,real i) {
      Edge *apt, *pr;
      int any;
  
      /* first remove any entry which doesn't intersect the new scan line */
      /*  (ie. stopped on last line) */
      for ( pr=NULL, apt=active; apt!=NULL; apt = apt->aenext ) {
  	if ( apt->mmax<i ) {
  	    if ( pr==NULL )
  		active = apt->aenext;
  	    else
  		pr->aenext = apt->aenext;
  	} else
  	    pr = apt;
      }
      /* then move the active list to the next line */
      for ( apt=active; apt!=NULL; apt = apt->aenext ) {
  	Spline1D *osp = &apt->spline->splines[es->other];
  	apt->t_cur = TOfNextMajor(apt,es,i);
  	apt->o_cur = ( ((osp->a*apt->t_cur+osp->b)*apt->t_cur+osp->c)*apt->t_cur + osp->d ) * es->scale;
      }
      /* reorder list */
      if ( active!=NULL ) {
  	any = true;
  	while ( any ) {
  	    any = false;
  	    for ( pr=NULL, apt=active; apt->aenext!=NULL; ) {
  		if ( apt->o_cur <= apt->aenext->o_cur ) {
  		    /* still ordered */;
  		    pr = apt;
  		    apt = apt->aenext;
  		} else if ( pr==NULL ) {
  		    active = apt->aenext;
  		    apt->aenext = apt->aenext->aenext;
  		    active->aenext = apt;
  		    /* don't need to set any, since this reorder can't disorder the list */
  		    pr = active;
  		} else {
  		    pr->aenext = apt->aenext;
  		    apt->aenext = apt->aenext->aenext;
  		    pr->aenext->aenext = apt;
  		    any = true;
  		    pr = pr->aenext;
  		}
  	    }
  	}
      }
      /* Insert new nodes */
      active = ActiveEdgesInsertNew(es,active,i);
  return( active );
  }
  
  Edge *ActiveEdgesFindStem(Edge *apt, Edge **prev, real i) {
      int cnt=apt->up?1:-1;
      Edge *pr, *e;
  
      for ( pr=apt, e=apt->aenext; e!=NULL && cnt!=0; pr=e, e=e->aenext ) {
  	if ( pr->up!=e->up )
  	    cnt += (e->up?1:-1);
  	else if ( (pr->before==e || pr->after==e ) &&
  		(( pr->mmax==i && e->mmin==i ) ||
  		 ( pr->mmin==i && e->mmax==i )) )
  	    /* This just continues the line and doesn't change count */;
  	else
  	    cnt += (e->up?1:-1);
      }
      /* color a horizontal line that comes out of the last vertex */
      if ( e!=NULL && (e->before==pr || e->after==pr) &&
  		(( pr->mmax==i && e->mmin==i ) ||
  		 ( pr->mmin==i && e->mmax==i )) ) {
  	pr = e;
  	e = e->aenext;
      } else if ( e!=NULL && ((pr->up && !e->up) || (!pr->up && e->up)) &&
  		    pr->spline!=e->spline &&
  		    ((pr->after==e && pr->spline->to->next!=NULL &&
  				pr->spline->to->next!=e->spline &&
  				pr->spline->to->next->to->next==e->spline ) ||
  			    (pr->before==e && pr->spline->from->prev!=NULL &&
  				pr->spline->from->prev!=e->spline &&
  				pr->spline->from->prev->from->prev!=e->spline )) &&
  		    ((pr->mmax == i && e->mmax==i ) ||
  		     (pr->mmin == i && e->mmin==i )) ) {
  	pr = e;
      }
      *prev = pr;
  return( e );
  }
  
  static int isvstem(EdgeList *es,real stem,int *vval) {
      Hints *hint;
  
      for ( hint=es->vhints; hint!=NULL ; hint=hint->next ) {
  	if ( stem>=hint->b1 && stem<=hint->b2 ) {
  	    *vval = hint->ab;
  return( true );
  	} else if ( stem>=hint->e1 && stem<=hint->e2 ) {
  	    *vval = hint->ae;
  return( true );
  	}
      }
  return( false );
  }
  
  static void FillChar(EdgeList *es) {
      Edge *active=NULL, *apt, *pr, *e, *prev;
      int i, k, end, width, oldk;
      uint8 *bpt;
  
      for ( i=0; i<es->cnt; ++i ) {
  	active = ActiveEdgesRefigure(es,active,i);
  
  	/* process scanline */
  	bpt = es->bitmap+((es->cnt-1-i)*es->bytes_per_line);
  	for ( apt=active; apt!=NULL; ) {
  	    e = ActiveEdgesFindStem(apt,&prev,i);
  	    pr = prev;
  	    width = rint(pr->o_cur-apt->o_cur);
  	    if ( width<=0 ) width=1;
  	    k=rint(apt->o_cur-es->omin);
  	    if ( k<0 ) k=0;
  	    end =rint(pr->o_cur-es->omin);
  	    if ( end-k > width-1 ) {
  		int kval = -999999, eval= -999999;
  		if ( isvstem(es,apt->o_cur,&kval) )
  		    k = kval;
  		if ( isvstem(es,pr->o_cur,&eval))
  		    end = eval;
  		if ( end-k > width-1 ) {
  		    if ( k!=kval && (end==eval || (apt->o_cur-es->omin)-k > end-(pr->o_cur-es->omin) ))
  			++k;
  		    else if ( end!=eval )
  			--end;
  		}
  	    }
  	    oldk = k;
  	    if ( apt->last_mpos==i-1 || pr->last_mpos==i-1 ) {
  		int lx1=apt->last_opos, lx2=pr->last_opos;
  		if ( apt->last_mpos!=i-1 ) lx1 = lx2;
  		else if ( pr->last_mpos!=i-1 ) lx2 = lx1;
  		if ( lx1>lx2 ) { int temp = lx1; lx1=lx2; lx2=temp; }
  		if ( lx2<k-1 ) {
  		    int mid = (lx2+k)/2;
  		    for ( ; lx2<mid; ++lx2 )
  			bpt[(lx2>>3)+es->bytes_per_line] |= (1<<(7-(lx2&7)));
  		    for ( ; lx2<k; ++lx2 )
  			bpt[(lx2>>3)] |= (1<<(7-(lx2&7)));
  		} else if ( lx1>end+1 ) {
  		    int mid = (lx1+end)/2;
  		    for ( ; lx1<mid; --lx1 )
  			bpt[(lx1>>3)+es->bytes_per_line] |= (1<<(7-(lx1&7)));
  		    for ( ; lx1<end; --lx1 )
  			bpt[(lx1>>3)] |= (1<<(7-(lx1&7)));
  		}
  	    }
  	    for ( ; k<=end; ++k )
  		bpt[k>>3] |= (1<<(7-(k&7)));
  	    apt->last_mpos = pr->last_mpos = i;
  	    apt->last_opos = oldk;
  	    pr->last_opos = end;
  	    apt = e;
  	}
      }
  }
  
  static void InitializeHints(SplineChar *sc, EdgeList *es) {
      Hints *hint, *last;
      StemInfo *s;
      real t1, t2;
      int k,end,width;
  
      /* we only care about hstem hints, and only if they fail to cross a */
      /*  vertical pixel boundary. If that happens, adjust either the top */
      /*  or bottom position so that a boundary forcing is crossed.   Any */
      /*  vertexes at those points will be similarly adjusted later... */
  
      last = NULL; es->hhints = NULL;
      for ( s=sc->hstem; s!=NULL; s=s->next ) {
  	hint = gcalloc(1,sizeof(Hints));
  	hint->base = s->start; hint->width = s->width;
  	if ( last==NULL ) es->hhints = hint;
  	else last->next = hint;
  	last = hint;
  	hint->adjuste = hint->adjustb = false;
  	t1 = hint->base*es->scale	-es->mmin;
  	t2 = (hint->base+hint->width)*es->scale	-es->mmin;
  	if ( floor(t1)==floor(t2) && t1!=floor(t1) && t2!=floor(t2) ) {
  	    if ( t1<t2 ) {
  		if ( t1-floor(t1) > ceil(t2)-t2 ) {
  		    hint->adjuste = true;
  		    hint->ae = ceil(t2);
  		} else {
  		    hint->adjustb = true;
  		    hint->ab = floor(t1);
  		}
  	    } else {
  		if ( t2-floor(t2) > ceil(t1)-t1 ) {
  		    hint->adjustb = true;
  		    hint->ab = ceil(t1);
  		} else {
  		    hint->adjuste = true;
  		    hint->ae = floor(t2);
  		}
  	    }
  	}
  	hint->b1 = t1-.2; hint->b2 = t1 + .2;
  	hint->e1 = t2-.2; hint->e2 = t2 + .2;
      }
  
      /* Nope. We care about vstems too now, but in a different case */
      last = NULL; es->vhints = NULL;
      for ( s=sc->vstem; s!=NULL; s=s->next ) {
  	hint = gcalloc(1,sizeof(Hints));
  	hint->base = s->start; hint->width = s->width;
  	if ( last==NULL ) es->vhints = hint;
  	else last->next = hint;
  	last = hint;
  	if ( hint->width<0 ) {
  	    width = -rint(-hint->width*es->scale);
  	    t1 = (hint->base+hint->width)*es->scale;
  	    t2 = hint->base*es->scale;
  	} else {
  	    width = rint(hint->width*es->scale);
  	    t1 = hint->base*es->scale;
  	    t2 = (hint->base+hint->width)*es->scale;
  	}
  	k = rint(t1-es->omin); end = rint(t2-es->omin);
  	if ( end-k > width-1 )
  	    if ( end-k > width-1 ) {
  		if ( (t1-es->omin)-k > end-(t2-es->omin) )
  		    ++k;
  		else
  		    --end;
  	    }
  	hint->ab = k; hint->ae = end;
  	hint->b1 = t1-.2; hint->b2 = t1 + .2;
  	hint->e1 = t2-.2; hint->e2 = t2 + .2;
      }
  }
  
  /* After a bitmap has been compressed, it's sizes may not comply with the */
  /*  expectations for saving images */
  void BCRegularizeBitmap(BDFChar *bdfc) {
      int bpl =(bdfc->xmax-bdfc->xmin)/8+1;
      int i;
  
      if ( bdfc->bytes_per_line!=bpl ) {
  	uint8 *bitmap = galloc(bpl*(bdfc->ymax-bdfc->ymin+1));
  	for ( i=0; i<=(bdfc->ymax-bdfc->ymin); ++i )
  	    memcpy(bitmap+i*bpl,bdfc->bitmap+i*bdfc->bytes_per_line,bpl);
  	free(bdfc->bitmap);
  	bdfc->bitmap= bitmap;
  	bdfc->bytes_per_line = bpl;
      }
  }
  
  void BCRegularizeGreymap(BDFChar *bdfc) {
      int bpl = bdfc->xmax-bdfc->xmin+1;
      int i;
  
      if ( bdfc->bytes_per_line!=bpl ) {
  	uint8 *bitmap = galloc(bpl*(bdfc->ymax-bdfc->ymin+1));
  	for ( i=0; i<=(bdfc->ymax-bdfc->ymin); ++i )
  	    memcpy(bitmap+i*bpl,bdfc->bitmap+i*bdfc->bytes_per_line,bpl);
  	free(bdfc->bitmap);
  	bdfc->bitmap= bitmap;
  	bdfc->bytes_per_line = bpl;
      }
  }
  
  void BCCompressBitmap(BDFChar *bdfc) {
      /* Now we may have allocated a bit more than we need to the bitmap */
      /*  check to see if there are any unused rows or columns.... */
      int i,j,any, off, last;
  
      /* we can use the same code to lop off rows whether we deal with bytes or bits */
      for ( i=0; i<bdfc->ymax-bdfc->ymin; ++i ) {
  	any = 0;
  	for ( j=0; j<bdfc->bytes_per_line; ++j )
  	    if ( bdfc->bitmap[i*bdfc->bytes_per_line+j]!=0 ) any = 1;
  	if ( any )
      break;
      }
      if ( i!=0 ) {
  	bdfc->ymax -= i;
  	memmove(bdfc->bitmap,bdfc->bitmap+i*bdfc->bytes_per_line,(bdfc->ymax-bdfc->ymin+1)*bdfc->bytes_per_line );
      }
  
      for ( i=bdfc->ymax-bdfc->ymin; i>0; --i ) {
  	any = 0;
  	for ( j=0; j<bdfc->bytes_per_line; ++j )
  	    if ( bdfc->bitmap[i*bdfc->bytes_per_line+j]!=0 ) any = 1;
  	if ( any )
      break;
      }
      if ( i!=bdfc->ymax-bdfc->ymin ) {
  	bdfc->ymin += bdfc->ymax-bdfc->ymin-i;
      }
  
      if ( !bdfc->byte_data ) {
  	for ( j=0; j<bdfc->xmax-bdfc->xmin; ++j ) {
  	    any = 0;
  	    for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i )
  		if ( bdfc->bitmap[i*bdfc->bytes_per_line+(j>>3)] & (1<<(7-(j&7))) )
  		    any = 1;
  	    if ( any )
  	break;
  	}
  	off = j;
  	if ( off/8>0 ) {
  	    for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i ) {
  		memmove(bdfc->bitmap+i*bdfc->bytes_per_line,
  			bdfc->bitmap+i*bdfc->bytes_per_line+off/8,
  			bdfc->bytes_per_line-off/8);
  		memset(bdfc->bitmap+(i+1)*bdfc->bytes_per_line-off/8,
  			0, off/8);
  	    }
  	    bdfc->xmin += off-off%8;
  	    off %= 8;
  	}
  	if ( off!=0 ) {
  	    for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i ) {
  		last = 0;
  		for ( j=bdfc->bytes_per_line-1; j>=0; --j ) {
  		    int index = i*bdfc->bytes_per_line+j;
  		    int temp = bdfc->bitmap[index]>>(8-off);
  		    bdfc->bitmap[index] = (bdfc->bitmap[index]<<off)|last;
  		    last = temp;
  		}
  		if ( last!=0 )
  		    IError("Sigh");
  	    }
  	    bdfc->xmin += off;
  	}
  
  	for ( j=bdfc->xmax-bdfc->xmin; j>0; --j ) {
  	    any = 0;
  	    for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i )
  		if ( bdfc->bitmap[i*bdfc->bytes_per_line+(j>>3)] & (1<<(7-(j&7))) )
  		    any = 1;
  	    if ( any )
  	break;
  	}
  	if ( j!=bdfc->xmax+bdfc->xmin ) {
  	    bdfc->xmax -= bdfc->xmax-bdfc->xmin-j;
  	}
  	BCRegularizeBitmap(bdfc);
      } else {
  	for ( j=0; j<bdfc->xmax-bdfc->xmin; ++j ) {
  	    any = 0;
  	    for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i )
  		if ( bdfc->bitmap[i*bdfc->bytes_per_line+j] != 0 )
  		    any = 1;
  	    if ( any )
  	break;
  	}
  	off = j;
  	if ( off!=0 ) {
  	    for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i ) {
  		memmove(bdfc->bitmap+i*bdfc->bytes_per_line,
  			bdfc->bitmap+i*bdfc->bytes_per_line+off,
  			bdfc->bytes_per_line-off);
  		memset(bdfc->bitmap+(i+1)*bdfc->bytes_per_line-off,
  			0, off);
  	    }
  	    bdfc->xmin += off;
  	}
  
  	for ( j=bdfc->xmax-bdfc->xmin; j>0; --j ) {
  	    any = 0;
  	    for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i )
  		if ( bdfc->bitmap[i*bdfc->bytes_per_line+j] != 0 )
  		    any = 1;
  	    if ( any )
  	break;
  	}
  	if ( j!=bdfc->xmax+bdfc->xmin ) {
  	    bdfc->xmax -= bdfc->xmax-bdfc->xmin-j;
  	}
  	BCRegularizeGreymap(bdfc);
      }
      if ( bdfc->xmax<bdfc->xmin || bdfc->ymax<bdfc->ymin ) {
  	bdfc->ymax = bdfc->ymin-1;
  	bdfc->xmax = bdfc->xmin-1;
      }
  }
  
  static void Bresenham(uint8 *bytemap,EdgeList *es,int x1,int x2,int y1,int y2,
  	int grey,uint8 *clipmask) {
      int dx, dy, incr1, incr2, d, x, y;
      int incr3;
      int bytes_per_line = es->bytes_per_line<<3;
      int ymax = es->cnt;
  
      /* We are guarenteed x1<=x2 */
      dx = x2-x1;
      if ( (dy = y1-y2)<0 ) dy=-dy;
      if ( dx>=dy ) {
  	d = 2 * dy - dx;
  	incr1 = 2*dy;
  	incr2 = 2*(dy-dx);
  	incr3 = y2>y1 ? 1 : -1;
  	x = x1;
  	y = y1;
  	if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  	    if ( clipmask==NULL || (clipmask[y*es->bytes_per_line+(x>>3)]&(0x80>>(x&7))) )
  		bytemap[y*bytes_per_line+x] = grey;
  	while ( x<x2 ) {
  	    ++x;
  	    if ( d<0 )
  		d += incr1;
  	    else {
  		y += incr3;
  		d += incr2;
  	    }
  	    if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  		if ( clipmask==NULL || (clipmask[y*es->bytes_per_line+(x>>3)]&(0x80>>(x&7))) )
  		    bytemap[y*bytes_per_line+x] = grey;
  	}
      } else {
  	if ( y1>y2 ) {
  	    incr1 = y1; y1 = y2; y2 = incr1;
  	    incr1 = x1; x1 = x2; x2 = incr1;
  	}
  	d = 2 * dx - dy;
  	incr1 = 2*dx;
  	incr2 = 2*(dx-dy);
  	incr3 = x2>x1 ? 1 : -1;
  	x = x1;
  	y = y1;
  	if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  	    if ( clipmask==NULL || (clipmask[y*es->bytes_per_line+(x>>3)]&(0x80>>(x&7))) )
  		bytemap[y*bytes_per_line+x] = grey;
  	while ( y<y2 ) {
  	    ++y;
  	    if ( d<0 )
  		d += incr1;
  	    else {
  		x += incr3;
  		d += incr2;
  	    }
  	    if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  		if ( clipmask==NULL || (clipmask[y*es->bytes_per_line+(x>>3)]&(0x80>>(x&7))) )
  		    bytemap[y*bytes_per_line+x] = grey;
  	}
      }
  }
  
  static void BresenhamT(uint8 *bytemap,EdgeList *es,int x1,int x2,int y1,int y2,
  	int grey,uint8 *clipmask) {
      if ( x1>x2 ) {
  	int dx, dy;
  	dx = x1; x1 = x2; x2 = dx;
  	dy = y1; y1 = y2; y2 = dy;
      }
      Bresenham(bytemap,es,x1,x2,y1,y2,grey,clipmask);
  }
  
  #if 0
  struct last_vector {
      int x1,y1;
      int x2,y2;
  };
  
  static void FillFromLast(uint8 *bytemap,struct last_vector *last,
  	int x1,int ox1,int y1,int oy1,EdgeList *es,int grey) {
      int x2 = last->x1, y2 = last->y1;
      int ox2 = last->x2, oy2 = last->y2;
      int dx, dy, incr1, incr2, d, x, y;
      int incr3;
      int bytes_per_line = es->bytes_per_line<<3;
      int ymax = es->cnt;
  
      if ( last->x1==-8000 )
  return;
  
   printf( "last (%d,%d - %d,%d)\n", last->x1,last->y1,  last->x2, last->y2 );
      BresenhamT(bytemap,es,x1,last->x1,y1,last->y1,grey);
      BresenhamT(bytemap,es,x2,last->x2,y2,last->y2,grey);
  return;
  
      if ( x1>x2 ) {
  	dx = x1; x1 = x2; x2 = dx;
  	dy = y1; y1 = y2; y2 = dy;
  	ox2 = ox1; oy2 = oy1;
  	ox1 = last->x2; oy1 = last->y2;
      }
  
      /* We are guarenteed x1<=x2 */
      dx = x2-x1;
      if ( (dy = y1-y2)<0 ) dy=-dy;
      if ( dx>=dy ) {
  	d = 2 * dy - dx;
  	incr1 = 2*dy;
  	incr2 = 2*(dy-dx);
  	incr3 = y2>y1 ? 1 : -1;
  	x = x1;
  	y = y1;
  	if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  	    BresenhamT(bytemap,es,x,ox1-(x-x1),y,oy1-(y-y1),grey);
  	while ( x<x2 ) {
  	    ++x;
  	    if ( d<0 )
  		d += incr1;
  	    else {
  		y += incr3;
  		d += incr2;
  	    }
  	    if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  		BresenhamT(bytemap,es,x,ox1-(x-x1),y,oy1-(y-y1),grey);
  	}
      } else {
  	if ( y1>y2 ) {
  	    incr1 = y1; y1 = y2; y2 = incr1;
  	    incr1 = x1; x1 = x2; x2 = incr1;
  	    incr1 = oy1; oy1 = oy2; oy2 = incr1;
  	    incr1 = ox1; ox1 = ox2; ox2 = incr1;
  	}
  	d = 2 * dx - dy;
  	incr1 = 2*dx;
  	incr2 = 2*(dx-dy);
  	incr3 = x2>x1 ? 1 : -1;
  	x = x1;
  	y = y1;
  	if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  	    BresenhamT(bytemap,es,x,ox1-(x-x1),y,oy1-(y-y1),grey);
  	while ( y<y2 ) {
  	    ++y;
  	    if ( d<0 )
  		d += incr1;
  	    else {
  		x += incr3;
  		d += incr2;
  	    }
  	    if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  		BresenhamT(bytemap,es,x,ox1-(x-x1),y,oy1-(y-y1),grey);
  	}
      }
  }
  #endif
  
  static void StrokeLine(uint8 *bytemap,IPoint *from, IPoint *to,EdgeList *es,int grey,int width, uint8 *clipmask) {
      int x1, x2, y1, y2;
      int dx, dy;
      BasePoint vector;
      bigreal len;
      int xoff, yoff;
  
      x1 = from->x - es->omin;
      x2 = to->x - es->omin;
      if ( (y1 = (es->cnt-1 - (from->y - es->mmin)))<0 ) y1=0;
      if ( (y2 = (es->cnt-1 - (to->y - es->mmin)))<0 ) y2=0;
  
      if ( width>1 ) {
  	int incr1, incr2, d, x, y;
  	int incr3;
  	int bytes_per_line = es->bytes_per_line<<3;
  	int ymax = es->cnt;
  
  	vector.x = x1-x2; vector.y = y1-y2;
  	len = vector.x*vector.x + vector.y*vector.y;
  	if ( len==0 )
  return;
  	len = sqrt(len);
  	yoff = -vector.x*width/(2.0*len);
  	xoff = vector.y*width/(2.0*len);
  	if ( xoff<0 ) {
  	    xoff = -xoff; yoff = -yoff;
  	}
  #if 0
   printf( "normal (%d,%d)\n", xoff, yoff );
  
  	FillFromLast(bytemap,last,x1-xoff,x1+xoff,y1-yoff,y2+yoff,es,grey);
  	last->x1 = x2-xoff; last->y1 = y2-yoff;
  	last->x2 = x2+xoff; last->y2 = y2+yoff;
  #endif
  
  	if ( x1>x2 ) {
  	    dx = x1; x1 = x2; x2 = dx;
  	    dy = y1; y1 = y2; y2 = dy;
  	}
  	/* This is just Bresenham's algorithem. We use it twice to draw a rectangle */
  	dx = x2-x1;
  	if ( (dy = y1-y2)<0 ) dy=-dy;
  	if ( dx>=dy ) {
  	    d = 2 * dy - dx;
  	    incr1 = 2*dy;
  	    incr2 = 2*(dy-dx);
  	    incr3 = y2>y1 ? 1 : -1;
  	    x = x1;
  	    y = y1;
  	    if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  		Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
  	    while ( x<x2 ) {
  		++x;
  		if ( d<0 )
  		    d += incr1;
  		else {
  		    if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  			Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
  		    y += incr3;
  		    d += incr2;
  		}
  		if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  		    Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
  	    }
  	} else {
  	    if ( y1>y2 ) {
  		incr1 = y1; y1 = y2; y2 = incr1;
  		incr1 = x1; x1 = x2; x2 = incr1;
  	    }
  	    d = 2 * dx - dy;
  	    incr1 = 2*dx;
  	    incr2 = 2*(dx-dy);
  	    incr3 = x2>x1 ? 1 : -1;
  	    x = x1;
  	    y = y1;
  	    if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  		Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
  	    while ( y<y2 ) {
  		++y;
  		if ( d<0 )
  		    d += incr1;
  		else {
  		    if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  			Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
  		    x += incr3;
  		    d += incr2;
  		}
  		if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
  		    Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
  	    }
  	}
      } else
  	BresenhamT(bytemap,es,x1,x2,y1,y2,grey,clipmask);
  }
  
  static void StrokeSS(uint8 *bytemap,EdgeList *es,int width,int grey,SplineSet *ss,
  	uint8 *clipmask) {
      LinearApprox *lap;
      LineList *line, *prev;
      Spline *spline, *first;
  
      for ( ; ss!=NULL; ss=ss->next ) {
  	first = NULL;
  	for ( spline = ss->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
  	    lap = SplineApproximate(spline,es->scale);
  	    if ( lap->lines!=NULL ) {
  		for ( prev = lap->lines, line=prev->next; line!=NULL; prev = line, line=line->next )
  		    StrokeLine(bytemap,&prev->here,&line->here,es,grey,width,clipmask);
  	    }
  	    if ( first == NULL ) first = spline;
  	}
      }
  }
  
  static void StrokeGlyph(uint8 *bytemap,EdgeList *es,real wid, SplineChar *sc) {
      RefChar *ref;
      int width = rint(wid*es->scale);
  
      StrokeSS(bytemap,es,width,0xff,sc->layers[ly_fore].splines,NULL);
      for ( ref=sc->layers[ly_fore].refs; ref!=NULL; ref = ref->next )
  	StrokeSS(bytemap,es,width,0xff,ref->layers[0].splines,NULL);
  }
  
  #ifdef FONTFORGE_CONFIG_TYPE3
  static void StrokePaths(uint8 *bytemap,EdgeList *es,Layer *layer,Layer *alt,
  	uint8 *clipmask) {
      uint32 col;
      int width;
      int grey;
  
      if ( layer->stroke_pen.brush.col!=COLOR_INHERITED )
  	col = layer->stroke_pen.brush.col;
      else if ( alt!=NULL && alt->stroke_pen.brush.col!=COLOR_INHERITED )
  	col = alt->stroke_pen.brush.col;
      else
  	col = 0x000000;
      if ( layer->stroke_pen.width!=WIDTH_INHERITED )
  	width = rint(layer->stroke_pen.width*layer->stroke_pen.trans[0]*es->scale);
      else if ( alt!=NULL && alt->stroke_pen.width!=WIDTH_INHERITED )
  	width = rint(alt->stroke_pen.width*alt->stroke_pen.trans[0]*es->scale);
      else
  	width = 1;
      grey = ( ((col>>16)&0xff)*3 + ((col>>8)&0xff)*6 + (col&0xff) )/ 10;
      /* but our internal greymap convention is backwards */
      grey = 255-grey;
  
      StrokeSS(bytemap,es,width,grey,layer->splines,clipmask);
  }
  
  static int PatternHere(bigreal scale,DBounds *bbox,int iy,int ix,
  	struct pattern *pat) {
      real x,y,tempx;
      int grey;
      BDFChar *bdfc;
  
      if ( pat==NULL || pat->pat==NULL )
  return( 0 );
  
      x = (ix+.5)/scale + bbox->minx;
      y = bbox->maxy-(iy-.5)/scale;
      tempx = pat->invtrans[0]*x + pat->invtrans[2]*y + pat->invtrans[4];
      y     = pat->invtrans[1]*x + pat->invtrans[3]*y + pat->invtrans[5];
      x = tempx;
      if ( (x = fmod(x,pat->width))<0 ) x+=pat->width;
      if ( (y = fmod(y,pat->height))<0 ) y+=pat->height;
      bdfc = pat->pat;
      ix = pat->bminx + rint(x*pat->bwidth/pat->width);
      iy = pat->bminy + rint(y*pat->bheight/pat->height);
      ix -= bdfc->xmin;
      iy = bdfc->ymax-1-iy;
      if ( ix<0 || iy<0 || ix>=bdfc->xmax || iy>=bdfc->ymax )
  return( 0 );
  	/* bdfc has a 4bit grey, convert to 8bit grey by multiplying by 17 */
      grey = 17*bdfc->bitmap[ iy*bdfc->bytes_per_line + ix];
  return( grey );
  }
  
  int GradientHere(bigreal scale,DBounds *bbox,int iy,int ix,
  	struct gradient *grad, struct pattern *pat,
  	int defgrey) {
      real x,y;
      BasePoint unit, offset;
      bigreal len, relpos;
      int i, col, grey;
  
      if ( grad==NULL ) {
  	if ( pat!=NULL && pat->pat!=NULL )
  return( PatternHere(scale,bbox,iy,ix,pat));
  	else
  return( defgrey );
      }
  
      x = (ix+.5)/scale + bbox->minx;
      y = bbox->maxy-(iy-.5)/scale;
      if ( grad->radius==0 ) {	/* Linear gradient */
  	unit.x = grad->stop.x - grad->start.x;
  	unit.y = grad->stop.y - grad->start.y;
  	len = sqrt(unit.x*unit.x + unit.y*unit.y);
  	if ( len==0 )
  return ( defgrey );
  	unit.x /= len;
  	unit.y /= len;
  	offset.x = x-grad->start.x;
  	offset.y = y-grad->start.y;
  	relpos = unit.x*offset.x + unit.y*offset.y;
  	relpos /= len;
      } else /*if ( grad->start.x==grad->stop.x && grad->start.y==grad->stop.y )*/ {
  	offset.x = x-grad->start.x;
  	offset.y = y-grad->start.y;
  	len = sqrt(offset.x*offset.x + offset.y*offset.y);
  	relpos = len/grad->radius;
      }
      if ( grad->sm==sm_repeat ) {
  	relpos = fmod(relpos,1.0);
  	if ( relpos<0 ) relpos += 1.0;
      } else if ( grad->sm==sm_reflect ) {
  	relpos = fmod(relpos,2.0);
  	if ( relpos<0 ) relpos += 2.0;
  	if ( relpos>1 ) relpos = 2.0-relpos;
      } else { /* sm_pad */
  	if ( relpos<0 ) relpos = 0;
  	if ( relpos>1 ) relpos = 1;
      }
      for ( i=0; i<grad->stop_cnt; ++i )
  	if ( relpos<=grad->grad_stops[i].offset )
      break;
      if ( i>=grad->stop_cnt )
  	col = grad->grad_stops[i-1].col;
      else if ( relpos==grad->grad_stops[i].offset || i==0 )
  	col = grad->grad_stops[i].col;
      else {
  	bigreal percent = (relpos-grad->grad_stops[i-1].offset)/ (grad->grad_stops[i].offset-grad->grad_stops[i-1].offset);
  	uint32 col1 = grad->grad_stops[i-1].col;
  	uint32 col2 = grad->grad_stops[i  ].col;
  	if ( col1==COLOR_INHERITED ) col1 = 0x000000;
  	if ( col2==COLOR_INHERITED ) col2 = 0x000000;
  	int red   = ((col1>>16)&0xff)*(1-percent) + ((col2>>16)&0xff)*percent;
  	int green = ((col1>>8 )&0xff)*(1-percent) + ((col2>>8 )&0xff)*percent;
  	int blue  = ((col1    )&0xff)*(1-percent) + ((col2    )&0xff)*percent;
  	col = (red<<16) | (green<<8) | blue;
      }
      if ( col==COLOR_INHERITED ) col = 0x000000;
      grey = ( ((col>>16)&0xff)*3 + ((col>>8)&0xff)*6 + (col&0xff) )/ 10;
      /* but our internal greymap convention is backwards */
  return( 255-grey );
  }
  	
  void PatternPrep(SplineChar *sc,struct brush *brush,bigreal scale) {
      SplineChar *psc;
      int pixelsize;
      SplineFont *sf;
      struct pattern *pattern;
      DBounds b;
  
      if ( brush->gradient!=NULL )
  return;
      if ( (pattern = brush->pattern)==NULL )
  return;
      if ( pattern->pat!=NULL )		/* Recursive pattern? */
  return;
  
      sf = sc->parent;
      psc = SFGetChar(sf,-1,pattern->pattern);
      if ( psc==NULL )
  return;
      PatternSCBounds(psc,&b);
      pixelsize = rint(scale*pattern->height*(sf->ascent+sf->descent)/(b.maxy-b.miny));
      if ( pixelsize<=1 )		/* Pattern too small to show anything */
  return;
      pattern->bheight = rint(scale*pattern->height);
      pattern->bwidth = rint(scale*pattern->width*(b.maxx-b.minx)/(b.maxy-b.miny));
      pattern->bminx = rint(scale*b.minx*pattern->width/(b.maxx-b.minx));
      pattern->bminy = rint(scale*b.miny*pattern->height/(b.maxy-b.miny));
      pattern->pat = SplineCharAntiAlias(psc,ly_fore,pixelsize,4);
      MatInverse(pattern->invtrans,pattern->transform);
  }
  
  static void SetByteMapToGrey(uint8 *bytemap,EdgeList *es,Layer *layer,Layer *alt,
  	uint8 *clipmask,SplineChar *sc) {
      uint32 col;
      int grey,i,j;
      uint8 *pt, *bpt, *cpt;
      struct gradient *grad = layer->fill_brush.gradient;
      struct pattern *pat  = layer->fill_brush.pattern;
  
      if ( layer->fill_brush.col!=COLOR_INHERITED )
  	col = layer->fill_brush.col;
      else if ( alt!=NULL && alt->fill_brush.col!=COLOR_INHERITED )
  	col = alt->fill_brush.col;
      else
  	col = 0x000000;
      grey = ( ((col>>16)&0xff)*3 + ((col>>8)&0xff)*6 + (col&0xff) )/ 10;
      /* but our internal greymap convention is backwards */
      grey = 255-grey;
      PatternPrep(sc,&layer->fill_brush,es->scale);
  
      for ( i=0; i<es->cnt; ++i ) {
  	bpt = es->bitmap + i*es->bytes_per_line;
  	cpt = clipmask + i*es->bytes_per_line;
  	pt = bytemap + i*8*es->bytes_per_line;
  	for ( j=0; j<8*es->bytes_per_line; ++j ) {
  	    if ( clipmask==NULL || (cpt[j>>3]&(0x80>>(j&7))) )
  		if ( bpt[j>>3]&(0x80>>(j&7)) ) {
  		    pt[j] = GradientHere(es->scale,&es->bbox,i,j,grad,pat,grey);
  		}
  	}
      }
      if ( pat!=NULL ) {
  	BDFCharFree(pat->pat);
  	pat->pat = NULL;
      }
  }
  
  static void FillImages(uint8 *bytemap,EdgeList *es,ImageList *img,Layer *layer,
  	Layer *alt, uint8 *clipmask) {
      uint32 fillcol, col;
      int grey,i,j,x1,x2,y1,y2,jj,ii;
  
      if ( layer->fill_brush.col!=COLOR_INHERITED )
  	fillcol = layer->fill_brush.col;
      else if ( alt!=NULL && alt->fill_brush.col!=COLOR_INHERITED )
  	fillcol = alt->fill_brush.col;
      else
  	fillcol = 0x000000;
  
      while ( img!=NULL ) {
  	struct _GImage *base = img->image->list_len==0?
  		img->image->u.image:img->image->u.images[0];
  
  	y1 = es->cnt-1 - (img->yoff*es->scale - es->mmin);
  	y2 = es->cnt-1 - ((img->yoff-base->height*img->yscale)*es->scale -es->mmin);
  	x1 = img->xoff*es->scale - es->omin;
  	x2 = (img->xoff+base->width*img->xscale)*es->scale - es->omin;
  	if ( y1==y2 || x1==x2 )	/* too small to show */
      continue;
  	for ( i=0; i<=y2-y1; ++i ) {
  	    if ( i+y1<0 || i+y1>=es->cnt )	/* Shouldn't happen, rounding errors might gives us off by 1s though */
  	continue;
  	    ii = i*(base->height-1)/(y2-y1);
  	    for ( j=0; j<x2-x1; ++j ) {
  		if ( j+x1<0 || j+x1>=8*es->bytes_per_line )
  	    continue;
  		jj = j*(base->width-1)/(x2-x1);
  		if ( base->image_type==it_true )
  		    col = ((uint32 *) (base->data + ii*base->bytes_per_line))[jj];
  		else if ( base->image_type==it_index ) {
  		    col = (base->data + ii*base->bytes_per_line)[jj];
  		    col = base->clut->clut[col];
  		} else if ( layer->dofill && base->trans!=-1) {		/* Equivalent to imagemask */
  		    if ( (base->trans==0 && !( (base->data + ii*base->bytes_per_line)[jj>>3]&(0x80>>(jj&7)) ) ) ||
  			    (base->trans!=0 && ( (base->data + ii*base->bytes_per_line)[jj>>3]&(0x80>>(jj&7)) ) ))
  	    continue;	/* transparent */
  		    col = fillcol;
  		} else {
  		    int index = 0;
  		    if ( (base->data + ii*base->bytes_per_line)[jj>>3]&(0x80>>(j&7)) )
  			index = 1;
  		    if ( base->clut!=NULL ) {
  			col = base->clut->clut[index];
  			if ( col==0xb0b0b0 ) col = 0xffffff;
  		    } else if ( index==1 )
  			col = 0xffffff;
  		    else
  			col = 0x000000;
  		}
  		grey = ( ((col>>16)&0xff)*3 + ((col>>8)&0xff)*6 + (col&0xff) )/ 10;
  		/* but our internal greymap convention is backwards */
  		grey = 255-grey;
  		if ( clipmask==NULL || (clipmask[(i+y1)*es->bytes_per_line + ((j+x1)>>3)]&(0x80>>((j+x1)&7)) ))
  		    bytemap[(i+y1)*8*es->bytes_per_line + j+x1] = grey;
  	    }
  	}
  	img = img->next;
      }
  }
  
  static void ProcessLayer(uint8 *bytemap,EdgeList *es,Layer *layer,
  	Layer *alt, uint8 *clipmask,SplineChar *sc) {
      ImageList *img;
  
      if ( !layer->fillfirst && layer->dostroke )
  	StrokePaths(bytemap,es,layer,alt,clipmask);
      if ( layer->dofill ) {
  	memset(es->bitmap,0,es->cnt*es->bytes_per_line);
  	FindEdgesSplineSet(layer->splines,es,true);
  	FillChar(es);
  	SetByteMapToGrey(bytemap,es,layer,alt, clipmask,sc);
  	_FreeEdgeList(es);
      }
      for ( img = layer->images; img!=NULL; img=img->next )
  	FillImages(bytemap,es,img,layer,alt,clipmask);
      if ( layer->fillfirst && layer->dostroke )
  	StrokePaths(bytemap,es,layer,alt,clipmask);
  }
  
  static uint8 *ProcessClipMask(EdgeList *es,Layer *layer) {
      uint8 *clipmask;
  
      if ( !SSHasClip(layer->splines) )
  return( NULL );
      clipmask = gcalloc(es->cnt*es->bytes_per_line,1);
      memset(es->bitmap,0,es->cnt*es->bytes_per_line);
      FindEdgesSplineSet(layer->splines,es,2);
      FillChar(es);
      memcpy(clipmask,es->bitmap,es->cnt*es->bytes_per_line);
      _FreeEdgeList(es);
  return( clipmask );
  }
  #endif
  
  static void FlattenBytemap(EdgeList *es,uint8 *bytemap) {
      int i,j;
      uint8 *bpt, *pt;
  
      memset(es->bitmap,0,es->cnt*es->bytes_per_line);
      for ( i=0; i<es->cnt; ++i ) {
  	bpt = es->bitmap + i*es->bytes_per_line;
  	pt = bytemap + i*8*es->bytes_per_line;
  	for ( j=0; j<8*es->bytes_per_line; ++j )
  	    if ( pt[j]>=128 )
  		bpt[j>>3] |= (0x80>>(j&7));
      }
  }
  
  static int FigureBitmap(EdgeList *es,uint8 *bytemap, int is_aa) {
      if ( is_aa ) {
  	free(es->bitmap);
  	es->bitmap = bytemap;
  return( 8 );
      } else {
  	FlattenBytemap(es,bytemap);
  	free(bytemap);
  return( 0 );
      }
  }
  
  /* Yes, I really do want a double, even though it will almost always be an */
  /*  integer value there are a few cases (fill pattern for charview) where */
  /*  I need more precision and the pixelsize itself is largely irrelevant */
  /*  (I care about the scale though) */
  static BDFChar *_SplineCharRasterize(SplineChar *sc, int layer, bigreal pixelsize, int is_aa) {
      EdgeList es;
      BDFChar *bdfc;
      int depth = 0;
  
      if ( sc==NULL )
  return( NULL );
      memset(&es,'\0',sizeof(es));
      if ( sc==NULL ) {
  	es.mmin = es.mmax = es.omin = es.omax = 0;
  	es.bitmap = gcalloc(1,1);
  	es.bytes_per_line = 1;
  	is_aa = false;
      } else {
  #ifdef FONTFORGE_CONFIG_TYPE3
  	if ( sc->parent->multilayer )
  	    /* I need to include the clipping path, FindBounds excludes it */
  	    SplineCharQuickConservativeBounds(sc,&es.bbox);
  	else
  #endif
  	    SplineCharFindBounds(sc,&es.bbox);
  	es.scale = (pixelsize-.1) / (real) (sc->parent->ascent+sc->parent->descent);
  	es.mmin = floor(es.bbox.miny*es.scale);
  	es.mmax = ceil(es.bbox.maxy*es.scale);
  	es.omin = es.bbox.minx*es.scale;
  	es.omax = es.bbox.maxx*es.scale;
  	es.cnt = (int) (es.mmax-es.mmin) + 1;
  	es.layer = layer;
  	if ( es.cnt<8000 && es.omax-es.omin<8000 && es.cnt>1 ) {
  	    es.edges = gcalloc(es.cnt,sizeof(Edge *));
  	    es.sc = sc;
  	    es.major = 1; es.other = 0;
  	    es.bytes_per_line = ((int) ceil(es.omax-es.omin) + 8)/8;
  	    es.bitmap = gcalloc(es.cnt*es.bytes_per_line,1);
  
  	    InitializeHints(sc,&es);
  #ifdef FONTFORGE_CONFIG_TYPE3
  	    if ( sc->parent->multilayer ) {
  		uint8 *bytemap = gcalloc(es.cnt*es.bytes_per_line*8,1);
  		int layer, i;
  		RefChar *rf;
  		for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) {
  		    uint8 *clipmask = ProcessClipMask(&es,&sc->layers[layer]);
  		    ProcessLayer(bytemap,&es,&sc->layers[layer],NULL,clipmask,sc);
  
  		    for ( rf=sc->layers[layer].refs; rf!=NULL; rf = rf->next ) {
  			for ( i=0; i<rf->layer_cnt; ++i ) {
  			    ProcessLayer(bytemap,&es,(Layer *) (&rf->layers[i]),
  				    &sc->layers[layer],clipmask,sc);
  			}
  		    }
  		    free(clipmask);
  		}
  		depth = FigureBitmap(&es,bytemap,is_aa);
  	    } else
  #endif
  	    if ( sc->parent->strokedfont ) {
  		uint8 *bytemap = gcalloc(es.cnt*es.bytes_per_line*8,1);
  		StrokeGlyph(bytemap,&es,sc->parent->strokewidth,sc);
  		depth = FigureBitmap(&es,bytemap,is_aa);
  	    } else {
  		FindEdges(sc,&es);
  		FillChar(&es);
  		depth = 0;
  	    }
  	} else {
  	    /* If they want a bitmap so enormous it threatens our memory */
  	    /*  then just give 'em a blank. It's probably by mistake anyway */
  	    es.mmin = es.mmax = es.omin = es.omax = 0;
  	    es.bitmap = gcalloc(1,1);
  	    es.bytes_per_line = 1;
  	}
      }
  
      bdfc = chunkalloc(sizeof(BDFChar));
      memset( bdfc,'\0',sizeof( BDFChar ));
      bdfc->sc = sc;
      bdfc->xmin = rint(es.omin);
      bdfc->ymin = es.mmin;
      bdfc->xmax = (int) ceil(es.omax-es.omin) + bdfc->xmin;
      bdfc->ymax = es.mmax;
      if ( sc!=NULL ) {
  	bdfc->width = rint(sc->width*pixelsize / (real) (sc->parent->ascent+sc->parent->descent));
  	bdfc->vwidth = rint(sc->vwidth*pixelsize / (real) (sc->parent->ascent+sc->parent->descent));
  	bdfc->orig_pos = sc->orig_pos;
      }
      bdfc->bitmap = es.bitmap;
      bdfc->depth = depth;
      bdfc->bytes_per_line = es.bytes_per_line;
      if ( depth==8 ) {
  	bdfc->byte_data = true;
  	bdfc->bytes_per_line *= 8;
      }
      BCCompressBitmap(bdfc);
      FreeEdges(&es);
  return( bdfc );
  }
  
  BDFChar *SplineCharRasterize(SplineChar *sc, int layer, bigreal pixelsize) {
  return( _SplineCharRasterize(sc,layer,pixelsize,false));
  }
  
  BDFFont *SplineFontToBDFHeader(SplineFont *_sf, int pixelsize, int indicate) {
      BDFFont *bdf = gcalloc(1,sizeof(BDFFont));
      int i;
      real scale;
      char size[40];
      char aa[200];
      int max;
      SplineFont *sf;	/* The complexity here is to pick the appropriate subfont of a CID font */
  
      sf = _sf;
      max = sf->glyphcnt;
      for ( i=0; i<_sf->subfontcnt; ++i ) {
  	sf = _sf->subfonts[i];
  	if ( sf->glyphcnt>max ) max = sf->glyphcnt;
      }
      scale = pixelsize / (real) (sf->ascent+sf->descent);
  
      if ( indicate ) {
  	sprintf(size,_("%d pixels"), pixelsize );
  	strcpy(aa,_("Generating bitmap font"));
  	if ( sf->fontname!=NULL ) {
  	    strcat(aa,": ");
  	    strncat(aa,sf->fontname,sizeof(aa)-strlen(aa));
  	    aa[sizeof(aa)-1] = '\0';
  	}
  	ff_progress_start_indicator(10,_("Rasterizing..."),
  		aa,size,sf->glyphcnt,1);
  	ff_progress_enable_stop(0);
      }
      bdf->sf = _sf;
      bdf->glyphcnt = bdf->glyphmax = max;
      bdf->pixelsize = pixelsize;
      bdf->glyphs = galloc(max*sizeof(BDFChar *));
      bdf->ascent = rint(sf->ascent*scale);
      bdf->descent = pixelsize-bdf->ascent;
      bdf->res = -1;
  return( bdf );
  }
  
  BDFFont *SplineFontRasterize(SplineFont *_sf, int layer, int pixelsize, int indicate) {
      BDFFont *bdf = SplineFontToBDFHeader(_sf,pixelsize,indicate);
      int i,k;
      SplineFont *sf=_sf;	/* The complexity here is to pick the appropriate subfont of a CID font */
  
      for ( i=0; i<bdf->glyphcnt; ++i ) {
  	if ( _sf->subfontcnt!=0 ) {
  	    for ( k=0; k<_sf->subfontcnt; ++k ) if ( _sf->subfonts[k]->glyphcnt>i ) {
  		sf = _sf->subfonts[k];
  		if ( SCWorthOutputting(sf->glyphs[i]))
  	    break;
  	    }
  	}
  	bdf->glyphs[i] = SplineCharRasterize(sf->glyphs[i],layer,pixelsize);
  	if ( indicate ) ff_progress_next();
      }
      if ( indicate ) ff_progress_end_indicator();
  return( bdf );
  }
  
  void BDFCAntiAlias(BDFChar *bc, int linear_scale) {
      BDFChar new;
      int i,j, l2 = linear_scale*linear_scale, max = l2-1;
      uint8 *bpt, *pt;
  
      if ( bc==NULL )
  return;
  
      memset(&new,'\0',sizeof(new));
      new.xmin = floor( ((real) bc->xmin)/linear_scale );
      new.ymin = floor( ((real) bc->ymin)/linear_scale );
      new.xmax = new.xmin + (bc->xmax-bc->xmin+linear_scale-1)/linear_scale;
      new.ymax = new.ymin + (bc->ymax-bc->ymin+linear_scale-1)/linear_scale;
      new.width = rint( ((real) bc->width)/linear_scale );
  
      new.bytes_per_line = (new.xmax-new.xmin+1);
      new.orig_pos = bc->orig_pos;
      new.sc = bc->sc;
      new.byte_data = true;
      new.depth = max==3 ? 2 : max==15 ? 4 : 8;
      new.bitmap = gcalloc( (new.ymax-new.ymin+1) * new.bytes_per_line, sizeof(uint8));
      if ( bc->depth>1 ) {
  	uint32 *sum = gcalloc(new.bytes_per_line,sizeof(uint32));
  	for ( i=0; i<=bc->ymax-bc->ymin; ++i ) {
  	    bpt = bc->bitmap + i*bc->bytes_per_line;
  	    for ( j=0; j<=bc->xmax-bc->xmin; ++j ) {
  		sum[j/linear_scale] += bpt[j];
  	    }
  	    if ( (i+1)%linear_scale==0 ) {
  		pt = new.bitmap + (i/linear_scale)*new.bytes_per_line;
  		for ( j=(bc->xmax-bc->xmin)/linear_scale-1; j>=0 ; --j ) {
  		    int val = (sum[j]+128)/255;
  		    if ( val>max ) val = max;
  		    pt[j] = val;
  		}
  		memset(sum,0,new.bytes_per_line*sizeof(uint32));
  	    }
  	}
      } else {
  	for ( i=0; i<=bc->ymax-bc->ymin; ++i ) {
  	    bpt = bc->bitmap + i*bc->bytes_per_line;
  	    pt = new.bitmap + (i/linear_scale)*new.bytes_per_line;
  	    for ( j=0; j<=bc->xmax-bc->xmin; ++j ) {
  		if ( bpt[(j>>3)] & (1<<(7-(j&7))) )
  		    /* we can get values between 0..2^n we want values between 0..(2^n-1) */
  		    if ( pt[ j/linear_scale ]!=max )
  			++pt[ j/linear_scale ];
  	    }
  	}
      }
      free(bc->bitmap);
      *bc = new;
  }
  
  GClut *_BDFClut(int linear_scale) {
      int scale = linear_scale*linear_scale, i;
      Color bg = default_background;
      int bgr=((bg>>16)&0xff), bgg=((bg>>8)&0xff), bgb= (bg&0xff);
      GClut *clut;
  
      clut = gcalloc(1,sizeof(GClut));
      clut->clut_len = scale;
      clut->is_grey = (bgr==bgg && bgb==bgr);
      clut->trans_index = -1;
      for ( i=0; i<scale; ++i ) {
  	clut->clut[i] =
  		COLOR_CREATE( bgr- (i*(bgr))/(scale-1),
  				bgg- (i*(bgg))/(scale-1),
  				bgb- (i*(bgb))/(scale-1));
      }
      clut->clut[scale-1] = 0;	/* avoid rounding errors */
  return( clut );
  }
  
  void BDFClut(BDFFont *bdf, int linear_scale) {
      bdf->clut = _BDFClut(linear_scale);
  }
  
  int BDFDepth(BDFFont *bdf) {
      if ( bdf->clut==NULL )
  return( 1 );
  
  return( bdf->clut->clut_len==256 ? 8 :
  	bdf->clut->clut_len==16 ? 4 : 2);
  }
  
  BDFChar *SplineCharAntiAlias(SplineChar *sc, int layer, int pixelsize, int linear_scale) {
      BDFChar *bc;
  
      bc = _SplineCharRasterize(sc,layer, pixelsize*linear_scale,true);
      if ( linear_scale!=1 )
  	BDFCAntiAlias(bc,linear_scale);
      BCCompressBitmap(bc);
  return( bc );
  }
  
  BDFFont *SplineFontAntiAlias(SplineFont *_sf, int layer, int pixelsize, int linear_scale) {
      BDFFont *bdf;
      int i,k;
      real scale;
      char size[40];
      char aa[200];
      int max;
      SplineFont *sf;	/* The complexity here is to pick the appropriate subfont of a CID font */
  
      if ( linear_scale==1 )
  return( SplineFontRasterize(_sf,layer,pixelsize,true));
  
      bdf = gcalloc(1,sizeof(BDFFont));
      sf = _sf;
      max = sf->glyphcnt;
      for ( i=0; i<_sf->subfontcnt; ++i ) {
  	sf = _sf->subfonts[i];
  	if ( sf->glyphcnt>max ) max = sf->glyphcnt;
      }
      scale = pixelsize / (real) (sf->ascent+sf->descent);
  
      sprintf(size,_("%d pixels"), pixelsize );
      strcpy(aa,_("Generating anti-alias font"));
      if ( sf->fontname!=NULL ) {
  	strcat(aa,": ");
  	strncat(aa,sf->fontname,sizeof(aa)-strlen(aa));
  	aa[sizeof(aa)-1] = '\0';
      }
      ff_progress_start_indicator(10,_("Rasterizing..."),
  	    aa,size,sf->glyphcnt,1);
      ff_progress_enable_stop(0);
  
      if ( linear_scale>16 ) linear_scale = 16;	/* can't deal with more than 256 levels of grey */
      if ( linear_scale<=1 ) linear_scale = 2;
      bdf->sf = _sf;
      bdf->glyphcnt = bdf->glyphmax = max;
      bdf->pixelsize = pixelsize;
      bdf->glyphs = galloc(max*sizeof(BDFChar *));
      bdf->ascent = rint(sf->ascent*scale);
      bdf->descent = pixelsize-bdf->ascent;
      bdf->res = -1;
      for ( i=0; i<max; ++i ) {
  	if ( _sf->subfontcnt!=0 ) {
  	    for ( k=0; k<_sf->subfontcnt; ++k ) if ( _sf->subfonts[k]->glyphcnt>i ) {
  		sf = _sf->subfonts[k];
  		if ( SCWorthOutputting(sf->glyphs[i]))
  	    break;
  	    }
  	    scale = pixelsize / (real) (sf->ascent+sf->descent);
  	}
  	bdf->glyphs[i] = SplineCharRasterize(sf->glyphs[i],layer,pixelsize*linear_scale);
  	BDFCAntiAlias(bdf->glyphs[i],linear_scale);
  	ff_progress_next();
      }
      BDFClut(bdf,linear_scale);
      ff_progress_end_indicator();
  return( bdf );
  }
  
  static void ByteMult(BDFChar *bc,int factor) {
      /* Internal rasterizer uses bit depth 4, but font expects 8. Correct by multiplying each byte by 17 */
      uint8 *pt, *end;
  
      pt = bc->bitmap;
      end = pt + bc->bytes_per_line*(bc->ymax-bc->ymin+1);
      while ( pt<end )
  	*pt++ *= factor;
  }
  
  BDFChar *BDFPieceMeal(BDFFont *bdf, int index) {
      SplineChar *sc;
  
      if ( index<0 )
  return( NULL );
      if ( bdf->glyphcnt<bdf->sf->glyphcnt ) {
  	if ( bdf->glyphmax<bdf->sf->glyphcnt )
  	    bdf->glyphs = grealloc(bdf->glyphs,(bdf->glyphmax = bdf->sf->glyphmax)*sizeof(BDFChar *));
  	memset(bdf->glyphs+bdf->glyphcnt,0,(bdf->glyphmax-bdf->glyphcnt)*sizeof(SplineChar *));
  	bdf->glyphcnt = bdf->sf->glyphcnt;
      }
      if ( index >= bdf->glyphcnt )
  return( NULL );
  
      sc = bdf->sf->glyphs[index];
      if ( sc==NULL )
  return(NULL);
      if ( bdf->freetype_context )
  	bdf->glyphs[index] = SplineCharFreeTypeRasterize(bdf->freetype_context,
  		sc->orig_pos,bdf->ptsize,bdf->dpi,bdf->clut?8:1);
      else if ( bdf->recontext_freetype ) {
  	void *ft_context = FreeTypeFontContext(bdf->sf,sc,NULL,bdf->layer);
  	if ( ft_context!=NULL ) {
  	    bdf->glyphs[index] = SplineCharFreeTypeRasterize(ft_context,
  		    sc->orig_pos,bdf->ptsize,bdf->dpi,bdf->clut?8:1);
  	    FreeTypeFreeContext(ft_context);
  	}
      } else if ( bdf->unhinted_freetype )
  	bdf->glyphs[index] = SplineCharFreeTypeRasterizeNoHints(sc,
  		bdf->layer,bdf->ptsize,bdf->dpi,bdf->clut?4:1);
      else
  	bdf->glyphs[index] = NULL;
      if ( bdf->glyphs[index]==NULL ) {
  	if ( bdf->clut ) {
  	    bdf->glyphs[index] = SplineCharAntiAlias(sc,bdf->layer,bdf->truesize,4);
  	    if ( bdf->freetype_context!=NULL || bdf->recontext_freetype || bdf->unhinted_freetype )
  		ByteMult(bdf->glyphs[index],17);	/* Internal rasterizer uses bit depth 4, but font expects 8. Correct by multiplying each byte by 17 */
  	} else
  	    bdf->glyphs[index] = SplineCharRasterize(sc,bdf->layer,bdf->truesize);
      }
  return( bdf->glyphs[index] );
  }
  
  BDFChar *BDFPieceMealCheck(BDFFont *bdf, int index) {
      if ( index<0 )
  return( NULL );
      if ( index>=bdf->glyphcnt )
  return(BDFPieceMeal(bdf,index));
  
      if ( bdf->glyphs[index]!=NULL )
  return( bdf->glyphs[index]);
  
  return(BDFPieceMeal(bdf,index));
  }
  
  /* Piecemeal fonts are used as the display font in the fontview, metricsview and other places*/
  /*  as such they are simple fonts (ie. we only display the current cid subfont) */
  BDFFont *SplineFontPieceMeal(SplineFont *sf,int layer,int ptsize,int dpi,
  	int flags,void *ftc) {
      BDFFont *bdf = gcalloc(1,sizeof(BDFFont));
      real scale;
      int pixelsize = (int) rint( (ptsize*dpi)/72.0 );
      int truesize = pixelsize;
  
      if ( flags&pf_bbsized ) {
  	DBounds bb;
  	if ( sf->multilayer )
  	    /* Quick bounds doesn't handle clipping paths, and we want that */
  	    /*  handled for type3 */
  	    SplineFontFindBounds(sf,&bb);
  	else
  	    SplineFontQuickConservativeBounds(sf,&bb);
  	if ( bb.maxy<sf->ascent ) bb.maxy = sf->ascent;
  	if ( bb.miny>-sf->descent ) bb.miny = -sf->descent;
  	/* Ignore absurd values */
  	if ( bb.maxy>10*(sf->ascent+sf->descent) ) bb.maxy = 2*(sf->ascent+sf->descent);
  	if ( bb.maxx>10*(sf->ascent+sf->descent) ) bb.maxx = 2*(sf->ascent+sf->descent);
  	if ( bb.miny<-10*(sf->ascent+sf->descent) ) bb.miny = -2*(sf->ascent+sf->descent);
  	if ( bb.minx<-10*(sf->ascent+sf->descent) ) bb.minx = -2*(sf->ascent+sf->descent);
  	scale = pixelsize/ (real) (bb.maxy-bb.miny);
  	bdf->ascent = rint(bb.maxy*scale);
  	truesize = rint( (sf->ascent+sf->descent)*scale );
  	if ( pixelsize!=0 )
  	    ptsize = rint( ptsize*(double) truesize/pixelsize );
      } else {
  	scale = pixelsize / (real) (sf->ascent+sf->descent);
  	bdf->ascent = rint(sf->ascent*scale);
      }
      if ( flags&pf_ft_nohints )
  	bdf->unhinted_freetype = true;
      else if ( flags&pf_ft_recontext )
  	bdf->recontext_freetype = true;
  
      bdf->sf = sf;
      bdf->layer = layer;
      bdf->glyphcnt = bdf->glyphmax = sf->glyphcnt;
      bdf->pixelsize = pixelsize;
      bdf->truesize = truesize;
      bdf->ptsize = ptsize;
      bdf->dpi = dpi;
      bdf->glyphs = gcalloc(sf->glyphcnt,sizeof(BDFChar *));
      bdf->descent = pixelsize-bdf->ascent;
      bdf->piecemeal = true;
      bdf->bbsized = (flags&pf_bbsized)?1:0;
      bdf->res = -1;
      bdf->freetype_context = ftc;
  
      if ( ftc==NULL && (bdf->recontext_freetype || bdf->unhinted_freetype) &&
  	    !hasFreeType())
  	bdf->recontext_freetype = bdf->unhinted_freetype = false;
  
      if ( (ftc || bdf->recontext_freetype || bdf->recontext_freetype) && (flags&pf_antialias) )
  	BDFClut(bdf,16);
      else if ( flags&pf_antialias )
  	BDFClut(bdf,4);
  return( bdf );
  }
  
  void BDFCharFree(BDFChar *bdfc) {
      BDFRefChar *head, *cur;
      
      if ( bdfc==NULL )
  return;
      for ( head = bdfc->refs; head != NULL; ) {
  	cur = head; head = head->next; free( cur );
      }
      free(bdfc->bitmap);
      chunkfree(bdfc,sizeof(BDFChar));
  }
  
  void BDFPropsFree(BDFFont *bdf) {
      int i;
  
      for ( i=0; i<bdf->prop_cnt; ++i ) {
  	free(bdf->props[i].name);
  	if ( (bdf->props[i].type&~prt_property)==prt_string ||
  		 (bdf->props[i].type&~prt_property)==prt_atom )
  	     free(bdf->props[i].u.str);
       }
       free( bdf->props );
  }
  
  void BDFFontFree(BDFFont *bdf) {
      int i;
  
      if ( bdf==NULL )
  return;
      for ( i=0; i<bdf->glyphcnt; ++i )
  	BDFCharFree(bdf->glyphs[i]);
      free(bdf->glyphs);
      if ( bdf->clut!=NULL )
  	free(bdf->clut);
      if ( bdf->freetype_context!=NULL )
  	FreeTypeFreeContext(bdf->freetype_context);
      BDFPropsFree(bdf);
      free( bdf->foundry );
      free(bdf);
  }
  