/* 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. */

/* Original author unknown (Bob Scheifler?).  
   Hacked by Robert S. Maier, 1998-1999. */

/* This module provides the function miFillArcs_internal(), which fills a
   poly-arc.  All painting goes through the low-level MI_PAINT_SPANS()
   macro. */

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

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

#define QUADRANT (90 * 64)
#define HALFCIRCLE (180 * 64)
#define QUADRANT3 (270 * 64)

/* trig functions, angle specified in 1/64 degrees */
#define Dsin(d)	sin((double)d*(M_PI/11520.0))
#define Dcos(d)	cos((double)d*(M_PI/11520.0))

/* internal functions that do painting of pixels */
static void miFillArcSliceD (miPaintedSet *paintedSet, const miGC *pGC, const miArc *arc);
static void miFillArcSliceI (miPaintedSet *paintedSet, const miGC *pGC, const miArc *arc);
static void miFillEllipseD (miPaintedSet *paintedSet, const miGC *pGC, const miArc *arc);
static void miFillEllipseI (miPaintedSet *paintedSet, const miGC *pGC, const miArc *arc);

/* internal functions that don't do painting of pixels */
static void miEllipseAngleToSlope (int angle, unsigned int width, unsigned int height, int *dxp, int *dyp, double *d_dxp, double *d_dyp);
static void miFillArcDSetup (const miArc *arc, miFillArcD *info);
static void miFillArcSetup (const miArc *arc, miFillArc *info);
static void miFillArcSliceSetup (const miGC *pGC, const miArc *arc, miArcSlice *slice);
static void miGetArcEdge (const miArc *arc, miSliceEdge *edge, int k, bool top, bool left);
static void miGetPieEdge (const miArc *arc, int angle, miSliceEdge *edge, bool top, bool left);


static void
miFillArcSetup (const miArc *arc, miFillArc *info)
{
  info->y = arc->height >> 1;
  info->dy = arc->height & 1;
  info->yorg = arc->y + info->y;
  info->dx = arc->width & 1;
  info->xorg = arc->x + (int)(arc->width >> 1) + info->dx;
  info->dx = 1 - info->dx;

  if (arc->width == arc->height) /* circular arc */
    {
      /* (2x - 2xorg)^2 = d^2 - (2y - 2yorg)^2 */
      /* even: xorg = yorg = 0   odd:  xorg = .5, yorg = -.5 */
      info->ym = 8;
      info->xm = 8;
      info->yk = info->y << 3;
      if (!info->dx)
	{
	  info->xk = 0;
	  info->e = -1;
	}
      else
	{
	  info->y++;
	  info->yk += 4;
	  info->xk = -4;
	  info->e = - (info->y << 3);
	}
    }
  else				/* non-circular arc */
    {
      /* h^2 * (2x - 2xorg)^2 = w^2 * h^2 - w^2 * (2y - 2yorg)^2 */
      /* even: xorg = yorg = 0   odd:  xorg = .5, yorg = -.5 */
      info->ym = (arc->width * arc->width) << 3;
      info->xm = (arc->height * arc->height) << 3;
      info->yk = info->y * info->ym;
      if (!info->dy)
	info->yk -= info->ym >> 1;
      if (!info->dx)
	{
	  info->xk = 0;
	  info->e = - (info->xm >> 3);
	}
      else
	{
	  info->y++;
	  info->yk += info->ym;
	  info->xk = -(info->xm >> 1);
	  info->e = info->xk - info->yk;
	}
    }
}

static void
miFillArcDSetup (const miArc *arc, miFillArcD *info)
{
  /* h^2 * (2x - 2xorg)^2 = w^2 * h^2 - w^2 * (2y - 2yorg)^2 */
  /* even: xorg = yorg = 0   odd:  xorg = .5, yorg = -.5 */
  info->y = arc->height >> 1;
  info->dy = arc->height & 1;
  info->yorg = arc->y + info->y;
  info->dx = arc->width & 1;
  info->xorg = arc->x + (int)(arc->width >> 1) + info->dx;
  info->dx = 1 - info->dx;
  info->ym = ((double)arc->width) * (arc->width * 8);
  info->xm = ((double)arc->height) * (arc->height * 8);
  info->yk = info->y * info->ym;
  if (!info->dy)
    info->yk -= (0.5 * info->ym);
  if (!info->dx)
    {
      info->xk = 0;
      info->e = - (0.125 * info->xm);
    }
  else
    {
      info->y++;
      info->yk += info->ym;
      info->xk = -0.5 * info->xm;
      info->e = info->xk - info->yk;
    }
}

