/* This file is part of the GNU libxmi package.  

   Copyright (C) 1985, 1986, 1987, 1988, 1989, X Consortium.  For an
   associated permission notice, see the accompanying file README-X.
   
   GNU enhancements Copyright (C) 1998, 1999, 2000, 2005, Free Software
   Foundation, Inc.

   The GNU libxmi package is free software.  You may redistribute it
   and/or modify it under the terms of the GNU General Public License as
   published by the Free Software foundation; either version 2, or (at your
   option) any later version.

   The GNU libxmi package is distributed in the hope that it will be
   useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with the GNU plotutils package; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
   Boston, MA 02110-1301, USA. */

/* This module provides several public functions: miNewPaintedSet(),
   miAddSpansToPaintedSet(), miUniquifyPaintedSet(), miClearPaintedSet(),
   miDeletePaintedSet().  They maintain a structure called a miPaintedSet,
   which is essentially an array of SpanGroup structures, one per pixel
   value.  A SpanGroup is essentially an unsorted list of Spans's.  A Spans
   is a list of spans (i.e. horizontal ranges) of miPoints, sorted so that
   the starting points have increasing y-values.  See mi_spans.h.

   Internally, each libxmi drawing function paints to a miPaintedSet by
   calling miAddSpansToPaintedSet() on one or more Spans's.  This function
   adds a Spans to a miPaintedSet, being careful first to remove each from
   the miPaintedSet each pixel in the Spans, if it has previously been
   painted another color.  However, for efficiency it does not check
   whether a pixel in the Spans has been previously painted the same color.
   So while the drawing function is being called, the Spans in any one of
   the PaintedSet's SpanGroups may overlap.  But different SpanGroups do
   not overlap.  That is an invariant.

   After all calls to miAddSpansToPaintedSet() are completed, duplicate
   pixels are resolved by invoking miUniquifyPaintedSet().  That takes
   place in the API wrappers in mi_api.c, just before the drawing function
   returns.

   The function miCopyPaintedSetToCanvas(), in mi_canvas.c, can copy the
   contents of a miPaintedSet, i.e. its spans of painted miPoints, to a
   miCanvas structure.  Sophisticated pixel merging is supported.  It would
   be easy to write other functions that copy pixels out of a
   miPaintedSet. */

/* Original version written by Joel McCormack, Summer 1989.  
   Hacked by Robert S. Maier, 1998-1999. */

#include "sys-defines.h"
#include "extern.h"

#include "xmi.h"
#include "mi_spans.h"
#include "mi_api.h"

/* spans in a Spans are sorted by y, so these give ymin, ymax for a
   nonempty Spans */
#define YMIN(spans) (spans->points[0].y)
#define YMAX(spans) (spans->points[spans->count-1].y)

/* internal functions */
static SpanGroup * miNewSpanGroup (miPixel pixel);
static int miUniquifySpansX (const Spans *spans, miPoint *newPoints, unsigned int *newWidths);
static void miAddSpansToSpanGroup (const Spans *spans, SpanGroup *spanGroup);
static void miDeleteSpanGroup (SpanGroup *spanGroup);
static void miQuickSortSpansX (miPoint *points, unsigned int *widths, int numSpans);
static void miSubtractSpans (SpanGroup *spanGroup, const Spans *sub);
static void miUniquifySpanGroup (SpanGroup *spanGroup);



/* The following functions are the public functions of this module. */

miPaintedSet *
miNewPaintedSet (void)
{
  miPaintedSet *paintedSet;

  paintedSet = (miPaintedSet *)mi_xmalloc (sizeof(miPaintedSet));
  paintedSet->groups = (SpanGroup **)NULL; /* pointer-to-SpanGroup slots */
  paintedSet->size = 0;		/* slots allocated */
  paintedSet->ngroups = 0;	/* slots filled */

  return paintedSet;
}

/* Add a Spans to a miPaintedSet's SpanGroup for a specified pixel values,
   and also subtract it from the SpanGroups for all other pixel values. */
