From d8c9ca184f29800f6c23d02bc450ea0e67981990 Mon Sep 17 00:00:00 2001 From: inmarket Date: Sat, 11 Mar 2017 14:39:53 +1000 Subject: [PATCH] Fix font baseline_x problem, decrease word-wrap stack usage, add text justification options --- changelog.txt | 7 ++ gfxconf.example.h | 2 + src/gdisp/gdisp.c | 197 ++++++++++++++++++++++---------------- src/gdisp/gdisp.h | 15 ++- src/gdisp/gdisp_driver.h | 4 + src/gdisp/gdisp_options.h | 18 ++++ 6 files changed, 160 insertions(+), 83 deletions(-) diff --git a/changelog.txt b/changelog.txt index e5cfb1a2..4a74a094 100644 --- a/changelog.txt +++ b/changelog.txt @@ -23,6 +23,13 @@ FEATURE: Significantly improved the FreeRTOS port FEATURE: Added support for operating system initialisation in FreeRTOS FEATURE: Added GFX_OS_CALL_UGFXMAIN configuration option to allow uGFXMain() to be automatically called FEATURE: Added GFX_OS_UGFXMAIN_STACKSIZE configuration option to control uGFXMain() stack size +FIX: Fixed where a font with more than 255 glyphs could fail to display some glyphs +FIX: Fixed where a font with a large x baseline could be incorrectly clipped or word-wrapped +IMPROVE: Significantly decrease the stack usage required for word-wrapping +FEATURE: Added justifyTop, justifyMiddle & justifyBottom text justification to GDISP +FEATURE: Added justifyWordWrap, justifyNoWordWrap text justification to GDISP (requires GDISP_NEED_TEXT_WORDWRAP) +FEATURE: Added justifyPad, justifyNoPad text justification to GDISP +FEATURE: Added GDISP_NEED_TEXT_BOXPADLR and GDISP_NEED_TEXT_BOXPADTB configuration options *** Release 2.7 *** diff --git a/gfxconf.example.h b/gfxconf.example.h index d4e34a5b..b278ed70 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -84,6 +84,8 @@ //#define GDISP_NEED_STREAMING FALSE //#define GDISP_NEED_TEXT FALSE // #define GDISP_NEED_TEXT_WORDWRAP FALSE +// #define GDISP_NEED_TEXT_BOXPADLR 1 +// #define GDISP_NEED_TEXT_BOXPADTB 1 // #define GDISP_NEED_ANTIALIAS FALSE // #define GDISP_NEED_UTF8 FALSE // #define GDISP_NEED_TEXT_KERNING FALSE diff --git a/src/gdisp/gdisp.c b/src/gdisp/gdisp.c index e1ee6951..3baef9de 100644 --- a/src/gdisp/gdisp.c +++ b/src/gdisp/gdisp.c @@ -20,17 +20,6 @@ #define GDISP_STARTUP_LOGO_TIMEOUT 0 #endif -// For internal use only. -#if GDISP_NEED_TEXT_WORDWRAP - typedef struct wrapParameters { - GDisplay* g; - coord_t x; - coord_t y; - font_t font; - justify_t justify; - } wrapParameters_t; -#endif - /*===========================================================================*/ /* Driver local variables. */ /*===========================================================================*/ @@ -3340,29 +3329,24 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co /* Callback to render string boxes with word wrap. */ #if GDISP_NEED_TEXT_WORDWRAP static bool mf_countline_callback(mf_str line, uint16_t count, void *state) { - uint16_t *linecount; (void) line; (void) count; - linecount = (uint16_t*)state; - (*linecount)++; - + ((coord_t*)state)[0]++; return TRUE; } static bool mf_drawline_callback(mf_str line, uint16_t count, void *state) { - wrapParameters_t* wrapParameters = (wrapParameters_t*)state; - - mf_render_aligned(wrapParameters->font, wrapParameters->x, wrapParameters->y, wrapParameters->justify, line, count, drawcharglyph, wrapParameters->g); - - wrapParameters->y += wrapParameters->font->line_height; + #define GD ((GDisplay *)state) + mf_render_aligned(GD->t.font, GD->t.wrapx, GD->t.wrapy, GD->t.lrj, line, count, drawcharglyph, state); + GD->t.wrapy += GD->t.font->line_height; + #undef GD return TRUE; } static bool mf_fillline_callback(mf_str line, uint16_t count, void *state) { - wrapParameters_t* wrapParameters = (wrapParameters_t*)state; - - mf_render_aligned(wrapParameters->font, wrapParameters->x, wrapParameters->y, wrapParameters->justify, line, count, fillcharglyph, wrapParameters->g); - - wrapParameters->y += wrapParameters->font->line_height; + #define GD ((GDisplay *)state) + mf_render_aligned(GD->t.font, GD->t.wrapx, GD->t.wrapy, GD->t.lrj, line, count, fillcharglyph, state); + GD->t.wrapy += GD->t.font->line_height; + #undef GD return TRUE; } #endif @@ -3411,7 +3395,7 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co g->t.font = font; g->t.clipx0 = x; g->t.clipy0 = y; - g->t.clipx1 = x + mf_get_string_width(font, str, 0, 0) + font->baseline_x; + g->t.clipx1 = 32768; //x + mf_get_string_width(font, str, 0, 0) + font->baseline_x; g->t.clipy1 = y + font->height; g->t.color = color; @@ -3444,24 +3428,55 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co } void gdispGDrawStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, justify_t justify) { - #if GDISP_NEED_TEXT_WORDWRAP - wrapParameters_t wrapParameters; - uint16_t nbrLines; - #endif + coord_t totalHeight; if (!font) return; MUTEX_ENTER(g); - g->t.font = font; + // Apply padding + #if GDISP_NEED_TEXT_BOXPADLR != 0 || GDISP_NEED_TEXT_BOXPADTB != 0 + if (!(justify & justifyNoPad)) { + #if GDISP_NEED_TEXT_BOXPADLR != 0 + x += GDISP_NEED_TEXT_BOXPADLR; + cx -= 2*GDISP_NEED_TEXT_BOXPADLR; + #endif + #if GDISP_NEED_TEXT_BOXPADTB != 0 + y += GDISP_NEED_TEXT_BOXPADTB; + cy -= 2*GDISP_NEED_TEXT_BOXPADTB; + #endif + } + #endif + + // Save the clipping area g->t.clipx0 = x; g->t.clipy0 = y; g->t.clipx1 = x+cx; g->t.clipy1 = y+cy; - g->t.color = color; - /* Select the anchor position */ - switch(justify) { + // Calculate the total text height + #if GDISP_NEED_TEXT_WORDWRAP + if (!(justify & justifyNoWordWrap)) { + // Count the number of lines + totalHeight = 0; + mf_wordwrap(font, cx, str, mf_countline_callback, &totalHeight); + totalHeight *= font->height; + } else + #endif + totalHeight = font->height; + + // Select the anchor position + switch((justify & JUSTIFYMASK_TOPBOTTOM)) { + case justifyTop: + break; + case justifyBottom: + y += cy - totalHeight; + break; + default: // justifyMiddle + y += (cy+1 - totalHeight)/2; + break; + } + switch((justify & JUSTIFYMASK_LEFTRIGHT)) { case justifyCenter: x += (cx + 1) / 2; break; @@ -3469,60 +3484,88 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co x += cx; break; default: // justifyLeft - x += font->baseline_x; break; } /* Render */ + g->t.font = font; + g->t.color = color; #if GDISP_NEED_TEXT_WORDWRAP - wrapParameters.x = x; - wrapParameters.y = y; - wrapParameters.font = font; - wrapParameters.justify = justify; - wrapParameters.g = g; - - // Count the number of lines - nbrLines = 0; - mf_wordwrap(font, cx, str, mf_countline_callback, &nbrLines); - wrapParameters.y += (cy+1 - nbrLines*font->height)/2; - - mf_wordwrap(font, cx, str, mf_drawline_callback, &wrapParameters); - #else - y += (cy+1 - font->height)/2; - mf_render_aligned(font, x, y, justify, str, 0, drawcharglyph, g); + if (!(justify & justifyNoWordWrap)) { + g->t.lrj = (justify & JUSTIFYMASK_LEFTRIGHT); + g->t.wrapx = x; + g->t.wrapy = y; + + mf_wordwrap(font, cx, str, mf_drawline_callback, g); + } else #endif + mf_render_aligned(font, x, y, (justify & JUSTIFYMASK_LEFTRIGHT), str, 0, drawcharglyph, g); autoflush(g); MUTEX_EXIT(g); } void gdispGFillStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, color_t bgcolor, justify_t justify) { - #if GDISP_NEED_TEXT_WORDWRAP - wrapParameters_t wrapParameters; - uint16_t nbrLines; - #endif + coord_t totalHeight; if (!font) return; MUTEX_ENTER(g); + g->p.x = x; + g->p.y = y; g->p.cx = cx; g->p.cy = cy; - g->t.font = font; - g->t.clipx0 = g->p.x = x; - g->t.clipy0 = g->p.y = y; - g->t.clipx1 = x+cx; - g->t.clipy1 = y+cy; - g->t.color = color; - g->t.bgcolor = g->p.color = bgcolor; TEST_CLIP_AREA(g) { // background fill + g->p.color = bgcolor; fillarea(g); - /* Select the anchor position */ - switch(justify) { + // Apply padding + #if GDISP_NEED_TEXT_BOXPADLR != 0 || GDISP_NEED_TEXT_BOXPADTB != 0 + if (!(justify & justifyNoPad)) { + #if GDISP_NEED_TEXT_BOXPADLR != 0 + x += GDISP_NEED_TEXT_BOXPADLR; + cx -= 2*GDISP_NEED_TEXT_BOXPADLR; + #endif + #if GDISP_NEED_TEXT_BOXPADTB != 0 + y += GDISP_NEED_TEXT_BOXPADTB; + cy -= 2*GDISP_NEED_TEXT_BOXPADTB; + #endif + } + #endif + + // Save the clipping area + g->t.clipx0 = x; + g->t.clipy0 = y; + g->t.clipx1 = x+cx; + g->t.clipy1 = y+cy; + + // Calculate the total text height + #if GDISP_NEED_TEXT_WORDWRAP + if (!(justify & justifyNoWordWrap)) { + // Count the number of lines + totalHeight = 0; + mf_wordwrap(font, cx, str, mf_countline_callback, &totalHeight); + totalHeight *= font->height; + } else + #endif + totalHeight = font->height; + + // Select the anchor position + switch((justify & JUSTIFYMASK_TOPBOTTOM)) { + case justifyTop: + break; + case justifyBottom: + y += cy - totalHeight; + break; + default: // justifyMiddle + y += (cy+1 - totalHeight)/2; + break; + } + switch((justify & JUSTIFYMASK_LEFTRIGHT)) { case justifyCenter: x += (cx + 1) / 2; break; @@ -3530,29 +3573,23 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co x += cx; break; default: // justifyLeft - x += font->baseline_x; break; } /* Render */ + g->t.font = font; + g->t.color = color; + g->t.bgcolor = bgcolor; #if GDISP_NEED_TEXT_WORDWRAP - wrapParameters.x = x; - wrapParameters.y = y; - wrapParameters.font = font; - wrapParameters.justify = justify; - wrapParameters.g = g; - - - // Count the number of lines - nbrLines = 0; - mf_wordwrap(font, cx, str, mf_countline_callback, &nbrLines); - wrapParameters.y += (cy+1 - nbrLines*font->height)/2; - - mf_wordwrap(font, cx, str, mf_fillline_callback, &wrapParameters); - #else - y += (cy+1 - font->height)/2; - mf_render_aligned(font, x, y, justify, str, 0, fillcharglyph, g); + if (!(justify & justifyNoWordWrap)) { + g->t.lrj = (justify & JUSTIFYMASK_LEFTRIGHT); + g->t.wrapx = x; + g->t.wrapy = y; + + mf_wordwrap(font, cx, str, mf_fillline_callback, g); + } else #endif + mf_render_aligned(font, x, y, (justify & JUSTIFYMASK_LEFTRIGHT), str, 0, fillcharglyph, g); } autoflush(g); diff --git a/src/gdisp/gdisp.h b/src/gdisp/gdisp.h index 706bc700..4a097604 100644 --- a/src/gdisp/gdisp.h +++ b/src/gdisp/gdisp.h @@ -58,10 +58,19 @@ typedef struct point { * @brief Type for the text justification. */ typedef enum justify { - justifyLeft = 0, /**< Justify Left */ - justifyCenter = 1, /**< Justify Center */ - justifyRight = 2 /**< Justify Right */ + justifyLeft = 0x00, /**< Justify Left (the default) */ + justifyCenter = 0x01, /**< Justify Center */ + justifyRight = 0x02, /**< Justify Right */ + justifyTop = 0x10, /**< Justify Top */ + justifyMiddle = 0x00, /**< Justify Middle (the default) */ + justifyBottom = 0x20, /**< Justify Bottom */ + justifyWordWrap = 0x00, /**< Word wrap (the default if GDISP_NEED_TEXT_WORDWRAP is on) */ + justifyNoWordWrap = 0x40, /**< No word wrap */ + justifyPad = 0x00, /**< Pad the text box (the default) */ + justifyNoPad = 0x04 /**< No padding the text box */ } justify_t; +#define JUSTIFYMASK_LEFTRIGHT (justifyLeft|justifyCenter|justifyRight) +#define JUSTIFYMASK_TOPBOTTOM (justifyTop|justifyMiddle|justifyBottom) /** * @enum fontmetric diff --git a/src/gdisp/gdisp_driver.h b/src/gdisp/gdisp_driver.h index 1912afd9..09a12b06 100644 --- a/src/gdisp/gdisp_driver.h +++ b/src/gdisp/gdisp_driver.h @@ -367,6 +367,10 @@ struct GDisplay { color_t bgcolor; coord_t clipx0, clipy0; coord_t clipx1, clipy1; + #if GDISP_NEED_TEXT_WORDWRAP + coord_t wrapx, wrapy; + justify_t lrj; + #endif } t; #endif #if GDISP_LINEBUF_SIZE != 0 && ((GDISP_NEED_SCROLL && !GDISP_HARDWARE_SCROLL) || (!GDISP_HARDWARE_STREAM_WRITE && GDISP_HARDWARE_BITFILLS)) diff --git a/src/gdisp/gdisp_options.h b/src/gdisp/gdisp_options.h index 91055a1c..f5f2c18b 100644 --- a/src/gdisp/gdisp_options.h +++ b/src/gdisp/gdisp_options.h @@ -574,6 +574,24 @@ #ifndef GDISP_NEED_TEXT_WORDWRAP #define GDISP_NEED_TEXT_WORDWRAP FALSE #endif + /** + * @brief Adding pixels to the left and right side of the box to pad text. + * @details Only has an effect with @p gdispGDrawStringBox() and @p gdispGFillStringBox() + * @note Can be turned off by using justifyNoPad + * @details Defaults to 1 + */ + #ifndef GDISP_NEED_TEXT_BOXPADLR + #define GDISP_NEED_TEXT_BOXPADLR 1 + #endif + /** + * @brief Adding pixels to the top and bottom side of the box to pad text. + * @details Only has an effect with @p gdispGDrawStringBox() and @p gdispGFillStringBox() + * @note Can be turned off by using justifyNoPad + * @details Defaults to 1 + */ + #ifndef GDISP_NEED_TEXT_BOXPADTB + #define GDISP_NEED_TEXT_BOXPADTB 1 + #endif /** * @brief Enable UTF-8 support for text rendering. * @details Defaults to FALSE