static void
miGetArcEdge (const miArc *arc, miSliceEdge *edge, int k, bool top, bool left)
{
  int xady, y;

  y = arc->height >> 1;
  if ((arc->width & 1) == 0)
    y++;
  if (!top)
    {
      y = -y;
      if (arc->height & 1)
	y--;
    }
  xady = k + y * edge->dx;
  if (xady <= 0)
    edge->x = - ((-xady) / edge->dy + 1);
  else
    edge->x = (xady - 1) / edge->dy;
  edge->e = xady - edge->x * edge->dy;
  if ((top && (edge->dx < 0)) || (!top && (edge->dx > 0)))
    edge->e = edge->dy - edge->e + 1;
  if (left)
    edge->x++;
  edge->x += arc->x + (int)(arc->width >> 1);
  if (edge->dx > 0)
    {
      edge->deltax = 1;
      edge->stepx = edge->dx / edge->dy;
      edge->dx = edge->dx % edge->dy;
    }
  else
    {
      edge->deltax = -1;
      edge->stepx = - ((-edge->dx) / edge->dy);
      edge->dx = (-edge->dx) % edge->dy;
    }
  if (!top)
    {
      edge->deltax = -edge->deltax;
      edge->stepx = -edge->stepx;
    }
}

static void
miEllipseAngleToSlope (int angle, unsigned int width, unsigned int height, int *dxp, int *dyp, double *d_dxp, double *d_dyp)
{
  int     dx, dy;
  double  d_dx, d_dy, scale;
  bool    negative_dx, negative_dy;

  switch (angle) 
    {
    case 0:
      *dxp = -1;
      *dyp = 0;
      if (d_dxp) 
	{
	  *d_dxp = 0.5 * (int)width;
	  *d_dyp = 0;
	}
      break;
    case QUADRANT:
      *dxp = 0;
      *dyp = 1;
      if (d_dxp) 
	{
	  *d_dxp = 0;
	  *d_dyp = -0.5 * (int)height;
	}
      break;
    case HALFCIRCLE:
      *dxp = 1;
      *dyp = 0;
      if (d_dxp) 
	{
	  *d_dxp = -0.5 * (int)width;
	  *d_dyp = 0;
	}
      break;
    case QUADRANT3:
      *dxp = 0;
      *dyp = -1;
      if (d_dxp) 
	{
	  *d_dxp = 0;
	  *d_dyp = 0.5 * (int)height;
	}
      break;
    default:			/* angle is not 0, 90, 180, or 270 */
      d_dx = Dcos(angle) * (int)width;
      d_dy = Dsin(angle) * (int)height;
      if (d_dxp) 
	{
	  *d_dxp = 0.5 * d_dx;
	  *d_dyp = -0.5 * d_dy;
	}
      negative_dx = false;
      if (d_dx < 0.0)
	{
	  d_dx = -d_dx;
	  negative_dx = true;
	}
      negative_dy = false;
      if (d_dy < 0.0)
	{
	  d_dy = -d_dy;
	  negative_dy = true;
	}
      scale = d_dx;
      if (d_dy > d_dx)
	scale = d_dy;
      dx = IFLOOR((d_dx * 32768) / scale + 0.5);
      if (negative_dx)
	dx = -dx;
      *dxp = dx;
      dy = IFLOOR((d_dy * 32768) / scale + 0.5);
      if (negative_dy)
	dy = -dy;
      *dyp = dy;
      break;
    }
}