void
miAddSpansToPaintedSet (const Spans *spans, miPaintedSet *paintedSet, miPixel pixel)
{
  bool found = false;
  int i;
  SpanGroup *spanGroup;

  if (spans->count == 0)
    return;

  for (i = 0; i < paintedSet->ngroups; i++)
    {
      miPixel stored_pixel;
      
      stored_pixel = paintedSet->groups[i]->pixel;
      if (MI_SAME_PIXEL(pixel, stored_pixel))
	{
	  found = true;		/* have a spanGroup for this pixel value */
	  break;
	}
    }
  if (!found)
    {
      if (paintedSet->ngroups == paintedSet->size)
	/* expand array of SpanGroups */
	{
	  int old_size = paintedSet->size;
	  int new_size = 2 * (old_size + 8);
	  
	  if (old_size == 0)
	    paintedSet->groups = (SpanGroup **)
	      mi_xmalloc(new_size * sizeof(SpanGroup *));
	  else
	    paintedSet->groups = (SpanGroup **)
	      mi_xrealloc(paintedSet->groups, new_size * sizeof(SpanGroup *));
	  paintedSet->size = new_size;
	}

      /* create a SpanGroup for this pixel value */
      i = paintedSet->ngroups;
      paintedSet->groups[i] = miNewSpanGroup (pixel);
      paintedSet->ngroups++;
    }
  
  spanGroup = paintedSet->groups[i];
  miAddSpansToSpanGroup (spans, spanGroup);

  /* subtract Spans from all other SpanGroups */
  for (i = 0; i < paintedSet->ngroups; i++)
    {
      SpanGroup *otherGroup;
      
      otherGroup = paintedSet->groups[i];
      if (otherGroup == spanGroup)
	continue;
      miSubtractSpans (otherGroup, spans);
    }
}

/* Deallocate all of a miPaintedSet's SpanGroups, including the points and
   width arrays that are part of its component Spans's.  So it will
   effectively become the empty set, as if it had been newly created. */
void
miClearPaintedSet (miPaintedSet *paintedSet)
{
  int i;

  if (paintedSet == (miPaintedSet *)NULL)
    return;

  for (i = 0; i < paintedSet->ngroups; i++)
    miDeleteSpanGroup (paintedSet->groups[i]);
  if (paintedSet->size > 0)
    free (paintedSet->groups);
  paintedSet->size = 0;		/* slots allocated */
  paintedSet->ngroups = 0;	/* slots filled */
}

/* Deallocate a miPaintedSet, including the points and width arrays that
   are part of its component Spans's. */
void
miDeletePaintedSet (miPaintedSet *paintedSet)
{
  int i;

  if (paintedSet == (miPaintedSet *)NULL)
    return;

  for (i = 0; i < paintedSet->ngroups; i++)
    miDeleteSpanGroup (paintedSet->groups[i]);

  if (paintedSet->size > 0)
    free (paintedSet->groups);
  free (paintedSet);
}

/* `Uniquify' a miPaintedSet, i.e. uniquify each of its SpanGroups (see
   below). */
void
miUniquifyPaintedSet (miPaintedSet *paintedSet)
{
  int i;

  if (paintedSet == (miPaintedSet *)NULL)
    return;

  for (i = 0; i < paintedSet->ngroups; i++)
    {
      if (paintedSet->groups[i]->count > 0)
	{
	  miUniquifySpanGroup (paintedSet->groups[i]);
	}
    }
}


/* Create and initialize a SpanGroup, i.e. an unsorted list of Spans's. */
static SpanGroup *
miNewSpanGroup (miPixel pixel)
{
  SpanGroup *spanGroup;

  spanGroup = (SpanGroup *)mi_xmalloc (sizeof(SpanGroup));
  spanGroup->pixel = pixel;	/* pixel to be used */
  spanGroup->size = 0;		/* slots allocated */
  spanGroup->count = 0;		/* slots filled */
  spanGroup->group = (Spans *)NULL; /* slots for Spans's */
  spanGroup->ymin = INT_MAX;	/* min over slots */
  spanGroup->ymax = INT_MIN;	/* max over slots */

  return spanGroup;
}

/* Add a Spans to a SpanGroup, by tacking it on the end; update SpanGroup's
   ymin, ymax. */
static void
miAddSpansToSpanGroup (const Spans *spans, SpanGroup *spanGroup)
{
  int ymin, ymax;

  if (spans->count == 0)
    return;
  if (spanGroup->size == spanGroup->count) 
    /* expand SpanGroup */
    {
      spanGroup->size = (spanGroup->size + 8) * 2;
      spanGroup->group = (Spans *)
	mi_xrealloc(spanGroup->group, sizeof(Spans) * spanGroup->size);
    }
  
  /* tack Spans onto end of SpanGroup, update SpanGroup's ymin and ymax */
  spanGroup->group[spanGroup->count] = *spans;
  (spanGroup->count)++;
  ymin = YMIN(spans);
  if (ymin < spanGroup->ymin) 
    spanGroup->ymin = ymin;
  ymax = YMAX(spans);
  if (ymax > spanGroup->ymax) 
    spanGroup->ymax = ymax;
}

