/* 	$NetBSD: rasops_bitops.h,v 1.25 2019/08/10 01:24:17 rin Exp $	*/

/*-
 * Copyright (c) 1999 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Andrew Doran.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _RASOPS_BITOPS_H_
#define _RASOPS_BITOPS_H_ 1

#if   RASOPS_DEPTH == 1
#define	PIXEL_SHIFT	0
#elif RASOPS_DEPTH == 2
#define	PIXEL_SHIFT	1
#elif RASOPS_DEPTH == 4
#define	PIXEL_SHIFT	2
#else
#error "Depth not supported"
#endif

#define	NAME(name)		NAME1(RASOPS_DEPTH, name)
#define	NAME1(depth, name)	NAME2(depth, name)
#define	NAME2(depth, name)	rasops ## depth ## _ ## name

/*
 * Erase columns.
 */
static void
NAME(erasecols)(void *cookie, int row, int col, int num, long attr)
{
	struct rasops_info *ri = (struct rasops_info *)cookie;
	int height, cnt;
	uint32_t bg, lbg, rbg, lmask, rmask, tmp;
	uint32_t *dp, *rp, *hp;

	hp = NULL;	/* XXX GCC */

#ifdef RASOPS_CLIPPING
	if ((unsigned)row >= (unsigned)ri->ri_rows)
		return;

	if (col < 0) {
		num += col;
		col = 0;
	}

	if (col + num > ri->ri_cols)
		num = ri->ri_cols - col;

	if (num <= 0)
		return;
#endif

	height = ri->ri_font->fontheight;
	col *= ri->ri_font->fontwidth << PIXEL_SHIFT;
	num *= ri->ri_font->fontwidth << PIXEL_SHIFT;

	rp = (uint32_t *)(ri->ri_bits + row*ri->ri_yscale + ((col >> 3) & ~3));
	if (ri->ri_hwbits)
		hp = (uint32_t *)(ri->ri_hwbits + row*ri->ri_yscale +
		    ((col >> 3) & ~3));

	col &= 31;

	bg = ATTR_BG(ri, attr);

	if (col + num <= 32) {
		lmask = ~rasops_pmask[col][num & 31];
		bg &= ~lmask;

		while (height--) {
			tmp = (*rp & lmask) | bg;
			*rp = tmp;

			if (ri->ri_hwbits) {
				*hp = tmp;
				DELTA(hp, ri->ri_stride, uint32_t *);
			}

			DELTA(rp, ri->ri_stride, uint32_t *);
		}
	} else {
		lmask = rasops_rmask[col];
		rmask = rasops_lmask[(col + num) & 31];

		if (lmask)
			num = (num - (32 - col)) >> 5;
		else
			num = num >> 5;

		lbg = bg & ~lmask;
		rbg = bg & ~rmask;

		while (height--) {
			dp = rp;

			if (lmask) {
				*dp = (*dp & lmask) | lbg;
				dp++;
			}

			for (cnt = num; cnt > 0; cnt--)
				*dp++ = bg;

			if (rmask)
				*dp = (*dp & rmask) | rbg;

			if (ri->ri_hwbits) {
				memcpy(hp, rp, ((lmask != 0) + num +
				    (rmask != 0)) << 2);
				DELTA(hp, ri->ri_stride, uint32_t *);
			}

			DELTA(rp, ri->ri_stride, uint32_t *);
		}
	}
}

/*
 * Actually paint the cursor.
 */
static void
NAME(do_cursor)(struct rasops_info *ri)
{
	int row, col, height, width, cnt;
	uint32_t lmask, rmask, tmp;
	uint32_t *dp, *rp, *hp;

	hp = NULL;	/* XXX GCC */

	row = ri->ri_crow;
	col = ri->ri_ccol * ri->ri_font->fontwidth << PIXEL_SHIFT;

	height = ri->ri_font->fontheight;
	width = ri->ri_font->fontwidth << PIXEL_SHIFT;

	rp = (uint32_t *)(ri->ri_bits + row * ri->ri_yscale +
	    ((col >> 3) & ~3));
	if (ri->ri_hwbits)
		hp = (uint32_t *)(ri->ri_hwbits + row * ri->ri_yscale +
		    ((col >> 3) & ~3));

	col &= 31;

	if (col + width <= 32) {
		lmask = rasops_pmask[col][width & 31];

		while (height--) {
			tmp = *rp ^ lmask;
			*rp = tmp;
			if (ri->ri_hwbits) {
				*hp = tmp;
				DELTA(hp, ri->ri_stride, uint32_t *);
			}
			DELTA(rp, ri->ri_stride, uint32_t *);
		}
	} else {
		lmask = ~rasops_rmask[col];
		rmask = ~rasops_lmask[(col + width) & 31];

		if (lmask != -1)
			width = (width - (32 - col)) >> 5;
		else
			width = width >> 5;

		while (height--) {
			dp = rp;

			if (lmask != -1)
				*dp++ ^= lmask;

			for (cnt = width; cnt; cnt--) {
				*dp = ~*dp;
				dp++;
			}

			if (rmask != -1)
				*dp ^= rmask;

			if (ri->ri_hwbits) {
				memcpy(hp, rp, ((lmask != -1) + width +
				    (rmask != -1)) << 2);
				DELTA(hp, ri->ri_stride, uint32_t *);
			}

			DELTA(rp, ri->ri_stride, uint32_t *);
		}
	}
}

/*
 * Copy columns. Ick!
 */