static void
miGetPieEdge (const miArc *arc, int angle, miSliceEdge *edge, bool top, bool left)
{
  int k;
  int dx, dy;
  /* why no signdx, signdy? */

  miEllipseAngleToSlope (angle, arc->width, arc->height, &dx, &dy, NULL, NULL);

  if (dy == 0)
    {
      edge->x = left ? INT_MIN : INT_MAX;
      edge->stepx = 0;
      edge->e = 0;
      edge->dx = -1;
      return;
    }
  if (dx == 0)
    {
      edge->x = arc->x + (int)(arc->width >> 1);
      if (left && (arc->width & 1))
	edge->x++;
      else if (!left && !(arc->width & 1))
	edge->x--;
      edge->stepx = 0;
      edge->e = 0;
      edge->dx = -1;
      return;
    }
  if (dy < 0) 
    {
      dx = -dx;
      dy = -dy;
    }
  k = (arc->height & 1) ? dx : 0;
  if (arc->width & 1)
    k += dy;
  edge->dx = dx << 1;
  edge->dy = dy << 1;
  miGetArcEdge (arc, edge, k, top, left);
}

static void
miFillArcSliceSetup (const miGC *pGC, const miArc *arc, miArcSlice *slice)
{
  int angle1, angle2;

  angle1 = arc->angle1;
  if (arc->angle2 < 0)
    {
      angle2 = angle1;
      angle1 += arc->angle2;
    }
  else
    angle2 = angle1 + arc->angle2;
  while (angle1 < 0)
    angle1 += FULLCIRCLE;
  while (angle1 >= FULLCIRCLE)
    angle1 -= FULLCIRCLE;
  while (angle2 < 0)
    angle2 += FULLCIRCLE;
  while (angle2 >= FULLCIRCLE)
    angle2 -= FULLCIRCLE;
  slice->min_top_y = 0;
  slice->max_top_y = arc->height >> 1;
  slice->min_bot_y = 1 - (arc->height & 1);
  slice->max_bot_y = slice->max_top_y - 1;
  slice->flip_top = false;
  slice->flip_bot = false;

  switch (pGC->arcMode)
    {
    case MI_ARC_CHORD:		/* chord filling, not pie slice */
    default:
      {
	double w2, h2, x1, y1, x2, y2, dx, dy, scale;
	int signdx, signdy, y, k;
	bool isInt1 = true, isInt2 = true;

	w2 = 0.5 * (double)arc->width;
	h2 = 0.5 * (double)arc->height;
	if ((angle1 == 0) || (angle1 == HALFCIRCLE))
	  {
	    x1 = angle1 ? -w2 : w2;
	    y1 = 0.0;
	  }
	else if ((angle1 == QUADRANT) || (angle1 == QUADRANT3))
	  {
	    x1 = 0.0;
	    y1 = (angle1 == QUADRANT) ? h2 : -h2;
	  }
	else
	  {
	    isInt1 = false;
	    x1 = Dcos(angle1) * w2;
	    y1 = Dsin(angle1) * h2;
	  }
	if ((angle2 == 0) || (angle2 == HALFCIRCLE))
	  {
	    x2 = angle2 ? -w2 : w2;
	    y2 = 0.0;
	  }
	else if ((angle2 == QUADRANT) || (angle2 == QUADRANT3))
	  {
	    x2 = 0.0;
	    y2 = (angle2 == QUADRANT) ? h2 : -h2;
	  }
	else
	  {
	    isInt2 = false;
	    x2 = Dcos(angle2) * w2;
	    y2 = Dsin(angle2) * h2;
	  }
	dx = x2 - x1;
	dy = y2 - y1;
	if (arc->height & 1)
	  {
	    y1 -= 0.5;
	    y2 -= 0.5;
	  }
	if (arc->width & 1)
	  {
	    x1 += 0.5;
	    x2 += 0.5;
	  }
	if (dy < 0.0)
	  {
	    dy = -dy;
	    signdy = -1;
	  }
	else
	  signdy = 1;
	if (dx < 0.0)
	  {
	    dx = -dx;
	    signdx = -1;
	  }
	else
	  signdx = 1;
	if (isInt1 && isInt2)
	  {
	    slice->edge1.dx = (int)(dx * 2);
	    slice->edge1.dy = (int)(dy * 2);
	  }
	else
	  {
	    scale = (dx > dy) ? dx : dy;
	    slice->edge1.dx = IFLOOR((dx * 32768) / scale + .5);
	    slice->edge1.dy = IFLOOR((dy * 32768) / scale + .5);
	  }
	if (!slice->edge1.dy)
	  {
	    if (signdx < 0)
	      {
		y = IFLOOR(y1 + 1.0);
		if (y >= 0)
		  {
		    slice->min_top_y = y;
		    slice->min_bot_y = arc->height;
		  }
		else
		  {
		    slice->max_bot_y = -y - (arc->height & 1);
		  }
	      }
	    else
	      {
		y = IFLOOR(y1);
		if (y >= 0)
		  slice->max_top_y = y;
		else
		  {
		    slice->min_top_y = arc->height;
		    slice->min_bot_y = -y - (arc->height & 1);
		  }
	      }
	    slice->edge1_top = true;
	    slice->edge1.x = INT_MAX;
	    slice->edge1.stepx = 0;
	    slice->edge1.e = 0;
	    slice->edge1.dx = -1;
	    slice->edge2 = slice->edge1;
	    slice->edge2_top = false;
	  }
	else if (!slice->edge1.dx)
	  {
	    if (signdy < 0)
	      x1 -= 1.0;
	    slice->edge1.x = ICEIL(x1);
	    slice->edge1_top = (signdy < 0 ? true : false);
	    slice->edge1.x += arc->x + (int)(arc->width >> 1);
	    slice->edge1.stepx = 0;
	    slice->edge1.e = 0;
	    slice->edge1.dx = -1;
	    slice->edge2_top = (slice->edge1_top ? false : true);
	    slice->edge2 = slice->edge1;
	  }
	else
	  {
	    if (signdx < 0)
	      slice->edge1.dx = -slice->edge1.dx;
	    if (signdy < 0)
	      slice->edge1.dx = -slice->edge1.dx;
	    k = ICEIL(((x1 + x2) * slice->edge1.dy - (y1 + y2) * slice->edge1.dx) / 2.0);
	    slice->edge2.dx = slice->edge1.dx;
	    slice->edge2.dy = slice->edge1.dy;
	    slice->edge1_top = (signdy < 0 ? true : false);
	    slice->edge2_top = (slice->edge1_top ? false : true);
	    miGetArcEdge(arc, &slice->edge1, k,
			 slice->edge1_top, (slice->edge1_top ? false : true));
	    miGetArcEdge(arc, &slice->edge2, k,
			 slice->edge2_top, slice->edge2_top);
	  }
      }
      break;

    case MI_ARC_PIE_SLICE:		/* pie slice filling, not chord */
      slice->edge1_top = (angle1 < HALFCIRCLE ? true : false);
      slice->edge2_top = (angle2 <= HALFCIRCLE ? true : false);
      if ((angle2 == 0) || (angle1 == HALFCIRCLE))
	{
	  if (angle2 ? slice->edge2_top : slice->edge1_top)
	    slice->min_top_y = slice->min_bot_y;
	  else
	    slice->min_top_y = arc->height;
	  slice->min_bot_y = 0;
	}
      else if ((angle1 == 0) || (angle2 == HALFCIRCLE))
	{
	  slice->min_top_y = slice->min_bot_y;
	  if (angle1 ? slice->edge1_top : slice->edge2_top)
	    slice->min_bot_y = (int)arc->height;
	  else
	    slice->min_bot_y = 0;
	}
      else if (slice->edge1_top == slice->edge2_top)
	{
	  if (angle2 < angle1)
	    {
	      slice->flip_top = slice->edge1_top;
	      slice->flip_bot = (slice->edge1_top ? false : true);
	    }
	  else if (slice->edge1_top)
	    {
	      slice->min_top_y = 1;
	      slice->min_bot_y = (int)arc->height;
	    }
	  else
	    {
	      slice->min_bot_y = 0;
	      slice->min_top_y = (int)arc->height;
	    }
	}
      miGetPieEdge(arc, angle1, &slice->edge1,
		   slice->edge1_top, (slice->edge1_top ? false : true));
      miGetPieEdge(arc, angle2, &slice->edge2,
		   slice->edge2_top, slice->edge2_top);
      break;
    }
}

