/* $NetBSD: rasops_putchar_width.h,v 1.15 2019/08/14 00:51:10 rin Exp $ */

/* NetBSD: rasops8.c,v 1.41 2019/07/25 03:02:44 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.
*/

#if RASOPS_DEPTH !=  2 && RASOPS_DEPTH !=  4 && RASOPS_DEPTH !=  8 &&   \
   RASOPS_DEPTH != 15 && RASOPS_DEPTH != 24 && RASOPS_DEPTH != 32
#error "Depth not supported"
#endif

#if RASOPS_WIDTH != 8 && RASOPS_WIDTH != 12 && RASOPS_WIDTH != 16
#error "Width not supported"
#endif

#if   RASOPS_DEPTH == 2
#define STAMP_TYPE      uint8_t
#elif RASOPS_DEPTH == 4
#define STAMP_TYPE      uint16_t
#else
#define STAMP_TYPE      uint32_t
#endif

#if   RASOPS_DEPTH <= 8
#define SUBST_UNIT      1
#elif RASOPS_DEPTH == 15
#define SUBST_UNIT      2
#elif RASOPS_DEPTH == 24
#define SUBST_UNIT      3
#elif RASOPS_DEPTH == 32
#define SUBST_UNIT      4
#endif

#define SUBST_BYTES     (SUBST_UNIT * (RASOPS_WIDTH / 4) * sizeof(STAMP_TYPE))

#if   RASOPS_DEPTH <= 8
#define FILLED_STAMP    15
#elif RASOPS_DEPTH == 15
#define FILLED_STAMP    30
#else
#define FILLED_STAMP    60
#endif

/* ################################################################### */

#if RASOPS_DEPTH <= 8

#define SUBST_STAMP1(off, base)                                         \
       rp[(off) * 1 + 0] = stamp[base]

#define SUBST_GLYPH1(index, nibble, off)                                \
       do {                                                            \
               int so = STAMP_SHIFT(fr[index], nibble) & STAMP_MASK;   \
               rp[(off) * 1 + 0] = STAMP_READ(so);                     \
       } while (0 /* CONSTCOND */)

#endif /* RASOPS_DEPTH <= 8 */

/* ################################################################### */

#if RASOPS_DEPTH == 15

#define SUBST_STAMP1(off, base)                                         \
       rp[(off) * 2 + 0] = rp[(off) * 2 + 1] = stamp[base]

#define SUBST_GLYPH1(index, nibble, off)                                \
       do {                                                            \
               int so = STAMP_SHIFT(fr[index], nibble) & STAMP_MASK;   \
               rp[(off) * 2 + 0] = STAMP_READ(so);                     \
               rp[(off) * 2 + 1] = STAMP_READ(so +  4);                \
       } while (0 /* CONSTCOND */)

#endif /* RASOPS_DEPTH == 15 */

/* ################################################################### */

#if RASOPS_DEPTH == 24

#define SUBST_STAMP1(off, base)                                         \
       do {                                                            \
               rp[(off) * 3 + 0] = stamp[(base) + 0];                  \
               rp[(off) * 3 + 1] = stamp[(base) + 1];                  \
               rp[(off) * 3 + 2] = stamp[(base) + 2];                  \
       } while (0 /* CONSTCOND */)

#define SUBST_GLYPH1(index, nibble, off)                                \
       do {                                                            \
               int so = STAMP_SHIFT(fr[index], nibble) & STAMP_MASK;   \
               rp[(off) * 3 + 0] = STAMP_READ(so);                     \
               rp[(off) * 3 + 1] = STAMP_READ(so +  4);                \
               rp[(off) * 3 + 2] = STAMP_READ(so +  8);                \
       } while (0 /* CONSTCOND */)

#endif /* RASOPS_DEPTH == 24 */

/* ################################################################### */

#if RASOPS_DEPTH == 32

#define SUBST_STAMP1(off, base)                                         \
       rp[(off) * 4 + 0] = rp[(off) * 4 + 1] =                         \
       rp[(off) * 4 + 2] = rp[(off) * 4 + 3] = stamp[base]

#define SUBST_GLYPH1(index, nibble, off)                                \
       do {                                                            \
               int so = STAMP_SHIFT(fr[index], nibble) & STAMP_MASK;   \
               rp[(off) * 4 + 0] = STAMP_READ(so);                     \
               rp[(off) * 4 + 1] = STAMP_READ(so +  4);                \
               rp[(off) * 4 + 2] = STAMP_READ(so +  8);                \
               rp[(off) * 4 + 3] = STAMP_READ(so + 12);                \
       } while (0 /* CONSTCOND */)

#endif /* RASOPS_DEPTH == 32 */

/* ################################################################### */

#if   RASOPS_WIDTH == 8
#define SUBST_STAMP(base)                       \
       do {                                    \
               SUBST_STAMP1(0, base);          \
               SUBST_STAMP1(1, base);          \
       } while (0 /* CONSTCOND */)
#elif RASOPS_WIDTH == 12
#define SUBST_STAMP(base)                       \
       do {                                    \
               SUBST_STAMP1(0, base);          \
               SUBST_STAMP1(1, base);          \
               SUBST_STAMP1(2, base);          \
       } while (0 /* CONSTCOND */)
