/****************************************************************************
 * apps/graphics/nuttx/pdcdisp.c
 *
 *   Copyright (C) 2017 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * 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.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
 * COPYRIGHT OWNER 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.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <sys/ioctl.h>
#include <errno.h>

#include "pdcnuttx.h"

/****************************************************************************
 * Public Data
 ****************************************************************************/

/* A port of PDCurses must provide acs_map[], a 128-element array of chtypes,
 * with values laid out based on the Alternate Character Set of the VT100
 * (see curses.h).  PDC_transform_line() must use this table; when it
 * encounters a chtype with the A_ALTCHARSET flag set, and an A_CHARTEXT
 * value in the range 0-127, it must render it using the A_CHARTEXT portion
 * of the corresponding value from this table, instead of the original
 * value.  Also, values may be read from this table by apps, and passed
 * through functions such as waddch(), which does no special processing on
 * control characters (0-31 and 127) when the A_ALTCHARSET flag is set.
 * Thus, any control characters used in acs_map[] should also have the
 * A_ALTCHARSET flag set. Implementations should provide suitable values
 * for all the ACS_ macros defined in curses.h; other values in the table
 * should be filled with their own indices (e.g., acs_map['E'] == 'E'). The
 * table can be either hardwired, or filled by PDC_scr_open(). Existing
 * ports define it in pdcdisp.c, but this is not required.
 */

#ifdef CONFIG_PDCURSES_CHTYPE_LONG

# define A(x) ((chtype)x | A_ALTCHARSET)

chtype acs_map[128] =
{
  A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9),
  A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18),
  A(19), A(20), A(21), A(22), A(23), A(24), A(25), A(26), A(27),
  A(28), A(29), A(30), A(31), ' ', '!', '"', '#', '$', '%', '&',
  '\'', '(', ')', '*',

  A(0x1a), A(0x1b), A(0x18), A(0x19),

  '/',

  0xdb,

  '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=',
  '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
  'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
  'X', 'Y', 'Z', '[', '\\', ']', '^', '_',

  A(0x04), 0xb1,

  'b', 'c', 'd', 'e',

  0xf8, 0xf1, 0xb0, A(0x0f), 0xd9, 0xbf, 0xda, 0xc0, 0xc5, 0x2d,
  0x2d, 0xc4, 0x2d, 0x5f, 0xc3, 0xb4, 0xc1, 0xc2, 0xb3, 0xf3,
  0xf2, 0xe3, 0xd8, 0x9c, 0xf9,

  A(127)
};
#endif

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: PDC_pixel_[x|y]
 *
 * Description:
 *   Convert row or column text position to framebuffer x/y positions
 *   in pixels.
 *
 ****************************************************************************/

static inline fb_coord_t PDC_pixel_x(FAR struct pdc_fbstate_s *fbstate,
                                     int col)
{
  return col * fbstate->fwidth + fbstate->hoffset;
}

static inline fb_coord_t PDC_pixel_y(FAR struct pdc_fbstate_s *fbstate,
                                     int row)
{
  return row * fbstate->fheight + fbstate->voffset;
}

/****************************************************************************
 * Name: PDC_fbmem_[x|y]
 *
 * Description:
 *   Convert row or column pixel position to framebuffer x/y byte offsets.
 *
 *   NOTE: For BPP={8,16,32} this function returns the exact positions.  For
 *   the case of BPP={1,2,4}, PDC_fbmem_x() aligns down to the address of
 *   the byte Aligns down to the byte that contains the 'col' pixel.
 *
 ****************************************************************************/

static inline uintptr_t PDC_fbmem_x(FAR struct pdc_fbstate_s *fbstate,
                                   int col)
{
  return (PDC_pixel_x(fbstate, col) * PDCURSES_BPP) >> 3;
}

static inline uintptr_t PDC_fbmem_y(FAR struct pdc_fbstate_s *fbstate,
                                    int row)
{
  return PDC_pixel_y(fbstate, row) * fbstate->stride;
}

/****************************************************************************
 * Name: PDC_color
 *
 * Description:
 *   Convert a pixel code to a RGB device pixel.
 *
 ****************************************************************************/

