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

static void
POLYLINE(DrawablePtr drawable, GCPtr gc, int mode, int n_0, DDXPointPtr pt_0)
{
	int xoff = drawable->x;
	int yoff = drawable->y;
	unsigned int bias = miGetZeroLineBias(drawable->pScreen);
	const BoxRec *clip = region_rects(gc->pCompositeClip);
	const BoxRec *const last_clip = clip + region_num_rects(gc->pCompositeClip);

	FbBits *dst;
	int dstStride;
	int dstBpp;
	int dstXoff, dstYoff;

	BITS *bits, *bitsBase;
	FbStride bitsStride;
	BITS xor = fb_gc(gc)->xor;
	BITS and = fb_gc(gc)->and;


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

	if (mode == CoordModePrevious)
		fbFixCoordModePrevious(n_0, pt_0);

	fbGetDrawable(drawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
	bitsStride = dstStride * (sizeof(FbBits) / sizeof(BITS));
	bitsBase = ((BITS *) dst) + (yoff + dstYoff) * bitsStride + (xoff + dstXoff);

	DBG(("%s: processing %ld clip boxes\n", __FUNCTION__, (long)(last_clip - clip)));
	do {
		INT32 *pt = (INT32 *)pt_0;
		int n = n_0;
		INT32 pt1, pt2;

		INT32 ul = coordToInt(clip->x1 - xoff, clip->y1 - yoff);
		INT32 lr = coordToInt(clip->x2 - xoff - 1, clip->y2 - yoff - 1);

		DBG(("%s: clip box=(%d, %d), (%d, %d)\n", __FUNCTION__, clip->x1, clip->y1, clip->x2, clip->y2));

		pt1 = *pt++; n--;
		pt2 = *pt++; n--;
		for (;;) {
			if (isClipped(pt1, ul, lr) | isClipped(pt2, ul, lr)) {
				int dashoffset = 0;
				fbSegment1(drawable, gc, clip,
					  intToX(pt1) + xoff, intToY(pt1) + yoff,
					  intToX(pt2) + xoff, intToY(pt2) + yoff,
					  n == 0 && gc->capStyle != CapNotLast, &dashoffset);
				if (!n)
					goto next_clip;

				pt1 = pt2;
				pt2 = *pt++;
				n--;
			} else {
				bits = bitsBase + intToY(pt1) * bitsStride + intToX(pt1);
				for (;;) {
					CalcLineDeltas(intToX(pt1), intToY(pt1),
						       intToX(pt2), intToY(pt2),
						       len, e1, stepmajor, stepminor, 1, bitsStride,
						       octant);
					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--) {
							WRITE(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 (!n) {
						if (gc->capStyle != CapNotLast &&
						    pt2 != *((INT32 *)pt_0)) {
							RROP(bits, and, xor);
						}
						goto next_clip;
					}
					pt1 = pt2;
					pt2 = *pt++;
					--n;
					if (isClipped(pt2, ul, lr))
						break;
				}
			}
		}
next_clip: (void)clip;
	} while (++clip != last_clip);
}

static void
POLYSEGMENT(DrawablePtr drawable, GCPtr gc, int n_0, xSegment *seg_0)
{
	int xoff = drawable->x;
	int yoff = drawable->y;
	unsigned int bias = miGetZeroLineBias(drawable->pScreen);
	const BoxRec *clip = region_rects(gc->pCompositeClip);
	const BoxRec *const last_clip = clip + region_num_rects(gc->pCompositeClip);

	FbBits *dst;
	int dstStride;
	int dstBpp;
	int dstXoff, dstYoff;

	BITS *bits, *bitsBase;
	FbStride bitsStride;
	FbBits xor = fb_gc(gc)->xor;
	FbBits and = fb_gc(gc)->and;

	int e, e1, e3, len;
	int stepmajor, stepminor;
	int octant;
	bool capNotLast = gc->capStyle == CapNotLast;

	fbGetDrawable(drawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
	bitsStride = dstStride * (sizeof(FbBits) / sizeof(BITS));
	bitsBase =
		((BITS *) dst) + (yoff + dstYoff) * bitsStride + (xoff + dstXoff);

	DBG(("%s: processing %ld clip boxes\n", __FUNCTION__, (long)(last_clip - clip)));
	do {
		INT32 ul = coordToInt(clip->x1 - xoff, clip->y1 - yoff);
		INT32 lr = coordToInt(clip->x2 - xoff - 1, clip->y2 - yoff - 1);
		uint64_t *pt = (uint64_t *)seg_0;
		int n = n_0;

		DBG(("%s: clip box=(%d, %d), (%d, %d)\n", __FUNCTION__, clip->x1, clip->y1, clip->x2, clip->y2));

		while (n--) {
			union {
				int32_t pt32[2];
				uint64_t pt64;
			} u;

			u.pt64 = *pt++;
			if (isClipped(u.pt32[0], ul, lr) | isClipped(u.pt32[1], ul, lr)) {
				int dashoffset = 0;
				fbSegment1(drawable, gc, clip,
					  intToX(u.pt32[0]) + xoff, intToY(u.pt32[0]) + yoff,
					  intToX(u.pt32[1]) + xoff, intToY(u.pt32[1]) + yoff,
					  !capNotLast, &dashoffset);
			} else {
				CalcLineDeltas(intToX(u.pt32[0]), intToY(u.pt32[0]),
					       intToX(u.pt32[1]), intToY(u.pt32[1]),
					       len, e1, stepmajor, stepminor, 1, bitsStride,
					       octant);
				if (e1 == 0 && len > 3) {
					int x1, x2;
					FbBits *dstLine;
					int dstX, width;
					FbBits startmask, endmask;
					int nmiddle;

					if (stepmajor < 0) {
						x1 = intToX(u.pt32[1]);
						x2 = intToX(u.pt32[0]) + 1;
						if (capNotLast)
							x1++;
					} else {
						x1 = intToX(u.pt32[0]);
						x2 = intToX(u.pt32[1]);
						if (!capNotLast)
							x2++;
					}
					dstX = (x1 + xoff + dstXoff) * (sizeof(BITS) * 8);
					width = (x2 - x1) * (sizeof(BITS) * 8);

					dstLine = dst + (intToY(u.pt32[0]) + yoff + dstYoff) * dstStride;
					dstLine += dstX >> FB_SHIFT;
					dstX &= FB_MASK;
					FbMaskBits(dstX, width, startmask, nmiddle, endmask);
					if (startmask) {
						WRITE(dstLine,
						      FbDoMaskRRop(READ(dstLine), and, xor,
								   startmask));
						dstLine++;
					}
					if (!and)
						while (nmiddle--)
							WRITE(dstLine++, xor);
					else
						while (nmiddle--) {
							WRITE(dstLine,
							      FbDoRRop(READ(dstLine), and, xor));
							dstLine++;
						}
					if (endmask)
						WRITE(dstLine,
						      FbDoMaskRRop(READ(dstLine), and, xor,
								   endmask));
				} else {
					bits = bitsBase + intToY(u.pt32[0]) * bitsStride + intToX(u.pt32[0]);
					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--) {
							WRITE(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;
							}
						}
					}
				}
			}
		}
	} while (++clip != last_clip);
}

#undef RROP
#undef isClipped