#define ADDSPANS(pts_upper, pts_lower, wids_upper, wids_lower, x, y, slw, e, xk, xorg, yorg, dy) \
  pts_upper->x = xorg - x; \
  pts_upper->y = yorg - y; \
  *wids_upper = (unsigned int)slw; \
  pts_upper++; \
  wids_upper++; \
  if (MIFILLARCLOWER(e, xk, y, dy, slw)) \
  { \
    pts_lower->x = xorg - x; \
    pts_lower->y = yorg + y + dy; \
    pts_lower--; \
    *wids_lower-- = (unsigned int)slw; \
  }

static void
miFillEllipseI (miPaintedSet *paintedSet, const miGC *pGC, const miArc *arc)
{
  int x, y, e;
  int yk, xk, ym, xm, dx, dy, xorg, yorg;
  int slw;
  miFillArc info;
  miPoint *points_upper, *points_lower, *pts_upper, *pts_lower;
  unsigned int *widths_upper, *widths_lower, *wids_upper, *wids_lower;
  int numUpperSpans, numLowerSpans;

  points_upper = (miPoint *)mi_xmalloc (sizeof(miPoint) * arc->height);
  widths_upper = (unsigned int *)mi_xmalloc (sizeof(unsigned int) * arc->height);
  pts_upper = points_upper;
  wids_upper = widths_upper;
  points_lower = (miPoint *)mi_xmalloc (sizeof(miPoint) * arc->height);
  widths_lower = (unsigned int *)mi_xmalloc (sizeof(unsigned int) * arc->height);
  pts_lower = points_lower + (arc->height - 1);	/* begin at end, will decr */
  wids_lower = widths_lower + (arc->height - 1);

  miFillArcSetup(arc, &info);
  MIFILLARCSETUP(info, x, y, e, xk, xm, yk, ym, dx, dy, xorg, yorg);
  while (y > 0)
    /* add an upper and maybe a lower span (resp. growing downward, upward) */
    {
      MIFILLARCSTEP(x, y, e, xk, xm, yk, ym, dx, slw); /* y-- */
      ADDSPANS(pts_upper, pts_lower, wids_upper, wids_lower, x, y, slw, e, xk, xorg, yorg, dy);
    }

  numUpperSpans = pts_upper - points_upper;
  numLowerSpans = points_lower + (arc->height - 1) - pts_lower;
  if (numUpperSpans > 0)
    MI_PAINT_SPANS(paintedSet, pGC->pixels[1], numUpperSpans, points_upper, widths_upper)
  if (numLowerSpans > 0)
    MI_COPY_AND_PAINT_SPANS(paintedSet, pGC->pixels[1], numLowerSpans, pts_lower + 1, wids_lower + 1)
  free (points_lower);
  free (widths_lower);
}