static inline pdc_color_t PDC_color(FAR struct pdc_fbstate_s *fbstate,
                                    short color)
{
#if defined (CONFIG_PDCURSES_COLORFMT_Y1)
  /* Returns 8 pixels packed into a byte */

  return (fbstate->greylevel[color] & 0xc0) == 0 ? 0x00 : 0xff;

#elif defined (CONFIG_PDCURSES_COLORFMT_Y2)
  uint8_t color8;

  /* Returns 4 pixels packed into a byte */

  color8 = fbstate->greylevel[color] >> 6;
  color8 = color8 << 2 | color8;
  color8 = color8 << 4 | color8;

  return color8;

#elif defined (CONFIG_PDCURSES_COLORFMT_Y4)
  uint8_t color8;

  /* Returns 2 pixels packed into a byte */

  color8 = fbstate->greylevel[color] >> 4;
  color8 = color8 << 4 | color8;

  return color8;

#elif defined(CONFIG_PDCURSES_COLORFMT_RGB332)
  /* Returns 8-bit RGB332 */

  return  RGBTO8(fbstate->rgbcolor[color].red,
                 fbstate->rgbcolor[color].green,
                 fbstate->rgbcolor[color].blue);

#elif defined(CONFIG_PDCURSES_COLORFMT_RGB565)
  /* Returns 16-bit RGB565 */

  return RGBTO16(fbstate->rgbcolor[color].red,
                 fbstate->rgbcolor[color].green,
                 fbstate->rgbcolor[color].blue);

#elif defined(CONFIG_PDCURSES_COLORFMT_RGB888)
  /* Returns 32-bit RGB888 */

  return RGBTO24(fbstate->rgbcolor[color].red,
                 fbstate->rgbcolor[color].green,
                 fbstate->rgbcolor[color].blue);

#else
#  error No color format selected
  return 0;
#endif
}

/****************************************************************************
 * Name: PDC_set_bg
 *
 * Description:
 *   Set memory to the device background RGB color.  For the case of BPP < 8,
 *   this is byte-aligned font buffer.  For other cases, this clears a patch
 *   of memory in the framebuffer.
 *
 ****************************************************************************/

#if PDCURSES_BPP < 8
static inline void PDC_set_bg(FAR struct pdc_fbstate_s *fbstate,
                              FAR uint8_t *fbuffer, short bg)
{
  uint8_t color8;
  int row;
  int col;

  /* Get a byte that packs multiple pixels into one byte */

  color8 = PDC_color(fbstate, bg);

  /* Now copy the color into the entire glyph region */

  for (row = 0; row < fbstate->fheight; row++, fbuffer += fbstate->fstride)
    {
      FAR uint8_t *fbdest = fbuffer;

      /* Note that there is no masking on the "right" side, so this will
       * set color in the unused bits in the final byte of the glyph row.
       * This should be harmless.
       */

      for (col = 0; col < fbstate->fwidth; col += PDCURSES_PPB)
        {
          *fbdest++ = color8;
        }
    }
}
#else
static inline void PDC_set_bg(FAR struct pdc_fbstate_s *fbstate,
                              FAR uint8_t *fbstart, short bg)
{
  pdc_color_t bgcolor = PDC_color(fbstate, bg);
  int row;
  int col;

  /* Set the glyph to the background color. */

  for (row = 0; row < fbstate->fheight; row++, fbstart += fbstate->stride)
    {
      FAR pdc_color_t *fbdest;

      for (col = 0, fbdest = (FAR pdc_color_t *)fbstart;
           col < fbstate->fwidth;
           col++)
        {
          *fbdest++ = bgcolor;
        }
    }
}
#endif

/****************************************************************************
 * Name: PDC_render_glyph
 *
 * Description:
 *   Render the font into the glyph memory using the foreground RGB color.
 *
 ****************************************************************************/