#elif RASOPS_WIDTH == 16
#define SUBST_STAMP(base)                       \
       do {                                    \
               SUBST_STAMP1(0, base);          \
               SUBST_STAMP1(1, base);          \
               SUBST_STAMP1(2, base);          \
               SUBST_STAMP1(3, base);          \
       } while (0 /* CONSTCOND */)
#endif

/* ################################################################### */

#if   RASOPS_WIDTH == 8
#define SUBST_GLYPH                             \
       do {                                    \
               SUBST_GLYPH1(0, 1, 0);          \
               SUBST_GLYPH1(0, 0, 1);          \
       } while (0 /* CONSTCOND */)
#elif RASOPS_WIDTH == 12
#define SUBST_GLYPH                             \
       do {                                    \
               SUBST_GLYPH1(0, 1, 0);          \
               SUBST_GLYPH1(0, 0, 1);          \
               SUBST_GLYPH1(1, 1, 2);          \
       } while (0 /* CONSTCOND */)
#elif RASOPS_WIDTH == 16
#define SUBST_GLYPH                             \
       do {                                    \
               SUBST_GLYPH1(0, 1, 0);          \
               SUBST_GLYPH1(0, 0, 1);          \
               SUBST_GLYPH1(1, 1, 2);          \
               SUBST_GLYPH1(1, 0, 3);          \
       } while (0 /* CONSTCOND */)
#endif

/* ################################################################### */

#define NAME(depth, width)      NAME1(depth, width)
#define NAME1(depth, width)     rasops ## depth ## _putchar ## width

#define PUTCHAR(depth)          PUTCHAR1(depth)
#define PUTCHAR1(depth)         rasops ## depth ## _putchar

#define MAKESTAMP(depth)        MAKESTAMP1(depth)
#define MAKESTAMP1(depth)       rasops ## depth ## _makestamp

/*
* Width-optimized putchar function.
*/
static void
NAME(RASOPS_DEPTH, RASOPS_WIDTH)(void *cookie, int row, int col, u_int uc,
   long attr)
{
       struct rasops_info *ri = (struct rasops_info *)cookie;
       struct wsdisplay_font *font = PICK_FONT(ri, uc);
       int height;
       uint8_t *fr;
       bool do_ul;
       STAMP_TYPE *rp, *hp;

       hp = NULL; /* XXX GCC */

       /* check if character fits into font limits */
       if (__predict_false(!CHAR_IN_FONT(uc, font)))
               return;

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

       if ((unsigned)col >= (unsigned)ri->ri_cols)
               return;
#endif

       /*
        * We don't care attributions other than back/foreground
        * colors when using stamp.
        */
       do_ul = (attr & WSATTR_UNDERLINE) != 0;
       attr &= (ATTR_MASK_BG | ATTR_MASK_FG);

       /* Recompute stamp? */
       if (attr != stamp_attr || __predict_false(ri != stamp_ri))
               MAKESTAMP(RASOPS_DEPTH)(ri, attr);

       height = font->fontheight;

       rp = (STAMP_TYPE *)(ri->ri_bits + FBOFFSET(ri, row, col));
       if (ri->ri_hwbits)
               hp = (STAMP_TYPE *)(ri->ri_hwbits + FBOFFSET(ri, row, col));

       if (uc == ' ') {
               while (height--) {
                       SUBST_STAMP(0);
                       if (ri->ri_hwbits) {
                               memcpy(hp, rp, SUBST_BYTES);
                               DELTA(hp, ri->ri_stride, STAMP_TYPE *);
                       }
                       DELTA(rp, ri->ri_stride, STAMP_TYPE *);
               }
       } else {
               fr = FONT_GLYPH(uc, font, ri);
               while (height--) {
                       SUBST_GLYPH;
                       fr += font->stride;
                       if (ri->ri_hwbits) {
                               memcpy(hp, rp, SUBST_BYTES);
                               DELTA(hp, ri->ri_stride, STAMP_TYPE *);
                       }
                       DELTA(rp, ri->ri_stride, STAMP_TYPE *);
               }
       }

       /* Do underline */
       if (do_ul) {
               DELTA(rp, - ri->ri_stride * ri->ri_ul.off, STAMP_TYPE *);
               if (ri->ri_hwbits)
                       DELTA(hp, - ri->ri_stride * ri->ri_ul.off,
                           STAMP_TYPE *);

               for (height = ri->ri_ul.height; height; height--) {
                       DELTA(rp, - ri->ri_stride, STAMP_TYPE *);
                       SUBST_STAMP(FILLED_STAMP);
                       if (ri->ri_hwbits) {
                               DELTA(hp, - ri->ri_stride, STAMP_TYPE *);
                               memcpy(hp, rp, SUBST_BYTES);
                       }
               }
       }
}

#undef  STAMP_TYPE

#undef  SUBST_UNIT
#undef  SUBST_BYTES

#undef  FILLED_STAMP

#undef  SUBST_STAMP1
#undef  SUBST_STAMP

#undef  SUBST_GLYPH1
#undef  SUBST_GLYPH

#undef  NAME
#undef  NAME1

#undef  PUTCHAR
#undef  PUTCHAR1

#undef  MAKESTAMP
#undef  MAKESTAMP1