static void
miFillEllipseD (miPaintedSet *paintedSet, const miGC *pGC, const miArc *arc)
{
  int x, y;
  int xorg, yorg, dx, dy, slw;
  double e, yk, xk, ym, xm;
  miFillArcD info;
  miPoint *points_upper, *points_lower, *pts_upper, *pts_lower;
  unsigned int *widths_upper, *widths_lower, *wids_upper, *wids_lower;
  int numUpperSpans, numLowerSpans;

  points_upper = (miPoint *)mi_xmalloc (sizeof(miPoint) * arc->height);
  widths_upper = (unsigned int *)mi_xmalloc (sizeof(unsigned int) * arc->height);
  pts_upper = points_upper;
  wids_upper = widths_upper;
  points_lower = (miPoint *)mi_xmalloc (sizeof(miPoint) * arc->height);
  widths_lower = (unsigned int *)mi_xmalloc (sizeof(unsigned int) * arc->height);
  pts_lower = points_lower + (arc->height - 1);	/* begin at end, will decr */
  wids_lower = widths_lower + (arc->height - 1);

  miFillArcDSetup(arc, &info);
  MIFILLARCSETUP(info, x, y, e, xk, xm, yk, ym, dx, dy, xorg, yorg);
  while (y > 0)
    /* add an upper and maybe a lower span (resp. growing downward, upward) */
    {
      MIFILLARCSTEP(x, y, e, xk, xm, yk, ym, dx, slw); /* y-- */
      ADDSPANS(pts_upper, pts_lower, wids_upper, wids_lower, x, y, slw, e, xk, xorg, yorg, dy);
    }

  numUpperSpans = pts_upper - points_upper;
  numLowerSpans = points_lower + (arc->height - 1) - pts_lower;
  if (numUpperSpans > 0)
    MI_PAINT_SPANS(paintedSet, pGC->pixels[1], numUpperSpans, points_upper, widths_upper)
  if (numLowerSpans > 0)
    MI_COPY_AND_PAINT_SPANS(paintedSet, pGC->pixels[1], numLowerSpans, pts_lower + 1, wids_lower + 1)
  free (points_lower);
  free (widths_lower);
}