/* Delete a SpanGroup, including the point and width arrays that are part
   of each Spans. */
static void
miDeleteSpanGroup (SpanGroup *spanGroup)
{
  int i;

  if (spanGroup == (SpanGroup *)NULL)
    return;

  for (i = 0; i < spanGroup->count; i++)
    {
      /*  free spanGroup->group[i], which is a Spans */
      free (spanGroup->group[i].points);
      free (spanGroup->group[i].widths);
    }
  if (spanGroup->group)
    free (spanGroup->group);
  free (spanGroup);
}

/* Subtract a Spans from a SpanGroup, i.e. from each of its Spans's; update
   SpanGroup's ymin, ymax. */
static void 
miSubtractSpans (SpanGroup *spanGroup, const Spans *sub)
{
  int		i, subCount, spansCount;
  int		ymin, ymax, xmin, xmax;
  Spans		*spans;
  miPoint	*subPt, *spansPt;
  unsigned int	*subWid, *spansWid;
  int		extra;
  bool		gross_change = false;

  if (sub->count == 0)		/* nothing to do */
    return;

  /* y range of Spans to be subtracted */
  ymin = YMIN(sub);
  ymax = YMAX(sub);

  /* loop through all Spans's in SpanGroup */
  spans = spanGroup->group;
  for (i = spanGroup->count; i > 0; i--, spans++) 
    {
      if (spans->count == 0)
	continue;

      /* look only at Spans's with y ranges that overlap with `sub' */
      if (YMIN(spans) <= ymax && ymin <= YMAX(spans)) 
	{
	  /* count, start points, and widths for `sub' */
	  subCount = sub->count;
	  subPt = sub->points;
	  subWid = sub->widths;

	  /* count, start points, and widths for current Spans */
	  spansCount = spans->count;
	  spansPt = spans->points;
	  spansWid = spans->widths;

	  extra = 0;		/* extra span slots available in Spans */
	  for (;;)
	    /* look at pairs of spans, one from each Spans, that have the
	       same value for y (break out when one or the other Spans is
	       exhausted) */
	    {
	      while (spansCount && spansPt->y < subPt->y)
		{
		  spansPt++;  
		  spansWid++; 
		  spansCount--;
		}
	      if (!spansCount)
		break;
	      while (subCount && subPt->y < spansPt->y)
		{
		  subPt++;
		  subWid++;
		  subCount--;
		}
	      if (!subCount)
		break;

	      if (subPt->y == spansPt->y)
		/* the two spans are at same y value, analyse in detail */
		{
		  xmin = subPt->x;
		  xmax = xmin + (int)(*subWid); /* just right of sub span */
		  if (xmin >= spansPt->x + (int)(*spansWid)
		      || spansPt->x >= xmax)
		    /* non-overlapping, do nothing */
		    {
		      ;
		    }
		  else if (xmin <= spansPt->x)
		    /* span to be subtracted begins at the same point, or
                       to the left */
		    {
		      if (xmax >= spansPt->x + (int)(*spansWid))
			/* span to be subtracted ends at the same point,
			   or to the right; delete this span by downshifting */
			{
			  memmove (spansPt, spansPt + 1, 
				   sizeof(miPoint) * (spansCount - 1));
			  memmove (spansWid, spansWid + 1, 
				   sizeof(unsigned int) * (spansCount - 1));
			  spansPt--;
			  spansWid--;
			  spans->count--;
			  extra++;
			  gross_change = true; /* span vanished */
			}
		      else 
			/* span to be subtracted ends to the left of this
			   one's ending point; alter ending point and width */
			{
			  *spansWid = 
			    *spansWid - (unsigned int)(xmax - spansPt->x);
			  spansPt->x = xmax;
			}
		    }
		  else
		    /* span to be subtracted overlaps with this one, and
                        begins to the right of this one */
		    {
		      if (xmax >= spansPt->x + (int)*spansWid)
			/* span to be subtracted ends at the same point, or
			   to the right; just update width */
			*spansWid = (unsigned int)(xmin - spansPt->x);
		      else
			/* hard case: must split the span */
			{
#define EXTRA 8
			  if (extra == 0)
			    /* reallocate; create EXTRA new span slots */
			    {
			      miPoint *newPt;
			      unsigned int *newwid;

			      newPt = (miPoint *)mi_xrealloc (spans->points, 
				       (spans->count + EXTRA)*sizeof(miPoint));
			      spansPt = newPt + (spansPt - spans->points);
			      spans->points = newPt;
			      newwid = (unsigned int *)mi_xrealloc (spans->widths, 
			          (spans->count + EXTRA)*sizeof(unsigned int));
			      spansWid = newwid + (spansWid - spans->widths);
			      spans->widths = newwid;
			      extra = EXTRA;
			    }

			  /* downshift; create two new spans as replacement */
			  memmove (spansPt + 1, spansPt, 
				   sizeof(miPoint) * spansCount);
			  memmove (spansWid + 1, spansWid, 
				   sizeof(unsigned int) * spansCount);
			  spans->count++;
			  extra--;
			  /* first new span */
			  *spansWid = (unsigned int)(xmin - spansPt->x);
			  spansWid++;
			  spansPt++;
			  /* second new span */
			  *spansWid = *spansWid - (unsigned int)(xmax - spansPt->x);
			  spansPt->x = xmax;
			}
		    }
		} /* end of same-value-of-y computations */

	      /* on to next span in the Spans */
	      spansPt++;  
	      spansWid++; 
	      spansCount--;
	    }
	}
    }

  if (gross_change)
    /* at least one span vanished; recompute SpanGroup's ymin, ymax */
    {
      ymax = INT_MIN;
      ymin = INT_MAX;

      /* loop through all Spans's in SpanGroup */
      spans = spanGroup->group;
      for (i = spanGroup->count; i > 0; i--, spans++) 
	{
	  int ymin_spans, ymax_spans;

	  if (spans->count == 0)
	    continue;
	  ymin_spans = YMIN(spans);
	  ymax_spans = YMAX(spans);
	  if (ymin_spans < ymin)
	    ymin = ymin_spans;
	  if (ymax_spans > ymax)
	    ymax = ymax_spans;
	}

      spanGroup->ymin = ymin;
      spanGroup->ymax = ymax;      
    }
}