static inline void PDC_render_glyph(FAR struct pdc_fbstate_s *fbstate,
                                    FAR const struct nx_fontbitmap_s *fbm,
                                    FAR uint8_t *fbstart, short fg)
{
  pdc_color_t fgcolor = PDC_color(fbstate, fg);
  unsigned int stride;
  int ret;

  /* Are we rendering into the framebuffer or the font buffer.  The only
   * difference here is the stride value.
   */

#if PDCURSES_BPP < 8
  stride = fbstate->fstride;  /* Width of the font buffer in bytes */
#else
  stride = fbstate->stride;   /* Width of the framebuffer in bytes */
#endif

  /* Then render the glyph into the allocated memory
   *
   * REVISIT:  The case where visibility==1 is not yet handled.  In that
   * case, only the lower quarter of the glyph should be reversed.
   */

  ret = RENDERER((FAR pdc_color_t *)fbstart, fbstate->fheight,
                 fbstate->fwidth, stride, fbm, fgcolor);
  if (ret < 0)
    {
      /* Actually, the RENDERER never returns a failure */

      PDC_LOG(("ERROR:  RENDERER failed: %d\n", ret));
    }
}

/****************************************************************************
 * Name: PDC_copy_glyph
 *
 * Description:
 *   Copy the font from the the font buffer into the correct location in the
 *   the frame buffer.
 *
 *   For the case of pixel depth less then 1-byte, we will need to rend the
 *   font into a font buffer first, then copy it into the frame buffer at
 *   the correct position when the font is completely rendered.
 *
 ****************************************************************************/

#if PDCURSES_BPP < 8
static inline void  PDC_copy_glyph(FAR struct pdc_fbstate_s *fbstate,
                                   FAR uint8_t *dest, unsigned int xpos)
{
  FAR const uint8_t *srcrow;
  FAR const uint8_t *sptr;
  FAR uint8_t *destrow;
  FAR uint8_t *dptr;
  unsigned int srcend;
  unsigned int dststart;
  unsigned int dstend;
  unsigned int row;
  unsigned int npixels;
  uint8_t lshift;
  uint8_t rshift;
  uint8_t lmask;
  uint8_t rmask;
  int n;

  /* Handle the case where the first or last bytes may require read, modify,
   * write operations.
   *
   * Get the start and end column in pixels (relative to the start position).
   */

  dststart = PDC_pixel_x(fbstate, xpos) & PDCURSES_PPB_MASK;
  dstend   = dststart + fbstate->fwidth;

  /* The data in the font buffer is probably shifted with respect to the
   * byte position in the framebuffer.
   */

  lshift   = (PDCURSES_PPB - dststart) << PDCURSES_BPP_SHIFT;
  rshift   = (dstend & PDCURSES_PPB_MASK) << PDCURSES_BPP_SHIFT;

#ifdef CONFIG_NXFONTS_PACKEDMSFIRST
  /* Get the mask for pixels that are ordered so that they pack from the
   * MS byte down.
   */

  lmask    = 0xff << lshift;
  rmask    = 0xff >> rshift;
#else
# warning Unverified
  /* Get the mask for pixels that are ordered so that they pack from the
   * LS byte up.
   */

  lmask    = 0xff >> lshift;
  rmask    = 0xff << rshift;
#endif

  /* Fixups.  Range of lshift should be 0-7, not 1-8;  Make all masks 0x00
   * if no masking is needed. Convert dstend to a byte offset into the frame
   * buffer.
   */

  lshift  &= 7;
  rmask    = (rmask == 0xff) ? 0x00 : rmask;

  /* Convert dstend to a byte offset into the frame buffer.
   * Calculate srcend, the byte offset into the font buffer.
   */

  srcend   = (fbstate->fwidth - 1) >> PDCURSES_PPB_SHIFT;
  dstend   = (dstend - 1) >> PDCURSES_PPB_SHIFT;

  /* Then copy the image */

  for (row = 0, srcrow  = fbstate->fbuffer, destrow = dest;
       row < fbstate->fheight;
       row++, srcrow += fbstate->fstride, destrow += fbstate->stride)
    {
      /* Handle masking of the fractional initial byte */

      sptr    = srcrow;           /* Set to the beginning of the src row */
      dptr    = destrow;          /* Set to the beginning of the dest row */
      npixels = fbstate->fwidth;  /* Number of pixels to copy */

      /* Special case:  Only one byte will be transferred */

      if (dstend == 0)
        {
          uint8_t dmask = lmask | rmask;

          /* Perform the read/modify/write */

#ifdef CONFIG_NXFONTS_PACKEDMSFIRST
          *destrow = (*destrow & dmask) |
                     ((*srcrow >> (8 - lshift)) & ~rmask);
#else
#  warning Unverified
          *destrow = (*destrow & dmask) |
                     ((*srcrow << (8 - lshift)) & ~rmask);
#endif
          continue;
        }

      /* Handle masking of the left, leading byte */

      if (lmask != 0x00)
        {
          /* Perform the read/modify/write */

#ifdef CONFIG_NXFONTS_PACKEDMSFIRST
          *destrow = (*destrow & lmask) |
                     (*srcrow >> (8 - lshift));
#else
#  warning Unverified
          *destrow = (*destrow & lmask) |
                     (*srcrow << (8 - lshift));
#endif

          dptr++;            /* Skip to the next destination byte */
          npixels -= lshift; /* Decrement number of pixels to copy */
        }

      /* Handle masking of the fractional final byte */

      if (rmask != 0x00)
        {
          /* Perform the read/modify/write */

#ifdef CONFIG_NXFONTS_PACKEDMSFIRST
          destrow[dstend] = (destrow[dstend] & rmask) |
                            ((srcrow[srcend] << lshift) & ~rmask);
#else
#  warning Unverified
          destrow[dstend] = (destrow[dstend] & rmask) |
                            ((srcrow[srcend] >> lshift) & ~rmask);
#endif
          npixels -= rshift; /* Decrement number of pixels to copy */
        }

      /* Handle all of the unmasked bytes in-between */

      if (lshift > 0)
        {
#ifdef CONFIG_NXFONTS_PACKEDMSFIRST
          uint16_t shifted;

          shifted = (uint16_t)sptr[0] << (8 + lshift) |
                    (uint16_t)sptr[1] << lshift;
          sptr++;

          for (n = lshift; n < npixels; n += 8)
            {
              *dptr++ = (shifted >> 8);
              shifted = (shifted << 8) | (uint16_t)sptr[1] << lshift;
              sptr++;
            }
#else
#  error Missing logic
#endif
        }
      else
        {
          for (n = 0; n < npixels; n += 8)
            {
               *dptr++ = *sptr++;
            }
        }
    }
}
#endif