static void
NAME(copycols)(void *cookie, int row, int src, int dst, int num)
{
	struct rasops_info *ri = (struct rasops_info *)cookie;
	int height, width, lnum, rnum, sb, db, full, cnt, sboff;
	uint32_t lmask, rmask, tmp;
	uint32_t *sp, *dp, *srp, *drp, *hp;
	bool sbover;

	hp = NULL;	/* XXX GCC */

	if (__predict_false(dst == src))
		return;

#ifdef RASOPS_CLIPPING
	/* Catches < 0 case too */
	if ((unsigned)row >= (unsigned)ri->ri_rows)
		return;

	if (src < 0) {
		num += src;
		src = 0;
	}

	if (src + num > ri->ri_cols)
		num = ri->ri_cols - src;

	if (dst < 0) {
		num += dst;
		dst = 0;
	}

	if (dst + num > ri->ri_cols)
		num = ri->ri_cols - dst;

	if (num <= 0)
		return;
#endif

	height = ri->ri_font->fontheight;
	width = ri->ri_font->fontwidth << PIXEL_SHIFT;

	row *= ri->ri_yscale;

	src *= width;
	dst *= width;
	num *= width;

	sb = src & 31;
	db = dst & 31;

	if (db + num <= 32) {
		/* Destination is contained within a single word */
		srp = (uint32_t *)(ri->ri_bits + row + ((src >> 3) & ~3));
		drp = (uint32_t *)(ri->ri_bits + row + ((dst >> 3) & ~3));
		if (ri->ri_hwbits)
			hp = (uint32_t *)(ri->ri_hwbits + row +
			    ((dst >> 3) & ~3));

		while (height--) {
			GETBITS(srp, sb, num, tmp);
			PUTBITS(tmp, db, num, drp);
			if (ri->ri_hwbits) {
				*hp = *drp;
				DELTA(hp, ri->ri_stride, uint32_t *);
			}	
			DELTA(srp, ri->ri_stride, uint32_t *);
			DELTA(drp, ri->ri_stride, uint32_t *);
		}

		return;
	}

	lmask = rasops_rmask[db];
	rmask = rasops_lmask[(dst + num) & 31];
	lnum = (32 - db) & 31;
	rnum = (dst + num) & 31;

	if (lmask != 0)
		full = (num - lnum) >> 5;
	else
		full = num >> 5;

	if (src < dst && src + num > dst) {
		/* Copy right-to-left */
		srp = (uint32_t *)(ri->ri_bits + row +
		    (((src + num) >> 3) & ~3));
		drp = (uint32_t *)(ri->ri_bits + row +
		    (((dst + num) >> 3) & ~3));
		if (ri->ri_hwbits) {
			hp = (uint32_t *)(ri->ri_hwbits + row +
			    (((dst + num) >> 3) & ~3));
			hp -= (lmask != 0) + full;
		}

		sboff = (src + num) & 31;
		sbover = sb + lnum >= 32;
		if ((sboff -= rnum) < 0) {
			srp--;
			sboff += 32;
		}

		while (height--) {
			sp = srp;
			dp = drp;

			if (rmask != 0) {
				GETBITS(sp, sboff, rnum, tmp);
				PUTBITS(tmp, 0, rnum, dp);
			}

			/* Now aligned to 32-bits wrt dp */
			for (cnt = full; cnt; cnt--) {
				--dp;
				--sp;
				GETBITS(sp, sboff, 32, tmp);
				*dp = tmp;
			}

			if (lmask != 0) {
				if (sbover)
					--sp;
				--dp;
				GETBITS(sp, sb, lnum, tmp);
				PUTBITS(tmp, db, lnum, dp);
 			}

			if (ri->ri_hwbits) {
				memcpy(hp, dp, ((lmask != 0) + full +
				    (rmask != 0)) << 2);
				DELTA(hp, ri->ri_stride, uint32_t *);
			}

			DELTA(srp, ri->ri_stride, uint32_t *);
			DELTA(drp, ri->ri_stride, uint32_t *);
 		}
	} else {
		/* Copy left-to-right */
		srp = (uint32_t *)(ri->ri_bits + row + ((src >> 3) & ~3));
		drp = (uint32_t *)(ri->ri_bits + row + ((dst >> 3) & ~3));
		if (ri->ri_hwbits)
			hp = (uint32_t *)(ri->ri_hwbits + row +
			    ((dst >> 3) & ~3));

		while (height--) {
			sp = srp;
			dp = drp;

			sboff = sb;

			if (lmask != 0) {
				GETBITS(sp, sboff, lnum, tmp);
				PUTBITS(tmp, db, lnum, dp);
				dp++;

				if ((sboff += lnum) > 31) {
					sp++;
					sboff -= 32;
				}
			}

			/* Now aligned to 32-bits wrt dp */
			for (cnt = full; cnt; cnt--, sp++) {
				GETBITS(sp, sboff, 32, tmp);
				*dp++ = tmp;
			}

			if (rmask != 0) {
				GETBITS(sp, sboff, rnum, tmp);
				PUTBITS(tmp, 0, rnum, dp);
 			}

			if (ri->ri_hwbits) {
				memcpy(hp, drp, ((lmask != 0) + full +
				    (rmask != 0)) << 2);
				DELTA(hp, ri->ri_stride, uint32_t *);
			}

			DELTA(srp, ri->ri_stride, uint32_t *);
			DELTA(drp, ri->ri_stride, uint32_t *);
 		}
 	}
}

#undef	PIXEL_SHIFT

#undef	NAME
#undef	NAME1
#undef	NAME2

#endif /* _RASOPS_BITOPS_H_ */