/* `Uniquify' a SpanGroup: merge all its Spans's into a single Spans, which
   will be sorted on x as well as on y. */
static void
miUniquifySpanGroup (SpanGroup *spanGroup)
{
  int    i;
  Spans  *spans;
  Spans  *yspans;
  int    *ysizes;
  int    ymin, ylength;

  /* the new single Spans */
  miPoint *points;
  unsigned int *widths;
  int count;

  if (spanGroup->count == 0) 
    return;

  /* Special case : ymin > ymax, so the Spans's in the SpanGroup, no matter
     how numerous, must be empty (and can't contain point or width arrays).  */
  if (spanGroup->ymin > spanGroup->ymax)
    {
      spanGroup->count = 0;
      return;
    }

  /* Yuck.  Gross.  Radix sort into y buckets, then sort x and uniquify */
  /* This seems to be the fastest thing to do.  I've tried sorting on
     both x and y at the same time rather than creating into all those
     y buckets, but it was somewhat slower. */
  
  ymin    = spanGroup->ymin;
  ylength = spanGroup->ymax - ymin + 1;
  
  /* allocate Spans's for y buckets (one Spans for every scanline);
     ysizes[] is number of allocated Span slots in each bucket */
  yspans = (Spans *)mi_xmalloc (ylength * sizeof(Spans));
  ysizes = (int *)mi_xmalloc (ylength * sizeof(int));
  for (i = 0; i < ylength; i++) 
    {
      ysizes[i]        = 0;
      yspans[i].count  = 0;
      yspans[i].points = (miPoint *)NULL;
      yspans[i].widths = (unsigned int *)NULL;
    }
  
  /* go through every single span and put it into the correct y bucket */
  count = 0;
  for (i = 0, spans = spanGroup->group; 
       i < spanGroup->count; i++, spans++) 
    {
      int j, index;
      
      for (j = 0, points = spans->points, widths = spans->widths;
	   j < spans->count; j++, points++, widths++) 
	{
	  index = points->y - ymin;
	  if (index >= 0 && index < ylength) /* paranoia */
	    {
	      Spans *newspans = &(yspans[index]);
	      
	      if (newspans->count == ysizes[index]) 
		/* expand bucket arrays by reallocating */
		{
		  ysizes[index] = (ysizes[index] + 8) * 2;
		  newspans->points
		    = (miPoint *)mi_xrealloc (newspans->points,
					      ysizes[index] * sizeof(miPoint));
		  newspans->widths
		    = (unsigned int *)mi_xrealloc (newspans->widths,
						   ysizes[index] * sizeof(unsigned int));
		}
	      newspans->points[newspans->count] = *points;
	      newspans->widths[newspans->count] = *widths;
	      (newspans->count)++;
	    } /* if y value of span in range */
	} /* for j through spans */
      
      count += spans->count;
    } /* for i through Spans */
  free (ysizes);
  
  /* now sort each bucket by x and uniquify it into new Spans */
  points = (miPoint *)mi_xmalloc (count * sizeof(miPoint));
  widths = (unsigned int *)mi_xmalloc (count * sizeof(unsigned int));
  count = 0;
  for (i = 0; i < ylength; i++) 
    {
      int ycount = yspans[i].count;
      
      if (ycount > 0) 
	{
	  if (ycount > 1) 
	    /* sort the >1 spans at this value of y */
	    {
	      miQuickSortSpansX (yspans[i].points, yspans[i].widths, ycount);
	      count += miUniquifySpansX
		(&(yspans[i]), &(points[count]), &(widths[count]));
	    } 
	  else 
	    /* just a single span at this value of y */
	    {
	      points[count] = yspans[i].points[0];
	      widths[count] = yspans[i].widths[0];
	      count++;
	    }
	  free (yspans[i].points);
	  free (yspans[i].widths);
	}
    }
  free (yspans);
  
  /* free SpanGroup's original Spans's, including Span arrays */
  for (i = 0; i < spanGroup->count; i++)
    {
      free (spanGroup->group[i].points);
      free (spanGroup->group[i].widths);
    }

  /* SpanGroup now has only a single Spans */
  spanGroup->count = 1;
  spanGroup->group[0].points = points;
  spanGroup->group[0].widths = widths;
  spanGroup->group[0].count = count;
}