#define ADDSPAN(pts, wids, lower, l, r, ya) \
if (r >= l) \
{ \
    pts->x = l; \
    pts->y = ya; \
    *wids = (unsigned int)(r - l + 1); \
    if (lower) \
      { pts--; wids--; } \
    else \
      { pts++; wids++; } \
}

#define ADDSLICESPANS(pts, wids, lower, xl, xr, xorg, ya, flip) \
if (!flip) \
{ \
    ADDSPAN(pts, wids, lower, xl, xr, ya) \
} \
else \
{ \
    int xc; \
    xc = xorg - x; \
    ADDSPAN(pts, wids, lower, xc, xr, ya) \
    xc += (slw - 1); \
    ADDSPAN(pts, wids, lower, xl, xc, ya) \
}

static void
miFillArcSliceI (miPaintedSet *paintedSet, const miGC *pGC, const miArc *arc)
{
  int yk, xk, ym, xm, dx, dy, xorg, yorg, slw, orig_slw;
  int x, y, e;
  miFillArc info;
  miArcSlice slice;
  int ya, xl, xr;
  miPoint *points_upper, *points_lower, *pts_upper, *pts_lower;
  unsigned int *widths_upper, *widths_lower, *wids_upper, *wids_lower;
  int numUpperSpans, numLowerSpans;

  miFillArcSetup (arc, &info);
  miFillArcSliceSetup (pGC, arc, &slice);
  MIFILLARCSETUP(info, x, y, e, xk, xm, yk, ym, dx, dy, xorg, yorg);
  slw = (int)arc->height;
  if (slice.flip_top || slice.flip_bot)
    slw += (int)(arc->height >> 1) + 1;
  orig_slw = slw;

  points_upper = (miPoint *)mi_xmalloc (sizeof(miPoint) * slw);
  widths_upper = (unsigned int *)mi_xmalloc (sizeof(unsigned int) * slw);
  pts_upper = points_upper;
  wids_upper = widths_upper;
  points_lower = (miPoint *)mi_xmalloc (sizeof(miPoint) * slw);
  widths_lower = (unsigned int *)mi_xmalloc (sizeof(unsigned int) * slw);
  pts_lower = points_lower + (slw - 1);	/* begin at end, will decrement */
  wids_lower = widths_lower + (slw - 1);

  while (y > 0)
    {
      MIFILLARCSTEP(x, y, e, xk, xm, yk, ym, dx, slw); /* y-- */
      MIARCSLICESTEP(slice.edge1);
      MIARCSLICESTEP(slice.edge2);
      if (MIFILLSLICEUPPER(y, slice))
	/* add an `upper' span (growing downward) */
	{
	  bool lower = false;

	  ya = yorg - y;
	  MIARCSLICEUPPER(xl, xr, slice, slw)
	  ADDSLICESPANS(pts_upper, wids_upper, lower, xl, xr, xorg, ya, slice.flip_top)
	}
      if (MIFILLSLICELOWER(y, slice))
	/* add a `lower' span (growing upward) */
	{
	  bool lower = true;

	  ya = yorg + y + dy;
	  MIARCSLICELOWER(xl, xr, slice, slw)
	  ADDSLICESPANS(pts_lower, wids_lower, lower, xl, xr, xorg, ya, slice.flip_bot)
	}
    }

  numUpperSpans = pts_upper - points_upper;
  numLowerSpans = points_lower + (orig_slw - 1) - pts_lower;
  if (numUpperSpans > 0)
    MI_PAINT_SPANS(paintedSet, pGC->pixels[1], numUpperSpans, points_upper, widths_upper)
  if (numLowerSpans > 0)
    MI_COPY_AND_PAINT_SPANS(paintedSet, pGC->pixels[1], numLowerSpans, pts_lower + 1, wids_lower + 1)
  free (points_lower);
  free (widths_lower);
}