/****************************************************************************
 * Name: PDC_update
 *
 * Description:
 *   Update the LCD display is necessary.
 *
 ****************************************************************************/

#ifdef CONFIG_LCD_UPDATE
static void PDC_update(FAR struct pdc_fbstate_s *fbstate, int row, int col,
                       int nchars)
{
  struct nxgl_rect_s rect;
  int ret;

  if (nchars > 0)
    {
      /* Setup the bounding rectangle */

      rect.pt1.x = PDC_pixel_x(fbstate, col);
      rect.pt1.y = PDC_pixel_y(fbstate, row);
      rect.pt2.x = rect.pt1.x + nchars * fbstate->fwidth - 1;
      rect.pt2.y = rect.pt1.y + fbstate->fheight - 1;

      /* Then perfom the update via IOCTL */

      ret = ioctl(fbstate->fbfd, FBIO_UPDATE,
                  (unsigned long)((uintptr_t)&rect));
      if (ret < 0)
        {
          PDC_LOG(("ERROR:  ioctl(FBIO_UPDATE) failed: %d\n", errno));
        }
    }
}
#else
#  define PDC_update(f,r,c,n)
#endif

/****************************************************************************
 * Name: PDC_putc
 *
 * Description:
 *   Put one character with selected attributes at the selected drawing
 *   position.
 *
 ****************************************************************************/