/* Sort each span in a Spans by x.  Called only if numSpans > 1. */
static void 
miQuickSortSpansX (miPoint *points, unsigned int *widths, int numSpans)
{
  int	 x;
  int	 i, j, m;
  miPoint *r;

#define ExchangeSpans(a, b)				    \
  {							    \
    miPoint tpt;	     					    \
    unsigned int tw;					    \
								\
    tpt = points[a]; points[a] = points[b]; points[b] = tpt;    \
    tw = widths[a]; widths[a] = widths[b]; widths[b] = tw;  \
  }

  do 
    {
      if (numSpans < 9) 
	/* do insertion sort */
	{
	  int xprev;

	  xprev = points[0].x;
	  i = 1;
	  do 			/* while i != numSpans */
	    {
	      x = points[i].x;
	      if (xprev > x) 
		{
		  /* points[i] is out of order.  Move into proper location. */
		  miPoint tpt;
		  unsigned int tw;
		  int k;

		  for (j = 0; x >= points[j].x; j++) 
		    { 
		    }
		  tpt = points[i];
		  tw  = widths[i];
		  for (k = i; k != j; k--) 
		    {
		      points[k] = points[k-1];
		      widths[k] = widths[k-1];
		    }
		  points[j] = tpt;
		  widths[j] = tw;
		  x = points[i].x;
		} /* if out of order */
	      xprev = x;
	      i++;
	    } while (i != numSpans);

	  /* end of insertion sort */
	  return;
	}

      /* Choose partition element, stick in location 0 */
      m = numSpans / 2;
      if (points[m].x > points[0].x)
	ExchangeSpans(m, 0);
      if (points[m].x > points[numSpans-1].x) 
	ExchangeSpans(m, numSpans-1);
      if (points[m].x > points[0].x)
	ExchangeSpans(m, 0);
      x = points[0].x;

      /* Partition array */
      i = 0;
      j = numSpans;
      do 
	{
	  r = &(points[i]);
	  do 
	    {
	      r++;
	      i++;
	    } 
	  while (i != numSpans && r->x < x)
	    ;
	  r = &(points[j]);
	  do 
	    {
	      r--;
	      j--;
	    } 
	  while (x < r->x);
	  if (i < j) ExchangeSpans(i, j);
	} 
      while (i < j);

      /* Move partition element back to middle */
      ExchangeSpans(0, j);

      /* Recurse */
      if (numSpans-j-1 > 1)
	miQuickSortSpansX (&points[j+1], &widths[j+1], numSpans-j-1);
      numSpans = j;
    } while (numSpans > 1);
}