static void
miFillArcSliceD (miPaintedSet *paintedSet, const miGC *pGC, const miArc *arc)
{
  int x, y;
  int dx, dy, xorg, yorg, slw, orig_slw;
  double e, yk, xk, ym, xm;
  miFillArcD info;
  miArcSlice slice;
  int ya, xl, xr;
  miPoint *points_upper, *points_lower, *pts_upper, *pts_lower;
  unsigned int *widths_upper, *widths_lower, *wids_upper, *wids_lower;
  int numUpperSpans, numLowerSpans;

  miFillArcDSetup (arc, &info);
  miFillArcSliceSetup (pGC, arc, &slice);
  MIFILLARCSETUP(info, x, y, e, xk, xm, yk, ym, dx, dy, xorg, yorg);
  slw = (int)arc->height;
  if (slice.flip_top || slice.flip_bot)
    slw += (int)(arc->height >> 1) + 1;
  orig_slw = slw;

  points_upper = (miPoint *)mi_xmalloc (sizeof(miPoint) * slw);
  widths_upper = (unsigned int *)mi_xmalloc (sizeof(unsigned int) * slw);
  pts_upper = points_upper;
  wids_upper = widths_upper;
  points_lower = (miPoint *)mi_xmalloc (sizeof(miPoint) * slw);
  widths_lower = (unsigned int *)mi_xmalloc (sizeof(unsigned int) * slw);
  pts_lower = points_lower + (slw - 1);	/* begin at end, will decrement */
  wids_lower = widths_lower + (slw - 1);

  while (y > 0)
    {
      MIFILLARCSTEP(x, y, e, xk, xm, yk, ym, dx, slw); /* y-- */
      MIARCSLICESTEP(slice.edge1);
      MIARCSLICESTEP(slice.edge2);
      if (MIFILLSLICEUPPER(y, slice))
	/* add an `upper' span (growing downward) */
	{
	  bool lower = false;

	  ya = yorg - y;
	  MIARCSLICEUPPER(xl, xr, slice, slw)
	  ADDSLICESPANS(pts_upper, wids_upper, lower, xl, xr, xorg, ya, slice.flip_top)
	}
      if (MIFILLSLICELOWER(y, slice))
	/* add a `lower' span (growing upward) */
	{
	  bool lower = true;

	  ya = yorg + y + dy;
	  MIARCSLICELOWER(xl, xr, slice, slw)
	  ADDSLICESPANS(pts_lower, wids_lower, lower, xl, xr, xorg, ya, slice.flip_bot)
	}
    }

  numUpperSpans = pts_upper - points_upper;
  numLowerSpans = points_lower + (orig_slw - 1) - pts_lower;
  if (numUpperSpans > 0)
    MI_PAINT_SPANS(paintedSet, pGC->pixels[1], numUpperSpans, points_upper, widths_upper)
  if (numLowerSpans > 0)
    MI_COPY_AND_PAINT_SPANS(paintedSet, pGC->pixels[1], numLowerSpans, pts_lower + 1, wids_lower + 1)
  free (points_lower);
  free (widths_lower);
}

void
miFillArcs_internal (miPaintedSet *paintedSet, const miGC *pGC, int narcs, const miArc *parcs)
{
  int i;
  const miArc *arc;

  for (i = narcs, arc = parcs; --i >= 0; arc++)
    {
      if (MI_FILLED_ARC_IS_EMPTY(arc))
	continue;

      if ((arc->angle2 >= FULLCIRCLE) || (arc->angle2 <= -FULLCIRCLE))
	{
	  if (MI_CAN_FILL_ARC(arc))
	    /* circle; or width, height <= 800 */
	    miFillEllipseI (paintedSet, pGC, arc);
	  else
	    miFillEllipseD (paintedSet, pGC, arc);
	}
      else
	{
	  if (MI_CAN_FILL_ARC(arc))
	    /* circle; or width, height <= 800 */
	    miFillArcSliceI (paintedSet, pGC, arc);
	  else
	    miFillArcSliceD (paintedSet, pGC, arc);
	}
    }
}
