/*
 * Copyright © 1998 Keith Packard
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Keith Packard not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Keith Packard makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file defines functions for drawing some primitives using
 * underlying datatypes instead of masks
 */

#define isClipped(c,ul,lr)  ((((c) - (ul)) | ((lr) - (c))) & 0x80008000)

#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

#ifdef BITSMUL
#define MUL BITSMUL
#else
#define MUL 1
#endif

#ifdef BITSSTORE
#define STORE(b,x)  BITSSTORE(b,x)
#else
#define STORE(b,x)  WRITE((b), (x))
#endif

#ifdef BITSRROP
#define RROP(b,a,x)	BITSRROP(b,a,x)
#else
#define RROP(b,a,x)	WRITE((b), FbDoRRop (READ(b), (a), (x)))
#endif

#ifdef BITSUNIT
#define UNIT BITSUNIT
#define USE_SOLID
#else
#define UNIT BITS
#endif

/*
 * Define the following before including this file:
 *
 *  BRESSOLID	name of function for drawing a solid segment
 *  BRESDASH	name of function for drawing a dashed segment
 *  DOTS	name of function for drawing dots
 *  ARC		name of function for drawing a solid arc
 *  BITS	type of underlying unit
 */

#ifdef BRESSOLID
void
BRESSOLID (DrawablePtr	pDrawable,
	   GCPtr	pGC,
	   int		dashOffset,
	   int		signdx,
	   int		signdy,
	   int		axis,
	   int		x1,
	   int		y1,
	   int		e,
	   int		e1,
	   int		e3,
	   int		len)
{
    FbBits	*dst;
    FbStride	dstStride;
    int		dstBpp;
    int		dstXoff, dstYoff;
    FbGCPrivPtr	pPriv = fbGetGCPrivate (pGC);
    UNIT	*bits;
    FbStride	bitsStride;
    FbStride	majorStep, minorStep;
    BITS	xor = (BITS) pPriv->xor;
    
    fbGetDrawable (pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
    bits = ((UNIT *) (dst + ((y1 + dstYoff) * dstStride))) + (x1 + dstXoff) * MUL;
    bitsStride = dstStride * (sizeof (FbBits) / sizeof (UNIT));
    if (signdy < 0)
	bitsStride = -bitsStride;
    if (axis == X_AXIS)
    {
	majorStep = signdx * MUL;
	minorStep = bitsStride;
    }
    else
    {
	majorStep = bitsStride;
	minorStep = signdx * MUL;
    }
    while (len--)
    {
	STORE(bits,xor);
	bits += majorStep;
	e += e1;
	if (e >= 0)
	{
	    bits += minorStep;
	    e += e3;
	}
    }

    fbFinishAccess (pDrawable);
}
#endif

#ifdef BRESDASH
void
BRESDASH (DrawablePtr	pDrawable,
	  GCPtr		pGC,
	  int		dashOffset,
	  int		signdx,
	  int		signdy,
	  int		axis,
	  int		x1,
	  int		y1,
	  int		e,
	  int		e1,
	  int		e3,
	  int		len)
{
    FbBits	*dst;
    FbStride	dstStride;
    int		dstBpp;
    int		dstXoff, dstYoff;
    FbGCPrivPtr	pPriv = fbGetGCPrivate (pGC);
    UNIT	*bits;
    FbStride	bitsStride;
    FbStride	majorStep, minorStep;
    BITS	xorfg, xorbg;
    FbDashDeclare;
    int		dashlen;
    Bool	even;
    Bool	doOdd;
    
    fbGetDrawable (pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
    doOdd = pGC->lineStyle == LineDoubleDash;
    xorfg = (BITS) pPriv->xor;
    xorbg = (BITS) pPriv->bgxor;
    
    FbDashInit (pGC, pPriv, dashOffset, dashlen, even);
    
    bits = ((UNIT *) (dst + ((y1 + dstYoff) * dstStride))) + (x1 + dstXoff) * MUL;
    bitsStride = dstStride * (sizeof (FbBits) / sizeof (UNIT));
    if (signdy < 0)
	bitsStride = -bitsStride;
    if (axis == X_AXIS)
    {
	majorStep = signdx * MUL;
	minorStep = bitsStride;
    }
    else
    {
	majorStep = bitsStride;
	minorStep = signdx * MUL;
    }
    if (dashlen >= len)
	dashlen = len;
    if (doOdd)
    {
	if (!even)
	    goto doubleOdd;
	for (;;)
	{
	    len -= dashlen;
	    while (dashlen--)
	    {
		STORE(bits,xorfg);
		bits += majorStep;
		if ((e += e1) >= 0)
		{
		    e += e3;
		    bits += minorStep;
		}
	    }
	    if (!len)
		break;
	    
	    FbDashNextEven(dashlen);
	    
	    if (dashlen >= len)
		dashlen = len;
doubleOdd:
	    len -= dashlen;
	    while (dashlen--)
	    {
		STORE(bits,xorbg);
		bits += majorStep;
		if ((e += e1) >= 0)
		{
		    e += e3;
		    bits += minorStep;
		}
	    }
	    if (!len)
		break;
	    
	    FbDashNextOdd(dashlen);
	    
	    if (dashlen >= len)
		dashlen = len;
	}
    }
    else
    {
	if (!even)
	    goto onOffOdd;
	for (;;)
	{
	    len -= dashlen;
	    while (dashlen--)
	    {
		STORE(bits,xorfg);
		bits += majorStep;
		if ((e += e1) >= 0)
		{
		    e += e3;
		    bits += minorStep;
		}
	    }
	    if (!len)
		break;

	    FbDashNextEven (dashlen);
	    
	    if (dashlen >= len)
		dashlen = len;
onOffOdd:
	    len -= dashlen;
	    while (dashlen--)
	    {
		bits += majorStep;
		if ((e += e1) >= 0)
		{
		    e += e3;
		    bits += minorStep;
		}
	    }
	    if (!len)
		break;
	    
	    FbDashNextOdd (dashlen);
	    
	    if (dashlen >= len)
		dashlen = len;
	}
    }

    fbFinishAccess (pDrawable);
}
#endif

#ifdef DOTS
void
DOTS (FbBits	    *dst,
      FbStride	    dstStride,
      int	    dstBpp,
      BoxPtr	    pBox,
      xPoint	    *ptsOrig,
      int	    npt,
      int	    xorg,
      int	    yorg,
      int	    xoff,
      int	    yoff,
      FbBits	    and,
      FbBits	    xor)
{
    INT32    	*pts = (INT32 *) ptsOrig;
    UNIT	*bits = (UNIT *) dst;
    UNIT	*point;
    BITS	bxor = (BITS) xor;
    BITS	band = (BITS) and;
    FbStride	bitsStride = dstStride * (sizeof (FbBits) / sizeof (UNIT));
    INT32    	ul, lr;
    INT32    	pt;

    ul = coordToInt(pBox->x1 - xorg,     pBox->y1 - yorg);
    lr = coordToInt(pBox->x2 - xorg - 1, pBox->y2 - yorg - 1);

    bits += bitsStride * (yorg + yoff) + (xorg + xoff) * MUL;
    
    if (and == 0)
    {
	while (npt--)
	{
	    pt = *pts++;
	    if (!isClipped(pt,ul,lr))
	    {
		point = bits + intToY(pt) * bitsStride + intToX(pt) * MUL;
		STORE(point,bxor);
	    }
	}
    }
    else
    {
	while (npt--)
	{
	    pt = *pts++;
	    if (!isClipped(pt,ul,lr))
	    {
		point = bits + intToY(pt) * bitsStride + intToX(pt) * MUL;
		RROP(point,band,bxor);
	    }
	}
    }
}
#endif

#ifdef ARC

#define ARCCOPY(d)  STORE(d,xorBits)
#define ARCRROP(d)  RROP(d,andBits,xorBits)

void
ARC (FbBits	*dst,
     FbStride	dstStride,
     int	dstBpp,
     xArc	*arc,
     int	drawX,
     int	drawY,
     FbBits	and,
     FbBits	xor)
{
    UNIT	    *bits;
    FbStride	    bitsStride;
    miZeroArcRec    info;
    Bool	    do360;
    int		    x;
    UNIT	    *yorgp, *yorgop;
    BITS	    andBits, xorBits;
    int		    yoffset, dyoffset;
    int		    y, a, b, d, mask;
    int		    k1, k3, dx, dy;
    
    bits = (UNIT *) dst;
    bitsStride = dstStride * (sizeof (FbBits) / sizeof (UNIT));
    andBits = (BITS) and;
    xorBits = (BITS) xor;
    do360 = miZeroArcSetup(arc, &info, TRUE);
    yorgp = bits + ((info.yorg + drawY) * bitsStride);
    yorgop = bits + ((info.yorgo + drawY) * bitsStride);
    info.xorg = (info.xorg + drawX) * MUL;
    info.xorgo = (info.xorgo + drawX) * MUL;
    MIARCSETUP();
    yoffset = y ? bitsStride : 0;
    dyoffset = 0;
    mask = info.initialMask;
    
    if (!(arc->width & 1))
    {
	if (andBits == 0)
	{
	    if (mask & 2)
		ARCCOPY(yorgp + info.xorgo);
	    if (mask & 8)
		ARCCOPY(yorgop + info.xorgo);
	}
	else
	{
	    if (mask & 2)
		ARCRROP(yorgp + info.xorgo);
	    if (mask & 8)
		ARCRROP(yorgop + info.xorgo);
	}
    }
    if (!info.end.x || !info.end.y)
    {
	mask = info.end.mask;
	info.end = info.altend;
    }
    if (do360 && (arc->width == arc->height) && !(arc->width & 1))
    {
	int xoffset = bitsStride;
	UNIT *yorghb = yorgp + (info.h * bitsStride) + info.xorg;
	UNIT *yorgohb = yorghb - info.h * MUL;

	yorgp += info.xorg;
	yorgop += info.xorg;
	yorghb += info.h * MUL;
	while (1)
	{
	    if (andBits == 0)
	    {
		ARCCOPY(yorgp + yoffset + x * MUL);
		ARCCOPY(yorgp + yoffset - x * MUL);
		ARCCOPY(yorgop - yoffset - x * MUL);
		ARCCOPY(yorgop - yoffset + x * MUL);
	    }
	    else
	    {
		ARCRROP(yorgp + yoffset + x * MUL);
		ARCRROP(yorgp + yoffset - x * MUL);
		ARCRROP(yorgop - yoffset - x * MUL);
		ARCRROP(yorgop - yoffset + x * MUL);
	    }
	    if (a < 0)
		break;
	    if (andBits == 0)
	    {
		ARCCOPY(yorghb - xoffset - y * MUL);
		ARCCOPY(yorgohb - xoffset + y * MUL);
		ARCCOPY(yorgohb + xoffset + y * MUL);
		ARCCOPY(yorghb + xoffset - y * MUL);
	    }
	    else
	    {
		ARCRROP(yorghb - xoffset - y * MUL);
		ARCRROP(yorgohb - xoffset + y * MUL);
		ARCRROP(yorgohb + xoffset + y * MUL);
		ARCRROP(yorghb + xoffset - y * MUL);
	    }
	    xoffset += bitsStride;
	    MIARCCIRCLESTEP(yoffset += bitsStride;);
	}
	yorgp -= info.xorg;
	yorgop -= info.xorg;
	x = info.w;
	yoffset = info.h * bitsStride;
    }
    else if (do360)
    {
	while (y < info.h || x < info.w)
	{
	    MIARCOCTANTSHIFT(dyoffset = bitsStride;);
	    if (andBits == 0)
	    {
		ARCCOPY(yorgp + yoffset + info.xorg + x * MUL);
		ARCCOPY(yorgp + yoffset + info.xorgo - x * MUL);
		ARCCOPY(yorgop - yoffset + info.xorgo - x * MUL);
		ARCCOPY(yorgop - yoffset + info.xorg + x * MUL);
	    }
	    else
	    {
		ARCRROP(yorgp + yoffset + info.xorg + x * MUL);
		ARCRROP(yorgp + yoffset + info.xorgo - x * MUL);
		ARCRROP(yorgop - yoffset + info.xorgo - x * MUL);
		ARCRROP(yorgop - yoffset + info.xorg + x * MUL);
	    }
	    MIARCSTEP(yoffset += dyoffset;, yoffset += bitsStride;);
	}
    }
    else
    {
	while (y < info.h || x < info.w)
	{
	    MIARCOCTANTSHIFT(dyoffset = bitsStride;);
	    if ((x == info.start.x) || (y == info.start.y))
	    {
		mask = info.start.mask;
		info.start = info.altstart;
	    }
	    if (andBits == 0)
	    {
		if (mask & 1)
		    ARCCOPY(yorgp + yoffset + info.xorg + x * MUL);
		if (mask & 2)
		    ARCCOPY(yorgp + yoffset + info.xorgo - x * MUL);
		if (mask & 4)
		    ARCCOPY(yorgop - yoffset + info.xorgo - x * MUL);
		if (mask & 8)
		    ARCCOPY(yorgop - yoffset + info.xorg + x * MUL);
	    }
	    else
	    {
		if (mask & 1)
		    ARCRROP(yorgp + yoffset + info.xorg + x * MUL);
		if (mask & 2)
		    ARCRROP(yorgp + yoffset + info.xorgo - x * MUL);
		if (mask & 4)
		    ARCRROP(yorgop - yoffset + info.xorgo - x * MUL);
		if (mask & 8)
		    ARCRROP(yorgop - yoffset + info.xorg + x * MUL);
	    }
	    if ((x == info.end.x) || (y == info.end.y))
	    {
		mask = info.end.mask;
		info.end = info.altend;
	    }
	    MIARCSTEP(yoffset += dyoffset;, yoffset += bitsStride;);
	}
    }
    if ((x == info.start.x) || (y == info.start.y))
	mask = info.start.mask;
    if (andBits == 0)
    {
	if (mask & 1)
	    ARCCOPY(yorgp + yoffset + info.xorg + x * MUL);
	if (mask & 4)
	    ARCCOPY(yorgop - yoffset + info.xorgo - x * MUL);
	if (arc->height & 1)
	{
	    if (mask & 2)
		ARCCOPY(yorgp + yoffset + info.xorgo - x * MUL);
	    if (mask & 8)
		ARCCOPY(yorgop - yoffset + info.xorg + x * MUL);
	}
    }
    else
    {
	if (mask & 1)
	    ARCRROP(yorgp + yoffset + info.xorg + x * MUL);
	if (mask & 4)
	    ARCRROP(yorgop - yoffset + info.xorgo - x * MUL);
	if (arc->height & 1)
	{
	    if (mask & 2)
		ARCRROP(yorgp + yoffset + info.xorgo - x * MUL);
	    if (mask & 8)
		ARCRROP(yorgop - yoffset + info.xorg + x * MUL);
	}
    }
}
#undef ARCCOPY
#undef ARCRROP
#endif

#ifdef GLYPH
#if BITMAP_BIT_ORDER == LSBFirst
# define WRITE_ADDR1(n)	    (n)
# define WRITE_ADDR2(n)	    (n)
# define WRITE_ADDR4(n)	    (n)
#else
# define WRITE_ADDR1(n)	    ((n) ^ 3)
# define WRITE_ADDR2(n)	    ((n) ^ 2)
# define WRITE_ADDR4(n)	    ((n))
#endif

#define WRITE1(d,n,fg)	    WRITE(d + WRITE_ADDR1(n), (BITS) (fg))

#ifdef BITS2
# define WRITE2(d,n,fg)	    WRITE((BITS2 *) &((d)[WRITE_ADDR2(n)]), (BITS2) (fg))
#else
# define WRITE2(d,n,fg)	    (WRITE1(d,n,fg), WRITE1(d,(n)+1,fg))
#endif

#ifdef BITS4
# define WRITE4(d,n,fg)	    WRITE((BITS4 *) &((d)[WRITE_ADDR4(n)]), (BITS4) (fg))
#else
# define WRITE4(d,n,fg)	    (WRITE2(d,n,fg), WRITE2(d,(n)+2,fg))
#endif

void
GLYPH (FbBits	*dstBits,
   FbStride	dstStride,
   int	dstBpp,
   FbStip	*stipple,
   FbBits	fg,
   int	x,
   int	height)
{
    int	    lshift;
    FbStip  bits;
    BITS    *dstLine;
    BITS    *dst;
    int	    n;
    int	    shift;

    dstLine = (BITS *) dstBits;
    dstLine += x & ~3;
    dstStride *= (sizeof (FbBits) / sizeof (BITS));
    shift = x & 3;
    lshift = 4 - shift;
    while (height--)
    {
	bits = *stipple++;
	dst = (BITS *) dstLine;
	n = lshift;
	while (bits)
	{
	    switch (FbStipMoveLsb (FbLeftStipBits (bits, n), 4, n)) {
	    case 0:
		break;
	    case 1:
		WRITE1(dst,0,fg);
		break;
	    case 2:
		WRITE1(dst,1,fg);
		break;
	    case 3:
		WRITE2(dst,0,fg);
		break;
	    case 4:
		WRITE1(dst,2,fg);
		break;
	    case 5:
		WRITE1(dst,0,fg);
		WRITE1(dst,2,fg);
		break;
	    case 6:
		WRITE1(dst,1,fg);
		WRITE1(dst,2,fg);
		break;
	    case 7:
		WRITE2(dst,0,fg);
		WRITE1(dst,2,fg);
		break;
	    case 8:
		WRITE1(dst,3,fg);
		break;
	    case 9:
		WRITE1(dst,0,fg);
		WRITE1(dst,3,fg);
		break;
	    case 10:
		WRITE1(dst,1,fg);
		WRITE1(dst,3,fg);
		break;
	    case 11:
		WRITE2(dst,0,fg);
		WRITE1(dst,3,fg);
		break;
	    case 12:
		WRITE2(dst,2,fg);
		break;
	    case 13:
		WRITE1(dst,0,fg);
		WRITE2(dst,2,fg);
		break;
	    case 14:
		WRITE1(dst,1,fg);
		WRITE2(dst,2,fg);
		break;
	    case 15:
		WRITE4(dst,0,fg);
		break;
	    }
	    bits = FbStipLeft (bits, n);
	    n = 4;
	    dst += 4;
	}
	dstLine += dstStride;
    }
}
#undef WRITE_ADDR1
#undef WRITE_ADDR2
#undef WRITE_ADDR4
#undef WRITE1
#undef WRITE2
#undef WRITE4

#endif

#ifdef POLYLINE
void
POLYLINE (DrawablePtr	pDrawable,
	  GCPtr		pGC,
	  int		mode,
	  int		npt,
	  DDXPointPtr	ptsOrig)
{
    INT32	    *pts = (INT32 *) ptsOrig;
    int		    xoff = pDrawable->x;
    int		    yoff = pDrawable->y;
    unsigned int    bias = miGetZeroLineBias(pDrawable->pScreen);
    BoxPtr	    pBox = REGION_EXTENTS (pDrawable->pScreen, fbGetCompositeClip (pGC));
    
    FbBits	    *dst;
    int		    dstStride;
    int		    dstBpp;
    int		    dstXoff, dstYoff;
    
    UNIT	    *bits, *bitsBase;
    FbStride	    bitsStride;
    BITS	    xor = fbGetGCPrivate(pGC)->xor;
    BITS	    and = fbGetGCPrivate(pGC)->and;
    int		    dashoffset = 0;
    
    INT32	    ul, lr;
    INT32	    pt1, pt2;

    int		    e, e1, e3, len;
    int		    stepmajor, stepminor;
    int		    octant;

    if (mode == CoordModePrevious)
	fbFixCoordModePrevious (npt, ptsOrig);
    
    fbGetDrawable (pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
    bitsStride = dstStride * (sizeof (FbBits) / sizeof (UNIT));
    bitsBase = ((UNIT *) dst) + (yoff + dstYoff) * bitsStride + (xoff + dstXoff) * MUL;
    ul = coordToInt(pBox->x1 - xoff,     pBox->y1 - yoff);
    lr = coordToInt(pBox->x2 - xoff - 1, pBox->y2 - yoff - 1);

    pt1 = *pts++;
    npt--;
    pt2 = *pts++;
    npt--;
    for (;;)
    {
	if (isClipped (pt1, ul, lr) | isClipped (pt2, ul, lr))
	{
	    fbSegment (pDrawable, pGC, 
		       intToX(pt1) + xoff, intToY(pt1) + yoff,
		       intToX(pt2) + xoff, intToY(pt2) + yoff,
		       npt == 0 && pGC->capStyle != CapNotLast,
		       &dashoffset);
	    if (!npt) {
		fbFinishAccess (pDrawable);
		return;
	    }
	    pt1 = pt2;
	    pt2 = *pts++;
	    npt--;
	}
	else
	{
	    bits = bitsBase + intToY(pt1) * bitsStride + intToX(pt1) * MUL;
	    for (;;)
	    {
		CalcLineDeltas (intToX(pt1), intToY(pt1),
				intToX(pt2), intToY(pt2),
				len, e1, stepmajor, stepminor, 1, bitsStride,
				octant);
		stepmajor *= MUL;
		if (len < e1)
		{
		    e3 = len;
		    len = e1;
		    e1 = e3;

		    e3 = stepminor;
		    stepminor = stepmajor;
		    stepmajor = e3;
		    SetYMajorOctant(octant);
		}
		e = -len;
		e1 <<= 1;
		e3 = e << 1;
		FIXUP_ERROR (e, octant, bias);
		if (and == 0)
		{
		    while (len--)
		    {
			STORE(bits,xor);
			bits += stepmajor;
			e += e1;
			if (e >= 0)
			{
			    bits += stepminor;
			    e += e3;
			}
		    }
		}
		else
		{
		    while (len--)
		    {
			RROP(bits,and,xor);
			bits += stepmajor;
			e += e1;
			if (e >= 0)
			{
			    bits += stepminor;
			    e += e3;
			}
		    }
		}
		if (!npt)
		{
		    if (pGC->capStyle != CapNotLast && 
			pt2 != *((INT32 *) ptsOrig))
		    {
			RROP(bits,and,xor);
		    }
		    fbFinishAccess (pDrawable);
		    return;
		}
		pt1 = pt2;
		pt2 = *pts++;
		--npt;
		if (isClipped (pt2, ul, lr))
		    break;
    	    }
	}
    }

    fbFinishAccess (pDrawable);
}
#endif

#ifdef POLYSEGMENT
void
POLYSEGMENT (DrawablePtr    pDrawable,
	     GCPtr	    pGC,
	     int	    nseg,
	     xSegment	    *pseg)
{
    INT32	    *pts = (INT32 *) pseg;
    int		    xoff = pDrawable->x;
    int		    yoff = pDrawable->y;
    unsigned int    bias = miGetZeroLineBias(pDrawable->pScreen);
    BoxPtr	    pBox = REGION_EXTENTS (pDrawable->pScreen, fbGetCompositeClip (pGC));
    
    FbBits	    *dst;
    int		    dstStride;
    int		    dstBpp;
    int		    dstXoff, dstYoff;
    
    UNIT	    *bits, *bitsBase;
    FbStride	    bitsStride;
    FbBits	    xorBits = fbGetGCPrivate(pGC)->xor;
    FbBits	    andBits = fbGetGCPrivate(pGC)->and;
    BITS	    xor = xorBits;
    BITS	    and = andBits;
    int		    dashoffset = 0;
    
    INT32	    ul, lr;
    INT32	    pt1, pt2;

    int		    e, e1, e3, len;
    int		    stepmajor, stepminor;
    int		    octant;
    Bool	    capNotLast;

    fbGetDrawable (pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
    bitsStride = dstStride * (sizeof (FbBits) / sizeof (UNIT));
    bitsBase = ((UNIT *) dst) + (yoff + dstYoff) * bitsStride + (xoff + dstXoff) * MUL;
    ul = coordToInt(pBox->x1 - xoff,     pBox->y1 - yoff);
    lr = coordToInt(pBox->x2 - xoff - 1, pBox->y2 - yoff - 1);

    capNotLast = pGC->capStyle == CapNotLast;
    
    while (nseg--)
    {
	pt1 = *pts++;
	pt2 = *pts++;
	if (isClipped (pt1, ul, lr) | isClipped (pt2, ul, lr))
	{
	    fbSegment (pDrawable, pGC, 
		       intToX(pt1) + xoff, intToY(pt1) + yoff,
		       intToX(pt2) + xoff, intToY(pt2) + yoff,
		       !capNotLast, &dashoffset);
	}
	else
	{
	    CalcLineDeltas (intToX(pt1), intToY(pt1),
			    intToX(pt2), intToY(pt2),
			    len, e1, stepmajor, stepminor, 1, bitsStride,
			    octant);
	    if (e1 == 0 && len > 3
#if MUL != 1
		&& FbCheck24Pix(and) && FbCheck24Pix(xor)
#endif
		)
	    {
		int	x1, x2;
		FbBits	*dstLine;
		int	dstX, width;
		FbBits	startmask, endmask;
		int	nmiddle;
		
		if (stepmajor < 0)
		{
		    x1 = intToX(pt2);
		    x2 = intToX(pt1) + 1;
		    if (capNotLast)
			x1++;
		}
		else
		{
		    x1 = intToX(pt1);
		    x2 = intToX(pt2);
		    if (!capNotLast)
			x2++;
		}
		dstX = (x1 + xoff + dstXoff) * (sizeof (UNIT) * 8 * MUL);
		width = (x2 - x1) * (sizeof (UNIT) * 8 * MUL);
		
		dstLine = dst + (intToY(pt1) + yoff + dstYoff) * dstStride;
		dstLine += dstX >> FB_SHIFT;
		dstX &= FB_MASK;
		FbMaskBits (dstX, width, startmask, nmiddle, endmask);
		if (startmask)
		{
		    WRITE(dstLine, FbDoMaskRRop (READ(dstLine), andBits, xorBits, startmask));
		    dstLine++;
		}
		if (!andBits)
		    while (nmiddle--)
			WRITE(dstLine++, xorBits);
		else
		    while (nmiddle--)
		    {
			WRITE(dstLine, FbDoRRop (READ(dstLine), andBits, xorBits));
			dstLine++;
		    }
		if (endmask)
		    WRITE(dstLine, FbDoMaskRRop (READ(dstLine), andBits, xorBits, endmask));
	    }
	    else
	    {
		stepmajor *= MUL;
		bits = bitsBase + intToY(pt1) * bitsStride + intToX(pt1) * MUL;
		if (len < e1)
		{
		    e3 = len;
		    len = e1;
		    e1 = e3;
    
		    e3 = stepminor;
		    stepminor = stepmajor;
		    stepmajor = e3;
		    SetYMajorOctant(octant);
		}
		e = -len;
		e1 <<= 1;
		e3 = e << 1;
		FIXUP_ERROR (e, octant, bias);
		if (!capNotLast)
		    len++;
		if (and == 0)
		{
		    while (len--)
		    {
			STORE(bits,xor);
			bits += stepmajor;
			e += e1;
			if (e >= 0)
			{
			    bits += stepminor;
			    e += e3;
			}
		    }
		}
		else
		{
		    while (len--)
		    {
			RROP(bits,and,xor);
			bits += stepmajor;
			e += e1;
			if (e >= 0)
			{
			    bits += stepminor;
			    e += e3;
			}
		    }
		}
	    }
	}
    }

    fbFinishAccess (pDrawable);
}
#endif

#undef MUL
#undef STORE
#undef RROP
#undef UNIT
#undef USE_SOLID

#undef isClipped
