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

#include "fb.h"
#include "fbclip.h"

static void
fbPushPattern(DrawablePtr drawable, GCPtr gc,
              FbStip *src, FbStride srcStride, int srcX,
	      int x, int y, int width, int height)
{
	FbStip *s, bitsMask, bitsMask0, bits;
	int xspan;
	int w;
	int lenspan;

	src += srcX >> FB_STIP_SHIFT;
	srcX &= FB_STIP_MASK;

	bitsMask0 = FbStipMask(srcX, 1);

	while (height--) {
		bitsMask = bitsMask0;
		w = width;
		s = src;
		src += srcStride;
		bits = READ(s++);
		xspan = x;
		while (w) {
			if (bits & bitsMask) {
				lenspan = 0;
				do {
					if (++lenspan == w)
						break;

					bitsMask = FbStipRight(bitsMask, 1);
					if (!bitsMask) {
						bits = READ(s++);
						bitsMask = FbBitsMask(0, 1);
					}
				} while (bits & bitsMask);
				fbFill(drawable, gc, xspan, y, lenspan, 1);
				xspan += lenspan;
				w -= lenspan;
			} else {
				do {
					xspan++;
					if (!--w)
						break;

					bitsMask = FbStipRight(bitsMask, 1);
					if (!bitsMask) {
						bits = READ(s++);
						bitsMask = FbBitsMask(0, 1);
					}
				} while (!(bits & bitsMask));
			}
		}
		y++;
	}
}

static void
fbPushFill(DrawablePtr drawable, GCPtr gc,
           FbStip *src, FbStride srcStride, int srcX,
	   int x, int y, int width, int height)
{
	FbGCPrivPtr pgc = fb_gc(gc);

	if (gc->fillStyle == FillSolid) {
		FbBits *dst;
		FbStride dstStride;
		int dstBpp;
		int dstXoff, dstYoff;
		int dstX;
		int dstWidth;

		fbGetDrawable(drawable, dst,
			      dstStride, dstBpp, dstXoff, dstYoff);
		dst = dst + (y + dstYoff) * dstStride;
		dstX = (x + dstXoff) * dstBpp;
		dstWidth = width * dstBpp;
		if (dstBpp == 1) {
			fbBltStip(src, srcStride, srcX,
				  (FbStip *)dst, dstStride, dstX,
				  dstWidth, height,
				  FbStipple1Rop(gc->alu, gc->fgPixel), pgc->pm, dstBpp);
		} else {
			fbBltOne(src, srcStride, srcX,
				 dst, dstStride, dstX, dstBpp,
				 dstWidth, height,
				 pgc->and, pgc->xor,
				 fbAnd(GXnoop, (FbBits) 0, FB_ALLONES),
				 fbXor(GXnoop, (FbBits) 0, FB_ALLONES));
		}
	} else
		fbPushPattern(drawable, gc, src, srcStride, srcX,
			      x, y, width, height);
}

struct fbPushImage {
	FbStip *src;
	FbStride stride;
	int x0, y0;
};

inline static void
_fbPushImage(DrawablePtr drawable, GCPtr gc, const BoxRec *b, void *_data)
{
	struct fbPushImage *data = _data;

	fbPushFill(drawable, gc,
		   data->src + (b->y1 - data->y0) * data->stride, data->stride,
		   b->x1 - data->x0,
		   b->x1, b->y1,
		   b->x2 - b->x1, b->y2 - b->y1);
}

void
fbPushImage(DrawablePtr drawable, GCPtr gc,
            FbStip *src, FbStride stride, int dx,
	    int x, int y, int width, int height)
{
	struct fbPushImage data;
	BoxRec box;

	DBG(("%s (%d, %d)x(%d, %d)\n", __FUNCTION__, x, y, width, height));

	data.src = src;
	data.stride = stride;
	data.y0 = y;
	data.x0 = x - dx;

	box.x1 = x;
	box.y1 = y;
	box.x2 = x + width;
	box.y2 = y + height;
	fbDrawableRun(drawable, gc, &box, _fbPushImage, &data);
}

void
fbPushPixels(GCPtr gc, PixmapPtr bitmap, DrawablePtr drawable,
	     int dx, int dy, int xOrg, int yOrg)
{
	FbStip *stip;
	FbStride stipStride;
	int stipBpp;
	_X_UNUSED int stipXoff, stipYoff;

	DBG(("%s bitmap=%x%d\n", __FUNCTION__,
	     bitmap->drawable.width, bitmap->drawable.height));

	fbGetStipDrawable(&bitmap->drawable, stip,
			  stipStride, stipBpp, stipXoff, stipYoff);

	fbPushImage(drawable, gc, stip, stipStride, 0, xOrg, yOrg, dx, dy);
}