static void PDC_putc(FAR struct pdc_fbstate_s *fbstate, int row, int col,
                     chtype ch)
{
  FAR const struct nx_fontbitmap_s *fbm;
  FAR uint8_t *dest;
  short fg;
  short bg;
#ifdef HAVE_BOLD_FONT
  bool bold = ((ch & A_BOLD) != 0);
#endif

  /* Clip */

  if (row < 0 || row >= SP->lines || col < 0 || col >= SP->cols)
    {
      PDC_LOG(("ERROR: Position out of range: row=%d col=%d\n", row, col));
      return;
    }

 /* Get the forground and background colors of the character */

 PDC_pair_content(PAIR_NUMBER(ch), &fg, &bg);

 /* Handle the A_REVERSE attribute. */

 if ((ch & A_REVERSE) != 0)
   {
     /* Swap the foreground and background colors if reversed */

     short tmp = fg;
     fg = bg;
     bg = tmp;
   }

#ifdef CONFIG_PDCURSES_CHTYPE_LONG
  /* Translate characters 0-127 via acs_map[], if they're flagged with
   * A_ALTCHARSET in the attribute portion of the chtype.
   */

  if (ch & A_ALTCHARSET && !(ch & 0xff80))
    {
      ch = (ch & (A_ATTRIBUTES ^ A_ALTCHARSET)) | acs_map[ch & 0x7f];
    }
#endif

#if PDCURSES_BPP < 8
  /* For the case of pixel depth less then 1-byte, we will need to rend the
   * font into a font buffer first, then copy it into the frame buffer at
   * the correct position when the font is completely rendered.
   */

  dest = fbstate->fbuffer;
#else
  /* Otherwise, we can rend directly into the frame buffer.  Calculate the
   * destination address in the framebuffer.
   */

  dest = (FAR uint8_t *)fbstate->fbmem +
                        PDC_fbmem_y(fbstate, row) +
                        PDC_fbmem_x(fbstate, col);
#endif

  /* Initialize the glyph to the (possibly reversed) background color */

  PDC_set_bg(fbstate, dest, bg);

  /* Does the code map to a font? */

#ifdef HAVE_BOLD_FONT
  fbm = nxf_getbitmap(bold ? fbstate->hfont : fbstate->hbold,
                      ch & A_CHARTEXT);
#else
  fbm = nxf_getbitmap(fbstate->hfont, ch & A_CHARTEXT);
#endif

  if (fbm != NULL)
    {
      /* Yes.. render the glyph */

      PDC_render_glyph(fbstate, fbm, dest, fg);
    }

  /* Apply more attributes */

  if ((ch & (A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE)) != 0)
    {
#warning Missing logic
    }

#if PDCURSES_BPP < 8
  /* Font is now completely rendered into the font buffer and can be copied
   * to the framebuffer.
   *
   * Calculate the destination address in the framebuffer.
   */

  dest = (FAR uint8_t *)fbstate->fbmem +
                        PDC_fbmem_y(fbstate, row) +
                        PDC_fbmem_x(fbstate, col);

  /* Then copy the glyph */

  PDC_copy_glyph(fbstate, dest, col);
#endif
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: PDC_gotoyx
 *
 * Description:
 *   Move the physical cursor (as opposed to the logical cursor affected by
 *   wmove()) to the given location. T his is called mainly from doupdate().
 *   In general, this function need not compare the old location with the
 *   new one, and should just move the cursor unconditionally.
 *
 ****************************************************************************/

void PDC_gotoyx(int row, int col)
{
  FAR struct pdc_fbscreen_s *fbscreen = (FAR struct pdc_fbscreen_s *)SP;
  FAR struct pdc_fbstate_s *fbstate;
  int oldrow;
  int oldcol;
  chtype ch;

  PDC_LOG(("PDC_gotoyx() - called: row %d col %d\n", row, col));

  DEBUGASSERT(fbscreen != NULL);
  fbstate = &fbscreen->fbstate;

  /* Clear the old cursor */

  oldrow = SP->cursrow;
  oldcol = SP->curscol;

  PDC_putc(fbstate, oldrow, oldcol, curscr->_y[oldrow][oldcol]);
  PDC_update(fbstate, oldrow, oldcol, 1);

  if (SP->visibility != 0)
    {
      /* Draw a new cursor by overprinting the existing character in
       * reverse.  NOTE: visibility {1, 2} are treated the same.
       */

      ch = curscr->_y[row][col] ^ A_REVERSE;
      PDC_putc(fbstate, row, col, ch);
      PDC_update(fbstate, row, col, 1);
    }
}

/****************************************************************************
 * Name: PDC_transform_line
 *
 * Description:
 *   The core output routine.  It takes len chtype entities from srcp (a
 *   pointer into curscr) and renders them to the physical screen at line
 *   lineno, column x.  It must also translate characters 0-127 via acs_map[],
 *   if they're flagged with A_ALTCHARSET in the attribute portion of the
 *   chtype.
 *
 ****************************************************************************/

void PDC_transform_line(int lineno, int x, int len, const chtype *srcp)
{
  FAR struct pdc_fbscreen_s *fbscreen = (FAR struct pdc_fbscreen_s *)SP;
  FAR struct pdc_fbstate_s *fbstate;
  int nextx;
  int i;

  PDC_LOG(("PDC_transform_line() - called: lineno=%d x=%d len=%d\n",
           lineno, x, len));

  DEBUGASSERT(fbscreen != NULL);
  fbstate = &fbscreen->fbstate;

  /* Add each character to the framebuffer at the current position,
   * incrementing the horizontal position after each character.
   */

  for (i = 0, nextx = x; i < len; i++, nextx++)
    {
      if (nextx >= SP->cols)
        {
          PDC_LOG(("ERROR:  Write past end of line\n"));
          break;
        }

      /* Render the font glyph into the framebuffer */

      PDC_putc(fbstate, lineno, nextx, srcp[i]);
    }

  PDC_update(fbstate, lineno, x, nextx - x);
}

/****************************************************************************
 * Name: PDC_clear_screen
 *
 * Description:
 *   Set the framebuffer content to a single color
 *
 ****************************************************************************/

void PDC_clear_screen(FAR struct pdc_fbstate_s *fbstate)
{
  FAR pdc_color_t *dest;
  FAR pdc_color_t bgcolor;
  FAR uint8_t *line;
  int width;
  int row;
  int col;

#ifdef CONFIG_LCD_UPDATE
  struct nxgl_rect_s rect;
  int ret;
#endif

  /* Get the background color and display width */

  bgcolor = PDCURSES_INIT_COLOR;      /* Background color for one pixel */
  width   = fbstate->xres;            /* Width in units of pixels */

#if PDCURSES_BPP < 8
  /* Pack multiple pixels into one byte.  Works for BPP={1,2,4} */

#if PDCURSES_BPP == 1                 /* BPP = 1 */
  bgcolor &= 1;                       /* Isolate 0 */
  bgcolor  = bgcolor << 1 | bgcolor;  /* Replicate 0 to 1 */
  bgcolor  = bgcolor << 2 | bgcolor;  /* Replicate 0-1 to 2-3 */
  bgcolor  = bgcolor << 4 | bgcolor;  /* Replicate 0-3 to 4-7 */
#elif PDCURSES_BPP == 2               /* BPP = 2 */
  bgcolor &= 3;                       /* Isolate 0-1 */
  bgcolor  = bgcolor << 2 | bgcolor;  /* Replicate 0-1 to 2-3 */
  bgcolor  = bgcolor << 4 | bgcolor;  /* Replicate 0-3 to 4-7 */
#else                                 /* BPP = 4 */
  bgcolor &= 15;                      /* Isolate 0-3 */
  bgcolor  = bgcolor << 4 | bgcolor;  /* Replicate 0-3 to 4-7 */
#endif

  /* Convert the width of the display to units of bytes. */

  width    = (width + PDCURSES_PPB - 1) / PDCURSES_PPB;
#endif

  /* Write the initial color into the entire framebuffer */

  for (row = 0, line = (FAR uint8_t *)fbstate->fbmem;
       row < fbstate->yres;
       row++, line += fbstate->stride)
    {
       for (col = 0, dest = (FAR pdc_color_t *)line;
            col < width;
            col++)
         {
           *dest++ = bgcolor;
         }
    }

#ifdef CONFIG_LCD_UPDATE
  /* Update the entire display */
  /* Setup the bounding rectangle */

  rect.pt1.x = 0;
  rect.pt1.y = 0;
  rect.pt2.x = fbstate->xres - 1;
  rect.pt2.y = fbstate->yres - 1;

  /* Then perfom the update via IOCTL */

  ret = ioctl(fbstate->fbfd, FBIO_UPDATE, (unsigned long)((uintptr_t)&rect));
  if (ret < 0)
    {
      PDC_LOG(("ERROR:  ioctl(FBIO_UPDATE) failed: %d\n", errno));
    }
#endif
}
