diff --git a/graphics/nxconsole/Make.defs b/graphics/nxconsole/Make.defs index 3b27853f25..e77f9450c0 100644 --- a/graphics/nxconsole/Make.defs +++ b/graphics/nxconsole/Make.defs @@ -34,5 +34,5 @@ ############################################################################ NXCON_ASRCS = -NXCON_CSRCS = nx_register.c nxcon_driver.c nxcon_register.c nxcon_unregister.c -NXCON_CSRCS += nxtk_register.c nxtool_register.c +NXCON_CSRCS = nx_register.c nxcon_driver.c nxcon_font.c nxcon_register.c +NXCON_CSRCS += nxcon_unregister.c nxtk_register.c nxtool_register.c diff --git a/graphics/nxconsole/nxcon_font.c b/graphics/nxconsole/nxcon_font.c new file mode 100644 index 0000000000..2152d8cbc5 --- /dev/null +++ b/graphics/nxconsole/nxcon_font.c @@ -0,0 +1,612 @@ +/**************************************************************************** + * nuttx/graphics/nxconsole/nxcon_font.c + * + * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "nxcon_internal.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/* Select renderer -- Some additional logic would be required to support + * pixel depths that are not directly addressable (1,2,4, and 24). + */ + +#if CONFIG_NXCONSOLE_BPP == 1 +# define RENDERER nxf_convert_1bpp +#elif CONFIG_NXCONSOLE_BPP == 2 +# define RENDERER nxf_convert_2bpp +#elif CONFIG_NXCONSOLE_BPP == 4 +# define RENDERER nxf_convert_4bpp +#elif CONFIG_NXCONSOLE_BPP == 8 +# define RENDERER nxf_convert_8bpp +#elif CONFIG_NXCONSOLE_BPP == 16 +# define RENDERER nxf_convert_16bpp +#elif CONFIG_NXCONSOLE_BPP == 24 +# define RENDERER nxf_convert_24bpp +#elif CONFIG_NXCONSOLE_BPP == 32 +# define RENDERER nxf_convert_32bpp +#else +# error "Unsupported CONFIG_NXCONSOLE_BPP" +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxcon_freeglyph + ****************************************************************************/ + +static void nxcon_freeglyph(FAR struct nxcon_glyph_s *glyph) +{ + if (glyph->bitmap) + { + free(glyph->bitmap); + } + memset(glyph, 0, sizeof(struct nxcon_glyph_s)); +} + +/**************************************************************************** + * Name: nxcon_allocglyph + ****************************************************************************/ + +static inline FAR struct nxcon_glyph_s * +nxcon_allocglyph(FAR struct nxcon_state_s *priv) +{ +#ifdef CONFIG_NXCONSOLE_FONTCACHE + FAR struct nxcon_glyph_s *glyph = NULL; + FAR struct nxcon_glyph_s *luglyph = NULL; + uint8_t luusecnt; + int i; + + /* Search through the glyph cache looking for an unused glyph. Also, keep + * track of the least used glyph as well. We need that if we have to replace + * a glyph in the cache. + */ + + for (i = 0; i < priv->maxglyphs; i++) + { + /* Is this glyph in use? */ + + glyph = &priv->glyph[i]; + if (!glyph->usecnt) + { + /* No.. return this glyph with a use count of one */ + + glyph->usecnt = 1; + return glyph; + } + + /* Yes.. check for the least recently used */ + + if (!luglyph || glyph->usecnt < luglyph->usecnt) + { + luglyph = glyph; + } + } + + /* If we get here, the glyph cache is full. We replace the least used + * glyph with the one we need now. (luglyph can't be NULL). + */ + + luusecnt = luglyph->usecnt; + nxcon_freeglyph(luglyph); + + /* But lets decrement all of the usecnts so that the new one one be so + * far behind in the counts as the older ones. + */ + + if (luusecnt > 1) + { + uint8_t decr = luusecnt - 1; + + for (i = 0; i < priv->maxglyphs; i++) + { + /* Is this glyph in use? */ + + glyph = &priv->glyph[i]; + if (glyph->usecnt > decr) + { + glyph->usecnt -= decr; + } + } + } + + /* Then return the least used glyph */ + + luglyph->usecnt = 1; + return luglyph; +#else + return &priv->glyph; +#endif +} + +/**************************************************************************** + * Name: nxcon_findglyph + ****************************************************************************/ + +#ifdef CONFIG_NXCONSOLE_FONTCACHE +static FAR struct nxcon_glyph_s * +nxcon_findglyph(FAR struct nxcon_state_s *priv, uint8_t ch) +{ + int i; + + /* First, try to find the glyph in the cache of pre-rendered glyphs */ + + for (i = 0; i < priv->maxglyphs; i++) + { + FAR struct nxcon_glyph_s *glyph = &priv->glyph[i]; + if (glyph->usecnt > 0 && glyph->code == ch) + { + /* Increment the use count (unless it is already at the max) */ + + if (glyph->usecnt < MAX_USECNT) + { + glyph->usecnt++; + } + + /* And return the glyph that we found */ + + return glyph; + } + } + return NULL; +} +#endif + +/**************************************************************************** + * Name: nxcon_renderglyph + ****************************************************************************/ + +static inline FAR struct nxcon_glyph_s * +nxcon_renderglyph(FAR struct nxcon_state_s *priv, + FAR const struct nx_fontbitmap_s *fbm, uint8_t ch) +{ + FAR struct nxcon_glyph_s *glyph = NULL; + FAR nxgl_mxpixel_t *ptr; +#if CONFIG_NXCONSOLE_BPP < 8 + nxgl_mxpixel_t pixel; +#endif + int bmsize; + int row; + int col; + int ret; + + /* Make sure that there is room for another glyph */ + + gvdbg("ch=%c [%02x]\n", isprint(ch) ? ch : '.', ch); + + /* Allocate the glyph (always succeeds) */ + + glyph = nxcon_allocglyph(priv); + glyph->code = ch; + + /* Get the dimensions of the glyph */ + + glyph->width = fbm->metric.width + fbm->metric.xoffset; + glyph->height = fbm->metric.height + fbm->metric.yoffset; + + /* Allocate memory to hold the glyph with its offsets */ + + glyph->stride = (glyph->width * CONFIG_NXCONSOLE_BPP + 7) / 8; + bmsize = glyph->stride * glyph->height; + glyph->bitmap = (FAR uint8_t *)malloc(bmsize); + + if (glyph->bitmap) + { + /* Initialize the glyph memory to the background color */ + +#if CONFIG_NXCONSOLE_BPP < 8 + pixel = priv->wcolor[0]; +# if CONFIG_NXCONSOLE_BPP == 1 + /* Pack 1-bit pixels into a 2-bits */ + + pixel &= 0x01; + pixel = (pixel) << 1 |pixel; +# endif +# if CONFIG_NXCONSOLE_BPP < 4 + /* Pack 2-bit pixels into a nibble */ + + pixel &= 0x03; + pixel = (pixel) << 2 |pixel; +# endif + + /* Pack 4-bit nibbles into a byte */ + + pixel &= 0x0f; + pixel = (pixel) << 4 | pixel; + + ptr = (FAR nxgl_mxpixel_t *)glyph->bitmap; + for (row = 0; row < glyph->height; row++) + { + for (col = 0; col < glyph->stride; col++) + { + /* Transfer the packed bytes into the buffer */ + + *ptr++ = pixel; + } + } + +#elif CONFIG_NXCONSOLE_BPP == 24 +# error "Additional logic is needed here for 24bpp support" + +#else /* CONFIG_NXCONSOLE_BPP = {8,16,32} */ + + ptr = (FAR nxgl_mxpixel_t *)glyph->bitmap; + for (row = 0; row < glyph->height; row++) + { + /* Just copy the color value into the glyph memory */ + + for (col = 0; col < glyph->width; col++) + { + *ptr++ = priv->wndo.wcolor[0]; + } + } +#endif + + /* Then render the glyph into the allocated memory */ + + ret = RENDERER((FAR nxgl_mxpixel_t*)glyph->bitmap, + glyph->height, glyph->width, glyph->stride, + fbm, priv->wndo.fcolor[0]); + if (ret < 0) + { + /* Actually, the RENDERER never returns a failure */ + + gdbg("nxcon_renderglyph: RENDERER failed\n"); + nxcon_freeglyph(glyph); + glyph = NULL; + } + } + + return glyph; +} + +/**************************************************************************** + * Name: nxcon_fontsize + ****************************************************************************/ + +static int nxcon_fontsize(NXHANDLE hfont, uint8_t ch, FAR struct nxgl_size_s *size) +{ + FAR const struct nx_fontbitmap_s *fbm; + + /* No, it is not cached... Does the code map to a font? */ + + fbm = nxf_getbitmap(hfont, ch); + if (fbm) + { + /* Yes.. return the font size */ + + size->w = fbm->metric.width + fbm->metric.xoffset; + size->h = fbm->metric.height + fbm->metric.yoffset; + return OK; + } + + return ERROR; +} + +/**************************************************************************** + * Name: nxcon_getglyph + ****************************************************************************/ + +static FAR struct nxcon_glyph_s * +nxcon_getglyph(NXHANDLE hfont, FAR struct nxcon_state_s *priv, uint8_t ch) +{ + FAR struct nxcon_glyph_s *glyph; + FAR const struct nx_fontbitmap_s *fbm; + + /* First, try to find the glyph in the cache of pre-rendered glyphs */ + +#ifdef CONFIG_NXCONSOLE_FONTCACHE + glyph = nxcon_findglyph(priv, ch); + if (glyph) + { + /* We found it in the cache .. return the cached glyph */ + + return glyph; + } +#else + glyph = NULL; +#endif + + /* No, it is not cached... Does the code map to a font? */ + + fbm = nxf_getbitmap(hfont, ch); + if (fbm) + { + /* Yes.. render the glyph */ + + glyph = nxcon_renderglyph(priv, fbm, ch); + } + + return glyph; +} + +/**************************************************************************** + * Name: nxcon_addchar + * + * Description: + * This is part of the nxcon_putc logic. It creates and positions a + * the character and renders (or re-uses) a glyph for font. + * + ****************************************************************************/ + +static FAR const struct nxcon_bitmap_s * +nxcon_addchar(NXHANDLE hfont, FAR struct nxcon_state_s *priv, uint8_t ch) +{ + FAR struct nxcon_bitmap_s *bm = NULL; + FAR struct nxcon_glyph_s *glyph; + + /* Is there space for another character on the display? */ + + if (priv->nchars < priv->maxchars) + { + /* Yes, setup the bitmap information */ + + bm = &priv->bm[priv->nchars]; + bm->code = ch; + bm->flags = 0; + bm->pos.x = priv->fpos.x; + bm->pos.y = priv->fpos.y; + + /* Find (or create) the matching glyph */ + + glyph = nxcon_getglyph(hfont, priv, ch); + if (!glyph) + { + /* No, there is no font for this code. Just mark this as a space. */ + + bm->flags |= BMFLAGS_NOGLYPH; + + /* Set up the next character position */ + + priv->fpos.x += priv->spwidth; + } + else + { + /* Set up the next character position */ + + priv->fpos.x += glyph->width; + } + + /* Success.. increment nchars to retain this character */ + + priv->nchars++; + } + + return bm; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxcon_home + * + * Description: + * Set the next character position to the top-left corner of the display. + * + ****************************************************************************/ + +void nxcon_home(FAR struct nxcon_state_s *priv) +{ + /* The first character is one space from the left */ + + priv->fpos.x = priv->spwidth; + + /* And CONFIG_NXCONSOLE_LINESEPARATION lines from the top */ + + priv->fpos.y = CONFIG_NXCONSOLE_LINESEPARATION; +} + +/**************************************************************************** + * Name: nxcon_newline + * + * Description: + * Set the next character position to the beginning of the next line. + * + ****************************************************************************/ + +void nxcon_newline(FAR struct nxcon_state_s *priv) +{ + /* Carriage return: The first character is one space from the left */ + + priv->fpos.x = priv->spwidth; + + /* Linefeed: Down the max font height + CONFIG_NXCONSOLE_LINESEPARATION */ + + priv->fpos.y += (priv->fheight + CONFIG_NXCONSOLE_LINESEPARATION); +} + +/**************************************************************************** + * Name: nxcon_putc + * + * Description: + * Render the specified character at the current display position. + * + ****************************************************************************/ + +void nxcon_putc(FAR struct nxcon_state_s *priv, uint8_t ch) +{ + FAR const struct nxcon_bitmap_s *bm; + + /* If it is a newline character, then just perform the logical newline + * operation. + */ + + if (ch == '\n') + { + nxcon_newline(priv); + } + + /* Otherwise, find the glyph associated with the character and render it + * onto the display. + */ + + else + { + bm = nxcon_addchar(priv->font, priv, ch); + if (bm) + { + nxcon_fillchar(priv, NULL, bm); + } + } +} + +/**************************************************************************** + * Name: nxcon_fillchar + * + * Description: + * This implements the character display. It is part of the nxcon_putc + * operation but may also be used when redrawing an existing display. + * + ****************************************************************************/ + +void nxcon_fillchar(FAR struct nxcon_state_s *priv, + FAR const struct nxgl_rect_s *rect, + FAR const struct nxcon_bitmap_s *bm) +{ + FAR struct nxcon_glyph_s *glyph; + struct nxgl_rect_s bounds; + struct nxgl_rect_s intersection; + struct nxgl_size_s fsize; + int ret; + + /* Handle the special case of spaces which have no glyph bitmap */ + + if (BM_ISSPACE(bm)) + { + return; + } + + /* Get the size of the font glyph (which may not have been created yet) */ + + ret = nxcon_fontsize(priv->font, bm->code, &fsize); + if (ret < 0) + { + /* This would mean that there is no bitmap for the character code and + * that the font would be rendered as a space. But this case should + * never happen here because the BM_ISSPACE() should have already + * found all such cases. + */ + + return; + } + + /* Construct a bounding box for the glyph */ + + bounds.pt1.x = bm->pos.x; + bounds.pt1.y = bm->pos.y; + bounds.pt2.x = bm->pos.x + fsize.w - 1; + bounds.pt2.y = bm->pos.y + fsize.h - 1; + + /* Should this also be clipped to a region in the window? */ + + if (rect) + { + /* Get the intersection of the redraw region and the character bitmap */ + + nxgl_rectintersect(&intersection, rect, &bounds); + } + else + { + /* The intersection is the whole glyph */ + + nxgl_rectcopy(&intersection, &bounds); + } + + /* Check for empty intersections */ + + if (!nxgl_nullrect(&intersection)) + { + FAR const void *src; + + /* Find (or create) the glyph that goes with this font */ + + glyph = nxcon_getglyph(priv->font, priv, bm->code); + if (!glyph) + { + /* Shouldn't happen */ + + return; + } + + /* Blit the font bitmap into the window */ + + src = (FAR const void *)glyph->bitmap; + ret = priv->ops->bitmap(priv, &intersection, &src, + &bm->pos, (unsigned int)glyph->stride); + if (ret < 0) + { + gdbg("bitmap failed: %d\n", errno); + } + } +} + diff --git a/graphics/nxconsole/nxcon_internal.h b/graphics/nxconsole/nxcon_internal.h index ea2e8c7476..590c169e69 100644 --- a/graphics/nxconsole/nxcon_internal.h +++ b/graphics/nxconsole/nxcon_internal.h @@ -56,17 +56,21 @@ * Definitions ****************************************************************************/ /* Configuration ************************************************************/ -/* Font cache */ +/* The maximum number of characters that can be remembered */ + +#ifndef CONFIG_NXCONSOLE_BMCACHE +# define CONFIG_NXCONSOLE_BMCACHE 128 +#endif + +/* Font cache -- this is the number or pre-rendered font glyphs that can be + * remembered. + */ #ifdef CONFIG_NXCONSOLE_FONTCACHE -# ifndef CONFIG_NXCONSOLE_BMCACHE -# define CONFIG_NXCONSOLE_BMCACHE 128 -# endif # ifndef CONFIG_NXCONSOLE_GLCACHE # define CONFIG_NXCONSOLE_GLCACHE 16 # endif #else -# undef CONFIG_NXCONSOLE_BMCACHE # undef CONFIG_NXCONSOLE_GLCACHE #endif @@ -147,10 +151,8 @@ struct nxcon_state_s sem_t exclsem; /* Forces mutually exclusive access */ struct nxgl_point_s fpos; /* Next display position */ -#ifdef CONFIG_NXCONSOLE_FONTCACHE uint16_t maxchars; /* Size of the bm[] array */ uint16_t nchars; /* Number of chars in the bm[] array */ -#endif uint8_t minor; /* Device minor number */ uint8_t fheight; /* Max height of a font in pixels */ @@ -158,11 +160,20 @@ struct nxcon_state_s uint8_t spwidth; /* The width of a space */ #ifdef CONFIG_NXCONSOLE_FONTCACHE uint8_t maxglyphs; /* Size of the glyph[] array */ +#endif /* Font cache data storage */ struct nxcon_bitmap_s bm[CONFIG_NXCONSOLE_BMCACHE]; + + /* Glyph cache data storage */ + +#ifdef CONFIG_NXCONSOLE_FONTCACHE struct nxcon_glyph_s glyph[CONFIG_NXCONSOLE_GLCACHE]; +#else + /* A glyph cache of size one -- all fonts will be re-rendered on each use */ + + struct nxcon_glyph_s glyph; #endif }; @@ -190,6 +201,9 @@ void nxcon_newline(FAR struct nxcon_state_s *priv); void nxcon_putc(FAR struct nxcon_state_s *priv, uint8_t ch); void nxcon_fillchar(FAR struct nxcon_state_s *priv, FAR const struct nxgl_rect_s *rect, FAR const struct nxcon_bitmap_s *bm); + +/* Scrolling support */ + void nxcon_scroll(FAR struct nxcon_state_s *priv, int scrollheight); #endif /* __GRAPHICS_NXCONSOLE_NXCON_INTERNAL_H */ diff --git a/graphics/nxconsole/nxcon_register.c b/graphics/nxconsole/nxcon_register.c index 7fd9310c81..9ed42c74de 100644 --- a/graphics/nxconsole/nxcon_register.c +++ b/graphics/nxconsole/nxcon_register.c @@ -122,8 +122,8 @@ FAR struct nxcon_state_s * /* Set up the text caches */ -#ifdef CONFIG_NXCONSOLE_FONTCACHE priv->maxchars = CONFIG_NXCONSOLE_BMCACHE; +#ifdef CONFIG_NXCONSOLE_FONTCACHE priv->maxglyphs = CONFIG_NXCONSOLE_GLCACHE; #endif diff --git a/include/nuttx/nx/nxconsole.h b/include/nuttx/nx/nxconsole.h index 4c0fdc4e6a..ce494bc39b 100644 --- a/include/nuttx/nx/nxconsole.h +++ b/include/nuttx/nx/nxconsole.h @@ -93,7 +93,9 @@ extern "C" { * Input Parameters: * hwnd - A handle that will be used to access the window. The window must * persist and this handle must be valid for the life of the NX console. - * wndo - Describes the window and font to be used + * wndo - Describes the window and font to be used. The information in + * this structure is copied and the original need not persist after + * nxtool_register() returns. * minor - The device minor number * * Return: @@ -114,7 +116,9 @@ EXTERN NXCONSOLE nx_register(NXWINDOW hwnd, FAR struct nxcon_window_s *wndo, * Input Parameters: * hfwnd - A handle that will be used to access the window. The window must * persist and this handle must be valid for the life of the NX console. - * wndo - Describes the window and font to be used + * wndo - Describes the window and font to be used. The information in + * this structure is copied and the original need not persist after + * nxtool_register() returns. * minor - The device minor number * * Return: @@ -137,7 +141,9 @@ EXTERN NXCONSOLE nxtk_register(NXTKWINDOW hfwnd, * hfwnd - A handle that will be used to access the toolbar. The toolbar * must persist and this handle must be valid for the life of the NX * console. - * wndo - Describes the window and font to be used + * wndo - Describes the window and font to be used. The information in + * this structure is copied and the original need not persist after + * nxtool_register() returns. * minor - The device minor number * * Return: