/*      $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_ */