/* Sort an unordered list of spans by y, so that it becomes a Spans. */
void 
miQuickSortSpansY (miPoint *points, unsigned int *widths, int numSpans)
{
  int	 y;
  int	 i, j, m;
  miPoint *r;

  if (numSpans <= 1)		/* nothing to do */
    return;

#define ExchangeSpans(a, b)				    \
  {							    \
    miPoint tpt;	     					    \
    unsigned int tw;					    \
								\
    tpt = points[a]; points[a] = points[b]; points[b] = tpt;    \
    tw = widths[a]; widths[a] = widths[b]; widths[b] = tw;  \
  }

  do 
    {
      if (numSpans < 9) 
	/* do insertion sort */
	{
	  int yprev;

	  yprev = points[0].y;
	  i = 1;
	  do 			/* while i != numSpans */
	    {
	      y = points[i].y;
	      if (yprev > y) 
		{
		  /* points[i] is out of order.  Move into proper location. */
		  miPoint tpt;
		  unsigned int tw;
		  int k;

		  for (j = 0; y >= points[j].y; j++) 
		    { 
		    }
		  tpt = points[i];
		  tw  = widths[i];
		  for (k = i; k != j; k--) 
		    {
		      points[k] = points[k-1];
		      widths[k] = widths[k-1];
		    }
		  points[j] = tpt;
		  widths[j] = tw;
		  y = points[i].y;
		} /* if out of order */
	      yprev = y;
	      i++;
	    } while (i != numSpans);

	  /* end of insertion sort */
	  return;
	}

      /* Choose partition element, stick in location 0 */
      m = numSpans / 2;
      if (points[m].y > points[0].y)
	ExchangeSpans(m, 0);
      if (points[m].y > points[numSpans-1].y) 
	ExchangeSpans(m, numSpans-1);
      if (points[m].y > points[0].y)
	ExchangeSpans(m, 0);
      y = points[0].y;

      /* Partition array */
      i = 0;
      j = numSpans;
      do 
	{
	  r = &(points[i]);
	  do 
	    {
	      r++;
	      i++;
	    } 
	  while (i != numSpans && r->y < y)
	    ;
	  r = &(points[j]);
	  do 
	    {
	      r--;
	      j--;
	    } 
	  while (y < r->y);
	  if (i < j) ExchangeSpans(i, j);
	} 
      while (i < j);

      /* Move partition element back to middle */
      ExchangeSpans(0, j);

      /* Recurse */
      if (numSpans-j-1 > 1)
	miQuickSortSpansY (&points[j+1], &widths[j+1], numSpans-j-1);
      numSpans = j;
    } while (numSpans > 1);
}

/* Uniquify the spans in a Spans.  (Spans at each y value are assumed to
   have been sorted on x, perhaps by calling miQuickSortSpansX() above.)
   Also, create a new Spans: stash the uniquified spans into the previously
   allocated arrays newPoints and newWidths.  Returns the number of unique
   spans.  Called only if numSpans > 1. */
static int 
miUniquifySpansX (const Spans *spans, miPoint *newPoints, unsigned int *newWidths)
{
  int		newx1, newx2, oldpt, i, y;
  miPoint	*oldPoints;
  unsigned int	*oldWidths, *startNewWidths;

  startNewWidths = newWidths;
  oldPoints = spans->points;
  oldWidths = spans->widths;
  y = oldPoints->y;
  newx1 = oldPoints->x;
  newx2 = newx1 + (int)(*oldWidths);

  for (i = spans->count - 1; i > 0; i--) 
    {
      oldPoints++;
      oldWidths++;
      oldpt = oldPoints->x;
      if (oldpt > newx2) 
	{
	  /* write current span, start a new one */
	  newPoints->x = newx1;
	  newPoints->y = y;
	  *newWidths = (unsigned int)(newx2 - newx1);
	  newPoints++;
	  newWidths++;
	  newx1 = oldpt;
	  newx2 = oldpt + (int)(*oldWidths);
	} 
      else 
	{
	  /* extend current span, if old extends beyond new */
	  oldpt = oldpt + (int)(*oldWidths);
	  if (oldpt > newx2)
	    newx2 = oldpt;
	}
    }

  /* write final span */
  newPoints->x = newx1;
  *newWidths = (unsigned int)(newx2 - newx1);
  newPoints->y = y;

  return (int)((newWidths - startNewWidths) + 1);
}
