/*
 * Copyright © 1998 Keith Packard
 * Copyright © 2012 Intel Corporation
 *
 * 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.
 */

#define isClipped(c,ul,lr)  (((c) | ((c) - (ul)) | ((lr) - (c))) & 0x80008000)
#define RROP(b,a,x)	WRITE((b), FbDoRRop (READ(b), (a), (x)))

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

static void
ARC(FbBits * dst,
    FbStride dstStride,
    int dstBpp, xArc * arc, int drawX, int drawY, FbBits and, FbBits xor)
{
	BITS *bits;
	FbStride bitsStride;
	miZeroArcRec info;
	Bool do360;
	int x;
	BITS *yorgp, *yorgop;
	BITS andBits, xorBits;
	int yoffset, dyoffset;
	int y, a, b, d, mask;
	int k1, k3, dx, dy;

	bits = (BITS *) dst;
	bitsStride = dstStride * (sizeof(FbBits) / sizeof(BITS));
	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);
	info.xorgo = (info.xorgo + drawX);
	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;
		BITS *yorghb = yorgp + (info.h * bitsStride) + info.xorg;
		BITS *yorgohb = yorghb - info.h;

		yorgp += info.xorg;
		yorgop += info.xorg;
		yorghb += info.h;
		while (1) {
			if (andBits == 0) {
				ARCCOPY(yorgp + yoffset + x);
				ARCCOPY(yorgp + yoffset - x);
				ARCCOPY(yorgop - yoffset - x);
				ARCCOPY(yorgop - yoffset + x);
			} else {
				ARCRROP(yorgp + yoffset + x);
				ARCRROP(yorgp + yoffset - x);
				ARCRROP(yorgop - yoffset - x);
				ARCRROP(yorgop - yoffset + x);
			}
			if (a < 0)
				break;
			if (andBits == 0) {
				ARCCOPY(yorghb - xoffset - y);
				ARCCOPY(yorgohb - xoffset + y);
				ARCCOPY(yorgohb + xoffset + y);
				ARCCOPY(yorghb + xoffset - y);
			} else {
				ARCRROP(yorghb - xoffset - y);
				ARCRROP(yorgohb - xoffset + y);
				ARCRROP(yorgohb + xoffset + y);
				ARCRROP(yorghb + xoffset - y);
			}
			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);
				ARCCOPY(yorgp + yoffset + info.xorgo - x);
				ARCCOPY(yorgop - yoffset + info.xorgo - x);
				ARCCOPY(yorgop - yoffset + info.xorg + x);
			} else {
				ARCRROP(yorgp + yoffset + info.xorg + x);
				ARCRROP(yorgp + yoffset + info.xorgo - x);
				ARCRROP(yorgop - yoffset + info.xorgo - x);
				ARCRROP(yorgop - yoffset + info.xorg + x);
			}
			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);
				if (mask & 2)
					ARCCOPY(yorgp + yoffset + info.xorgo - x);
				if (mask & 4)
					ARCCOPY(yorgop - yoffset + info.xorgo - x);
				if (mask & 8)
					ARCCOPY(yorgop - yoffset + info.xorg + x);
			} else {
				if (mask & 1)
					ARCRROP(yorgp + yoffset + info.xorg + x);
				if (mask & 2)
					ARCRROP(yorgp + yoffset + info.xorgo - x);
				if (mask & 4)
					ARCRROP(yorgop - yoffset + info.xorgo - x);
				if (mask & 8)
					ARCRROP(yorgop - yoffset + info.xorg + x);
			}
			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);
		if (mask & 4)
			ARCCOPY(yorgop - yoffset + info.xorgo - x);
		if (arc->height & 1) {
			if (mask & 2)
				ARCCOPY(yorgp + yoffset + info.xorgo - x);
			if (mask & 8)
				ARCCOPY(yorgop - yoffset + info.xorg + x);
		}
	} else {
		if (mask & 1)
			ARCRROP(yorgp + yoffset + info.xorg + x);
		if (mask & 4)
			ARCRROP(yorgop - yoffset + info.xorgo - x);
		if (arc->height & 1) {
			if (mask & 2)
				ARCRROP(yorgp + yoffset + info.xorgo - x);
			if (mask & 8)
				ARCRROP(yorgop - yoffset + info.xorg + x);
		}
	}
}

#undef ARCCOPY
#undef ARCRROP

#undef RROP
#undef isClipped