aboutsummaryrefslogtreecommitdiffstats
path: root/src/gdisp/mcufont
diff options
context:
space:
mode:
authorinmarket <andrewh@inmarket.com.au>2013-07-28 17:08:45 +1000
committerinmarket <andrewh@inmarket.com.au>2013-07-28 17:08:45 +1000
commit3977ee687ffff23e49dcac0ea9a7c3e8652248f0 (patch)
treec5be0359998987d29b6be213413c896fe4d6b07f /src/gdisp/mcufont
parentf84bc2a3f6b82b0f05319fd7c609f8b30929d788 (diff)
downloaduGFX-3977ee687ffff23e49dcac0ea9a7c3e8652248f0.tar.gz
uGFX-3977ee687ffff23e49dcac0ea9a7c3e8652248f0.tar.bz2
uGFX-3977ee687ffff23e49dcac0ea9a7c3e8652248f0.zip
First cut - beautiful new font handling by PetteriAimonen
Diffstat (limited to 'src/gdisp/mcufont')
-rw-r--r--src/gdisp/mcufont/mcufont.h14
-rw-r--r--src/gdisp/mcufont/mcufont.mk19
-rw-r--r--src/gdisp/mcufont/mf_bwfont.c134
-rw-r--r--src/gdisp/mcufont/mf_bwfont.h77
-rw-r--r--src/gdisp/mcufont/mf_config.h148
-rw-r--r--src/gdisp/mcufont/mf_encoding.c69
-rw-r--r--src/gdisp/mcufont/mf_encoding.h53
-rw-r--r--src/gdisp/mcufont/mf_font.c77
-rw-r--r--src/gdisp/mcufont/mf_font.h118
-rw-r--r--src/gdisp/mcufont/mf_justify.c321
-rw-r--r--src/gdisp/mcufont/mf_justify.h74
-rw-r--r--src/gdisp/mcufont/mf_kerning.c118
-rw-r--r--src/gdisp/mcufont/mf_kerning.h29
-rw-r--r--src/gdisp/mcufont/mf_rlefont.c282
-rw-r--r--src/gdisp/mcufont/mf_rlefont.h74
-rw-r--r--src/gdisp/mcufont/mf_scaledfont.c83
-rw-r--r--src/gdisp/mcufont/mf_scaledfont.h23
-rw-r--r--src/gdisp/mcufont/mf_wordwrap.c290
-rw-r--r--src/gdisp/mcufont/mf_wordwrap.h32
19 files changed, 2035 insertions, 0 deletions
diff --git a/src/gdisp/mcufont/mcufont.h b/src/gdisp/mcufont/mcufont.h
new file mode 100644
index 00000000..8a81ff42
--- /dev/null
+++ b/src/gdisp/mcufont/mcufont.h
@@ -0,0 +1,14 @@
+/* Tiny library for rendering compressed bitmap fonts on microcontrollers. */
+
+#ifndef _MCUFONT_H_
+#define _MCUFONT_H_
+
+#include "mf_config.h"
+#include "mf_encoding.h"
+#include "mf_justify.h"
+#include "mf_kerning.h"
+#include "mf_rlefont.h"
+#include "mf_scaledfont.h"
+#include "mf_wordwrap.h"
+
+#endif
diff --git a/src/gdisp/mcufont/mcufont.mk b/src/gdisp/mcufont/mcufont.mk
new file mode 100644
index 00000000..0b402877
--- /dev/null
+++ b/src/gdisp/mcufont/mcufont.mk
@@ -0,0 +1,19 @@
+# Makefile fragment listing the source files for the mcufont decoder.
+
+# Directory where the decoder source code recides
+# Usually you'll want to set this in your own Makefile.
+MFDIR ?= mcufont/decoder
+
+# Name of the include directory
+MFINC = $(MFDIR)
+
+# Source code files to include
+MFSRC = \
+ $(MFDIR)/mf_encoding.c \
+ $(MFDIR)/mf_font.c \
+ $(MFDIR)/mf_justify.c \
+ $(MFDIR)/mf_kerning.c \
+ $(MFDIR)/mf_rlefont.c \
+ $(MFDIR)/mf_bwfont.c \
+ $(MFDIR)/mf_scaledfont.c \
+ $(MFDIR)/mf_wordwrap.c
diff --git a/src/gdisp/mcufont/mf_bwfont.c b/src/gdisp/mcufont/mf_bwfont.c
new file mode 100644
index 00000000..918fbb29
--- /dev/null
+++ b/src/gdisp/mcufont/mf_bwfont.c
@@ -0,0 +1,134 @@
+#include "mf_bwfont.h"
+#include <stdbool.h>
+
+/* Find the character range and index that contains a given glyph.. */
+static const struct mf_bwfont_char_range_s *find_char_range(
+ const struct mf_bwfont_s *font, uint16_t character, uint16_t *index_ret)
+{
+ unsigned i, index;
+ const struct mf_bwfont_char_range_s *range;
+ for (i = 0; i < font->char_range_count; i++)
+ {
+ range = &font->char_ranges[i];
+ index = character - range->first_char;
+ if (character >= range->first_char && index < range->char_count)
+ {
+ *index_ret = index;
+ return range;
+ }
+ }
+
+ return 0;
+}
+
+static uint8_t get_width(const struct mf_bwfont_char_range_s *r, uint16_t index)
+{
+ if (r->width)
+ {
+ return r->width + r->offset_x;
+ }
+ else
+ {
+ return r->glyph_widths[index];
+ }
+}
+
+static uint8_t render_char(const struct mf_bwfont_char_range_s *r,
+ int16_t x0, int16_t y0, uint16_t index,
+ mf_pixel_callback_t callback,
+ void *state)
+{
+ const uint8_t *data, *p;
+ uint8_t stride, runlen;
+ uint8_t x, y, height, num_cols;
+ uint8_t bit, byte, mask;
+ bool oldstate, newstate;
+
+ if (r->width)
+ {
+ data = r->glyph_data + r->width * index * r->height_bytes;
+ num_cols = r->width;
+ }
+ else
+ {
+ data = r->glyph_data + r->glyph_offsets[index] * r->height_bytes;
+ num_cols = r->glyph_offsets[index + 1] - r->glyph_offsets[index];
+ }
+
+ stride = r->height_bytes;
+ height = r->height_pixels;
+ y0 += r->offset_y;
+ x0 += r->offset_x;
+ bit = 0;
+ byte = 0;
+
+ for (y = 0; y < height; y++)
+ {
+ mask = (1 << bit);
+
+ oldstate = false;
+ runlen = 0;
+ p = data + byte;
+ for (x = 0; x < num_cols; x++, p += stride)
+ {
+ newstate = *p & mask;
+ if (newstate != oldstate)
+ {
+ if (oldstate && runlen)
+ {
+ callback(x0 + x - runlen, y0 + y, runlen, 255, state);
+ }
+
+ oldstate = newstate;
+ runlen = 0;
+ }
+
+ runlen++;
+ }
+
+ if (oldstate && runlen)
+ {
+ callback(x0 + x - runlen, y0 + y, runlen, 255, state);
+ }
+
+ bit++;
+ if (bit > 7)
+ {
+ bit = 0;
+ byte++;
+ }
+ }
+
+ return get_width(r, index);
+}
+
+uint8_t mf_bwfont_render_character(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ uint16_t character,
+ mf_pixel_callback_t callback,
+ void *state)
+{
+ const struct mf_bwfont_s *bwfont = (const struct mf_bwfont_s*)font;
+ const struct mf_bwfont_char_range_s *range;
+ uint16_t index;
+
+ range = find_char_range(bwfont, character, &index);
+ if (!range)
+ return 0;
+
+ return render_char(range, x0, y0, index, callback, state);
+}
+
+uint8_t mf_bwfont_character_width(const struct mf_font_s *font,
+ uint16_t character)
+{
+ const struct mf_bwfont_s *bwfont = (const struct mf_bwfont_s*)font;
+ const struct mf_bwfont_char_range_s *range;
+ uint16_t index;
+
+ range = find_char_range(bwfont, character, &index);
+ if (!range)
+ return 0;
+
+ return get_width(range, index);
+}
diff --git a/src/gdisp/mcufont/mf_bwfont.h b/src/gdisp/mcufont/mf_bwfont.h
new file mode 100644
index 00000000..9aea2ac9
--- /dev/null
+++ b/src/gdisp/mcufont/mf_bwfont.h
@@ -0,0 +1,77 @@
+/* Uncompressed font format for storing black & white fonts. Very efficient
+ * to decode and works well for small font sizes.
+ */
+
+#ifndef _MF_BWFONT_H_
+#define _MF_BWFONT_H_
+
+#include "mf_font.h"
+
+/* Versions of the BW font format that are supported. */
+#define MF_BWFONT_VERSION_4_SUPPORTED 1
+
+/* Structure for a range of characters. */
+struct mf_bwfont_char_range_s
+{
+ /* The number of the first character in this range. */
+ uint16_t first_char;
+
+ /* The total count of characters in this range. */
+ uint16_t char_count;
+
+ /* The left and top skips of the characters in this range.
+ * This is the number of empty rows at left and at top. */
+ uint8_t offset_x;
+ uint8_t offset_y;
+
+ /* Column height for glyphs in this range, in bytes and pixels. */
+ uint8_t height_bytes;
+ uint8_t height_pixels;
+
+ /* Positive value if the width of all glyphs in this range is the
+ * same, or zero if it is not. */
+ uint8_t width;
+
+ /* Lookup table for the character widths. NULL if width is specified. */
+ const uint8_t *glyph_widths;
+
+ /* Lookup table for the character offsets. Multiply by height_bytes
+ * to get the byte offset. Also allows lookup of the number of columns.
+ * NULL if width is specified. */
+ const uint16_t *glyph_offsets;
+
+ /* Table for the glyph data.
+ * The data for each glyph is column-by-column, with N bytes per each
+ * column. The LSB of the first byte is the top left pixel.
+ */
+ const uint8_t *glyph_data;
+};
+
+/* Structure for the font */
+struct mf_bwfont_s
+{
+ struct mf_font_s font;
+
+ /* Version of the font format. */
+ const uint8_t version;
+
+ /* Number of character ranges. */
+ const uint8_t char_range_count;
+
+ /* Array of the character ranges */
+ const struct mf_bwfont_char_range_s *char_ranges;
+};
+
+#ifdef MF_BWFONT_INTERNALS
+/* Internal functions, don't use these directly. */
+MF_EXTERN uint8_t mf_bwfont_render_character(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ mf_char character,
+ mf_pixel_callback_t callback,
+ void *state);
+
+MF_EXTERN uint8_t mf_bwfont_character_width(const struct mf_font_s *font,
+ mf_char character);
+#endif
+
+#endif
diff --git a/src/gdisp/mcufont/mf_config.h b/src/gdisp/mcufont/mf_config.h
new file mode 100644
index 00000000..7e58c8f6
--- /dev/null
+++ b/src/gdisp/mcufont/mf_config.h
@@ -0,0 +1,148 @@
+/* Configuration constants for mcufont. */
+
+#ifndef _MF_CONFIG_H_
+#define _MF_CONFIG_H_
+
+#include <gfx.h>
+
+/* Mapping from uGFX settings to mcufont settings */
+#if GDISP_NEED_UTF8
+#define MF_ENCODING MF_ENCODING_UTF8
+#else
+#define MF_ENCODING MF_ENCODING_ASCII
+#endif
+
+#define MF_USE_KERNING GDISP_NEED_TEXT_KERNING
+
+/* These are not used for now */
+#define MF_USE_ADVANCED_WORDWRAP 0
+#define MF_USE_JUSTIFY 0
+
+
+/*******************************************************
+ * Configuration settings related to build environment *
+ *******************************************************/
+
+/* Name of the file that contains all the included fonts. */
+#ifndef MF_FONT_FILE_NAME
+#define MF_FONT_FILE_NAME "fonts.h"
+#endif
+
+
+/*****************************************
+ * Configuration settings related to API *
+ *****************************************/
+
+/* Encoding for the input data.
+ * With the unicode encodings, the library supports the range of unicode
+ * characters 0x0000-0xFFFF (the Basic Multilingual Plane).
+ *
+ * ASCII: Plain ascii (somewhat works with ISO8859-1 also)
+ * UTF8: UTF8 encoding (variable number of bytes)
+ * UTF16: UTF16 encoding (2 bytes per character, compatible with UCS-2)
+ * WCHAR: Use compiler's wchar_t (usually same as UTF16)
+ */
+#define MF_ENCODING_ASCII 0
+#define MF_ENCODING_UTF8 1
+#define MF_ENCODING_UTF16 2
+#define MF_ENCODING_WCHAR 3
+#ifndef MF_ENCODING
+#define MF_ENCODING MF_ENCODING_UTF8
+#endif
+
+
+/************************************************************************
+ * Configuration settings related to visual appearance of rendered text *
+ ************************************************************************/
+
+/* Minimum space between characters, in percents of the glyph width.
+ * Increasing this causes the kerning module to leave more space between
+ * characters.
+ */
+#ifndef MF_KERNING_SPACE_PERCENT
+#define MF_KERNING_SPACE_PERCENT 15
+#endif
+
+/* Minimum space between characters, in pixels. Added to the percentual
+ * spacing. This pixel-based value guarantees enough space even with small
+ * fonts.
+ */
+#ifndef MF_KERNING_SPACE_PIXELS
+#define MF_KERNING_SPACE_PIXELS 3
+#endif
+
+/* Maximum adjustment done by the kerning algorithm, as percent of the
+ * glyph width.
+ */
+#ifndef MF_KERNING_LIMIT
+#define MF_KERNING_LIMIT 20
+#endif
+
+/* Spacing of tabulator stops. The value is multiplied by the width of the
+ * 'm' character in the current font.
+ */
+#ifndef MF_TABSIZE
+#define MF_TABSIZE 8
+#endif
+
+
+/*************************************************************************
+ * Configuration settings to strip down library to reduce resource usage *
+ *************************************************************************/
+
+/* Enable or disable the kerning module.
+ * Disabling it saves some code size and run time, but causes the spacing
+ * between characters to be less consistent.
+ */
+#ifndef MF_USE_KERNING
+#define MF_USE_KERNING 1
+#endif
+
+/* Enable or disable the advanced word wrap algorithm.
+ * If disabled, uses a simpler algorithm.
+ */
+#ifndef MF_USE_ADVANCED_WORDWRAP
+#define MF_USE_ADVANCED_WORDWRAP 1
+#endif
+
+/* Enable of disable the justification algorithm.
+ * If disabled, mf_render_justified renders just left-aligned.
+ */
+#ifndef MF_USE_JUSTIFY
+#define MF_USE_JUSTIFY 1
+#endif
+
+/* Enable or disable the center and right alignment code.
+ * If disabled, any alignment results in MF_ALIGN_LEFT.
+ */
+#ifndef MF_USE_ALIGN
+#define MF_USE_ALIGN 1
+#endif
+
+/* Enable or disable the support for tab alignment.
+ * If disabled, tabs will be rendered as regular space character.
+ */
+#ifndef MF_USE_TABS
+#define MF_USE_TABS 1
+#endif
+
+/* Number of vertical zones to use when computing kerning.
+ * Larger values give more accurate kerning, but are slower and use somewhat
+ * more memory. There is no point to increase this beyond the height of the
+ * font.
+ */
+#ifndef MF_KERNING_ZONES
+#define MF_KERNING_ZONES 16
+#endif
+
+
+
+/* Add extern "C" when used from C++. */
+#ifdef __cplusplus
+#define MF_EXTERN extern "C"
+#else
+#define MF_EXTERN extern
+#endif
+
+#endif
+
diff --git a/src/gdisp/mcufont/mf_encoding.c b/src/gdisp/mcufont/mf_encoding.c
new file mode 100644
index 00000000..4e3975ae
--- /dev/null
+++ b/src/gdisp/mcufont/mf_encoding.c
@@ -0,0 +1,69 @@
+#include "mf_encoding.h"
+
+#if MF_ENCODING == MF_ENCODING_UTF8
+
+mf_char mf_getchar(mf_str *str)
+{
+ uint8_t c;
+ uint8_t tmp, seqlen;
+ uint16_t result;
+
+ c = **str;
+ if (!c)
+ return 0;
+
+ (*str)++;
+
+ if ((c & 0x80) == 0)
+ {
+ /* Just normal ASCII character. */
+ return c;
+ }
+ else if ((c & 0xC0) == 0x80)
+ {
+ /* Dangling piece of corrupted multibyte sequence.
+ * Did you cut the string in the wrong place?
+ */
+ return c;
+ }
+ else if ((**str & 0xC0) == 0xC0)
+ {
+ /* Start of multibyte sequence without any following bytes.
+ * Silly. Maybe you are using the wrong encoding.
+ */
+ return c;
+ }
+ else
+ {
+ /* Beginning of a multi-byte sequence.
+ * Find out how many characters and combine them.
+ */
+ seqlen = 2;
+ tmp = 0x20;
+ result = 0;
+ while ((c & tmp) && (seqlen < 5))
+ {
+ seqlen++;
+ tmp >>= 1;
+
+ result = (result << 6) | (**str & 0x3F);
+ (*str)++;
+ }
+
+ result = (result << 6) | (**str & 0x3F);
+ (*str)++;
+
+ result |= (c & (tmp - 1)) << ((seqlen - 1) * 6);
+ return result;
+ }
+}
+
+void mf_rewind(mf_str *str)
+{
+ (*str)--;
+
+ while ((**str & 0x80) != 0x00 && (**str & 0xC0) != 0xC0)
+ (*str)--;
+}
+
+#endif
diff --git a/src/gdisp/mcufont/mf_encoding.h b/src/gdisp/mcufont/mf_encoding.h
new file mode 100644
index 00000000..e96718b7
--- /dev/null
+++ b/src/gdisp/mcufont/mf_encoding.h
@@ -0,0 +1,53 @@
+/* Simple UTF-8 decoder. Also implements the much simpler ASCII and UTF16
+ * input encodings.
+ */
+
+#ifndef _MF_ENCODING_H_
+#define _MF_ENCODING_H_
+
+#include "mf_config.h"
+#include <stdint.h>
+
+/* Type used to represent characters internally. */
+#if MF_ENCODING == MF_ENCODING_ASCII
+typedef char mf_char;
+#else
+typedef uint16_t mf_char;
+#endif
+
+/* Type used to represent input strings. */
+#if MF_ENCODING == MF_ENCODING_ASCII
+typedef const char * mf_str;
+#elif MF_ENCODING == MF_ENCODING_UTF8
+typedef const char * mf_str;
+#elif MF_ENCODING == MF_ENCODING_UTF16
+typedef const uint16_t * mf_str;
+#elif MF_ENCODING == MF_ENCODING_WCHAR
+#include <stddef.h>
+typedef const wchar_t * mf_str;
+#endif
+
+/* Returns the next character in the string and advances the pointer.
+ * When the string ends, returns 0 and leaves the pointer at the 0 byte.
+ *
+ * str: Pointer to variable holding current location in string.
+ * Initialize it to the start of the string.
+ *
+ * Returns: The next character, as unicode codepoint.
+ */
+#if MF_ENCODING == MF_ENCODING_UTF8
+MF_EXTERN mf_char mf_getchar(mf_str *str);
+#else
+static mf_char mf_getchar(mf_str *str) { return *(*str)++; }
+#endif
+
+/* Moves back the pointer to the beginning of the previous character.
+ * Be careful not to go beyond the start of the string.
+ */
+#if MF_ENCODING == MF_ENCODING_UTF8
+MF_EXTERN void mf_rewind(mf_str *str);
+#else
+static void mf_rewind(mf_str *str) { (*str)--; }
+#endif
+
+#endif
diff --git a/src/gdisp/mcufont/mf_font.c b/src/gdisp/mcufont/mf_font.c
new file mode 100644
index 00000000..698a3d8e
--- /dev/null
+++ b/src/gdisp/mcufont/mf_font.c
@@ -0,0 +1,77 @@
+#include "mf_font.h"
+#include <stdbool.h>
+
+/* This will be made into a list of included fonts using macro magic. */
+#define MF_INCLUDED_FONTS 0
+
+/* Included fonts begin here */
+#include MF_FONT_FILE_NAME
+/* Include fonts end here */
+
+uint8_t mf_render_character(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ mf_char character,
+ mf_pixel_callback_t callback,
+ void *state)
+{
+ uint8_t width;
+ width = font->render_character(font, x0, y0, character, callback, state);
+
+ if (!width)
+ {
+ width = font->render_character(font, x0, y0, font->fallback_character,
+ callback, state);
+ }
+
+ return width;
+}
+
+uint8_t mf_character_width(const struct mf_font_s *font,
+ mf_char character)
+{
+ uint8_t width;
+ width = font->character_width(font, character);
+
+ if (!width)
+ {
+ width = font->character_width(font, font->fallback_character);
+ }
+
+ return width;
+}
+
+/* Avoids a dependency on libc */
+static bool strequals(const char *a, const char *b)
+{
+ while (*a)
+ {
+ if (*a++ != *b++)
+ return false;
+ }
+ return (!*b);
+}
+
+const struct mf_font_s *mf_find_font(const char *name)
+{
+ const struct mf_font_list_s *f;
+ f = MF_INCLUDED_FONTS;
+
+ while (f)
+ {
+ if (strequals(f->font->full_name, name) ||
+ strequals(f->font->short_name, name))
+ {
+ return f->font;
+ }
+
+ f = f->next;
+ }
+
+ return 0;
+}
+
+const struct mf_font_list_s *mf_get_font_list()
+{
+ return MF_INCLUDED_FONTS;
+}
+
diff --git a/src/gdisp/mcufont/mf_font.h b/src/gdisp/mcufont/mf_font.h
new file mode 100644
index 00000000..591ebab6
--- /dev/null
+++ b/src/gdisp/mcufont/mf_font.h
@@ -0,0 +1,118 @@
+/* Generic font type that supports fonts with multiple kinds of compression.
+ * Provides an interface for decoding and rendering single characters.
+ */
+
+#ifndef _MF_FONT_H_
+#define _MF_FONT_H_
+
+#include "mf_encoding.h"
+
+/* Callback function that writes pixels to screen / buffer / whatever.
+ *
+ * x: X coordinate of the first pixel to write.
+ * y: Y coordinate of the first pixel to write.
+ * count: Number of pixels to fill (horizontally).
+ * alpha: The "opaqueness" of the pixels, 0 for background, 255 for text.
+ * state: Free variable that was passed to render_character().
+ */
+typedef void (*mf_pixel_callback_t) (int16_t x, int16_t y, uint8_t count,
+ uint8_t alpha, void *state);
+
+/* General information about a font. */
+struct mf_font_s
+{
+ /* Full name of the font, comes from the original font file. */
+ const char *full_name;
+
+ /* Short name of the font, comes from file name. */
+ const char *short_name;
+
+ /* Width and height of the character bounding box. */
+ uint8_t width;
+ uint8_t height;
+
+ /* Minimum and maximum tracking width of characters. */
+ uint8_t min_x_advance;
+ uint8_t max_x_advance;
+
+ /* Location of the text baseline relative to character. */
+ uint8_t baseline_x;
+ uint8_t baseline_y;
+
+ /* Line height of the font (vertical advance). */
+ uint8_t line_height;
+
+ /* Flags identifying various aspects of the font. */
+ uint8_t flags;
+
+ /* Fallback character to use for missing glyphs. */
+ mf_char fallback_character;
+
+ /* Function to get character width. Should return 0 if character is
+ * not found. */
+ uint8_t (*character_width)(const struct mf_font_s *font, mf_char character);
+
+ /* Function to render a character. Returns the character width or 0 if
+ * character is not found. */
+ uint8_t (*render_character)(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ mf_char character,
+ mf_pixel_callback_t callback,
+ void *state);
+};
+
+/* The flag definitions for the font.flags field. */
+#define MF_FONT_FLAG_MONOSPACE 0x01
+#define MF_FONT_FLAG_BW 0x02
+
+/* Lookup structure for searching fonts by name. */
+struct mf_font_list_s
+{
+ const struct mf_font_list_s *next;
+ const struct mf_font_s *font;
+};
+
+
+/* Function to decode and render a single character.
+ *
+ * font: Pointer to the font definition.
+ * x0, y0: Upper left corner of the target area.
+ * character: The character code (unicode) to render.
+ * callback: Callback function to write out the pixels.
+ * state: Free variable for caller to use (can be NULL).
+ *
+ * Returns width of the character.
+ */
+MF_EXTERN uint8_t mf_render_character(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ mf_char character,
+ mf_pixel_callback_t callback,
+ void *state);
+
+/* Function to get the width of a single character.
+ * This is not necessarily the bounding box of the character
+ * data, but rather the tracking width.
+ *
+ * font: Pointer to the font definition.
+ * character: The character code (unicode) to render.
+ *
+ * Returns width of the character in pixels.
+ */
+MF_EXTERN uint8_t mf_character_width(const struct mf_font_s *font,
+ mf_char character);
+
+/* Find a font based on name. The name can be either short name or full name.
+ * Note: You can pass MF_INCLUDED_FONTS to search among all the included .h
+ * files.
+ *
+ * name: Font name to search for.
+ * fonts: Pointer to the first font search entry.
+ *
+ * Returns a pointer to the font or NULL if not found.
+ */
+MF_EXTERN const struct mf_font_s *mf_find_font(const char *name);
+
+/* Get the list of included fonts */
+MF_EXTERN const struct mf_font_list_s *mf_get_font_list();
+
+#endif \ No newline at end of file
diff --git a/src/gdisp/mcufont/mf_justify.c b/src/gdisp/mcufont/mf_justify.c
new file mode 100644
index 00000000..1938bb6a
--- /dev/null
+++ b/src/gdisp/mcufont/mf_justify.c
@@ -0,0 +1,321 @@
+#include "mf_justify.h"
+#include "mf_kerning.h"
+
+#if MF_USE_TABS
+/* Round the X coordinate up to the nearest tab stop. */
+static int16_t mf_round_to_tab(const struct mf_font_s *font,
+ int16_t x0, int16_t x)
+{
+ int16_t tabw, dx;
+
+ tabw = mf_character_width(font, 'm') * MF_TABSIZE;
+
+ /* Always atleast 1 space */
+ x += mf_character_width(font, ' ');
+
+ /* Round to next tab stop */
+ dx = x - x0 + font->baseline_x;
+ x += tabw - (dx % tabw);
+
+ return x;
+}
+
+/* Round the X coordinate down to the nearest tab stop. */
+static int16_t mf_round_to_prev_tab(const struct mf_font_s *font,
+ int16_t x0, int16_t x)
+{
+ int16_t tabw, dx;
+
+ tabw = mf_character_width(font, 'm') * MF_TABSIZE;
+
+ /* Always atleast 1 space */
+ x -= mf_character_width(font, ' ');
+
+ /* Round to previous tab stop */
+ dx = x0 - x + font->baseline_x;
+ x -= tabw - (dx % tabw);
+
+ return x;
+}
+#endif
+
+int16_t mf_get_string_width(const struct mf_font_s *font, mf_str text,
+ uint16_t count, bool kern)
+{
+ int16_t result = 0;
+ uint16_t c1 = 0, c2;
+
+ if (!count)
+ count = 0xFFFF;
+
+ while (count-- && *text)
+ {
+ c2 = mf_getchar(&text);
+
+ if (kern && c1 != 0)
+ result += mf_compute_kerning(font, c1, c2);
+
+ result += mf_character_width(font, c2);
+ c1 = c2;
+ }
+
+ return result;
+}
+
+/* Return the length of the string without trailing spaces. */
+static uint16_t strip_spaces(mf_str text, uint16_t count, mf_char *last_char)
+{
+ uint16_t i = 0, result = 0;
+ mf_char tmp = 0;
+
+ if (!count)
+ count = 0xFFFF;
+
+ while (count-- && *text)
+ {
+ i++;
+ tmp = mf_getchar(&text);
+ if (tmp != ' ' && tmp != 0xA0 && tmp != '\n' &&
+ tmp != '\r' && tmp != '\t')
+ {
+ result = i;
+ }
+ }
+
+ if (last_char)
+ {
+ if (!*text)
+ *last_char = 0;
+ else
+ *last_char = tmp;
+ }
+
+ return result;
+}
+
+/* Render left-aligned string, left edge at x0. */
+static void render_left(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ mf_str text, uint16_t count,
+ mf_character_callback_t callback,
+ void *state)
+{
+ int16_t x;
+ mf_char c1 = 0, c2;
+
+ x = x0 - font->baseline_x;
+ while (count--)
+ {
+ c2 = mf_getchar(&text);
+
+ if (c2 == '\t')
+ {
+#if MF_USE_TABS
+ x = mf_round_to_tab(font, x0, x);
+ c1 = ' ';
+ continue;
+#else
+ c2 = ' ';
+#endif
+ }
+
+ if (c1 != 0)
+ x += mf_compute_kerning(font, c1, c2);
+
+ x += callback(x, y0, c2, state);
+ c1 = c2;
+ }
+}
+
+#if !MF_USE_ALIGN
+
+void mf_render_aligned(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ enum mf_align_t align,
+ mf_str text, uint16_t count,
+ mf_character_callback_t callback,
+ void *state)
+{
+ int16_t string_width;
+ count = strip_spaces(text, count, 0);
+ render_left(font, x0, y0, text, count, callback, state);
+}
+
+#else
+
+/* Render right-aligned string, right edge at x0. */
+static void render_right(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ mf_str text, uint16_t count,
+ mf_character_callback_t callback,
+ void *state)
+{
+ int16_t x;
+ uint16_t i;
+ mf_char c1, c2 = 0;
+ mf_str tmp;
+
+ /* Go to the end of the line. */
+ for (i = 0; i < count; i++)
+ mf_getchar(&text);
+
+ x = x0 - font->baseline_x;
+ for (i = 0; i < count; i++)
+ {
+ mf_rewind(&text);
+ tmp = text;
+ c1 = mf_getchar(&tmp);
+
+ /* Perform tab alignment */
+ if (c1 == '\t')
+ {
+#if MF_USE_TABS
+ x = mf_round_to_prev_tab(font, x0, x);
+ c2 = ' ';
+ continue;
+#else
+ c1 = ' ';
+#endif
+ }
+
+ /* Apply the nominal character width */
+ x -= mf_character_width(font, c1);
+
+ /* Apply kerning */
+ if (c2 != 0)
+ x -= mf_compute_kerning(font, c1, c2);
+
+ callback(x, y0, c1, state);
+ c2 = c1;
+ }
+}
+
+void mf_render_aligned(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ enum mf_align_t align,
+ mf_str text, uint16_t count,
+ mf_character_callback_t callback,
+ void *state)
+{
+ int16_t string_width;
+ count = strip_spaces(text, count, 0);
+
+ if (align == MF_ALIGN_LEFT)
+ {
+ render_left(font, x0, y0, text, count, callback, state);
+ }
+ if (align == MF_ALIGN_CENTER)
+ {
+ string_width = mf_get_string_width(font, text, count, false);
+ x0 -= string_width / 2;
+ render_left(font, x0, y0, text, count, callback, state);
+ }
+ else if (align == MF_ALIGN_RIGHT)
+ {
+ render_right(font, x0, y0, text, count, callback, state);
+ }
+}
+
+#endif
+
+
+#if !MF_USE_JUSTIFY
+
+void mf_render_justified(const struct mf_font_s *font,
+ int16_t x0, int16_t y0, int16_t width,
+ mf_str text, uint16_t count,
+ mf_character_callback_t callback,
+ void *state)
+{
+ mf_render_aligned(font, x0, y0, MF_ALIGN_LEFT, text, count, callback, state);
+}
+
+#else
+
+/* Returns true if the character is a justification point, i.e. expands
+ * when the text is being justified. */
+static bool is_justify_space(uint16_t c)
+{
+ return c == ' ' || c == 0xA0;
+}
+
+/* Count the number of space characters in string */
+static uint16_t count_spaces(mf_str text, uint16_t count)
+{
+ uint16_t spaces = 0;
+ while (count-- && *text)
+ {
+ if (is_justify_space(mf_getchar(&text)))
+ spaces++;
+ }
+ return spaces;
+}
+
+void mf_render_justified(const struct mf_font_s *font,
+ int16_t x0, int16_t y0, int16_t width,
+ mf_str text, uint16_t count,
+ mf_character_callback_t callback,
+ void *state)
+{
+ int16_t string_width, adjustment;
+ uint16_t num_spaces;
+ mf_char last_char;
+
+ count = strip_spaces(text, count, &last_char);
+
+ if (last_char == '\n' || last_char == 0)
+ {
+ /* Line ends in linefeed, do not justify. */
+ render_left(font, x0, y0, text, count, callback, state);
+ return;
+ }
+
+ string_width = mf_get_string_width(font, text, count, false);
+ adjustment = width - string_width;
+ num_spaces = count_spaces(text, count);
+
+ {
+ int16_t x, tmp;
+ uint16_t c1 = 0, c2;
+
+ x = x0 - font->baseline_x;
+ while (count--)
+ {
+ c2 = mf_getchar(&text);
+
+ if (c2 == '\t')
+ {
+#if MF_USE_TABS
+ tmp = x;
+ x = mf_round_to_tab(font, x0, x);
+ adjustment -= x - tmp - mf_character_width(font, '\t');
+ c1 = c2;
+ continue;
+#else
+ c2 = ' ';
+#endif
+ }
+
+ if (is_justify_space(c2))
+ {
+ tmp = (adjustment + num_spaces / 2) / num_spaces;
+ adjustment -= tmp;
+ num_spaces--;
+ x += tmp;
+ }
+
+ if (c1 != 0)
+ {
+ tmp = mf_compute_kerning(font, c1, c2);
+ x += tmp;
+ adjustment -= tmp;
+ }
+
+ x += callback(x, y0, c2, state);
+ c1 = c2;
+ }
+ }
+}
+
+#endif
+
diff --git a/src/gdisp/mcufont/mf_justify.h b/src/gdisp/mcufont/mf_justify.h
new file mode 100644
index 00000000..c3e8ba75
--- /dev/null
+++ b/src/gdisp/mcufont/mf_justify.h
@@ -0,0 +1,74 @@
+/* Text alignment and justification algorithm. Supports left, right, center
+ * alignment and justify. Supports tab stops and kerning.
+ */
+
+#ifndef _MF_JUSTIFY_H_
+#define _MF_JUSTIFY_H_
+
+#include "mf_rlefont.h"
+#include <stdbool.h>
+
+enum mf_align_t
+{
+ MF_ALIGN_LEFT = 0,
+ MF_ALIGN_CENTER,
+ MF_ALIGN_RIGHT
+};
+
+/* Callback for rendering a single character.
+ * x0: Left edge of the target position of character.
+ * y0: Upper edge of the target position of character.
+ * character: Character to render.
+ * state: Free state variable for use by the callback.
+ * Returns the width of the character.
+ */
+typedef uint8_t (*mf_character_callback_t) (int16_t x0, int16_t y0,
+ mf_char character, void *state);
+
+/* Get width of a string in pixels.
+ *
+ * font: Pointer to the font definition.
+ * text: Pointer to start of the text to measure.
+ * count: Number of characters on the line or 0 to read until end of string.
+ * kern: True to consider kerning (slower).
+ */
+MF_EXTERN int16_t mf_get_string_width(const struct mf_font_s *font,
+ mf_str text, uint16_t count, bool kern);
+
+/* Render a single line of aligned text.
+ *
+ * font: Pointer to the font definition.
+ * x0: Depending on aligned, either left, center or right edge of target.
+ * y0: Upper edge of the target area.
+ * align: Type of alignment.
+ * text: Pointer to start of the text to render.
+ * count: Number of characters on the line or 0 to read until end of string.
+ * callback: Callback to call for each character.
+ * state: Free variable for use in the callback.
+ */
+MF_EXTERN void mf_render_aligned(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ enum mf_align_t align,
+ mf_str text, uint16_t count,
+ mf_character_callback_t callback,
+ void *state);
+
+/* Render a single line of justified text.
+ *
+ * font: Pointer to the font definition.
+ * x0: Left edge of the target area.
+ * y0: Upper edge of the target area.
+ * width: Width of the target area.
+ * text: Pointer to start of the text to render.
+ * count: Number of characters on the line or 0 to read until end of string.
+ * callback: Callback to call for each character.
+ * state: Free variable for use in the callback.
+ */
+MF_EXTERN void mf_render_justified(const struct mf_font_s *font,
+ int16_t x0, int16_t y0, int16_t width,
+ mf_str text, uint16_t count,
+ mf_character_callback_t callback,
+ void *state);
+
+
+#endif
diff --git a/src/gdisp/mcufont/mf_kerning.c b/src/gdisp/mcufont/mf_kerning.c
new file mode 100644
index 00000000..bd5afc1a
--- /dev/null
+++ b/src/gdisp/mcufont/mf_kerning.c
@@ -0,0 +1,118 @@
+#include "mf_kerning.h"
+#include <stdbool.h>
+
+#if MF_USE_KERNING
+
+/* Structure for keeping track of the edge of the glyph as it is rendered. */
+struct kerning_state_s
+{
+ uint8_t edgepos[MF_KERNING_ZONES];
+ uint8_t zoneheight;
+};
+
+/* Pixel callback for analyzing the left edge of a glyph. */
+static void fit_leftedge(int16_t x, int16_t y, uint8_t count, uint8_t alpha,
+ void *state)
+{
+ struct kerning_state_s *s = state;
+
+ if (alpha > 7)
+ {
+ uint8_t zone = y / s->zoneheight;
+ if (x < s->edgepos[zone])
+ s->edgepos[zone] = x;
+ }
+}
+
+/* Pixel callback for analyzing the right edge of a glyph. */
+static void fit_rightedge(int16_t x, int16_t y, uint8_t count, uint8_t alpha,
+ void *state)
+{
+ struct kerning_state_s *s = state;
+
+ if (alpha > 7)
+ {
+ uint8_t zone = y / s->zoneheight;
+ x += count - 1;
+ if (x > s->edgepos[zone])
+ s->edgepos[zone] = x;
+ }
+}
+
+/* Should kerning be done against this character? */
+static bool do_kerning(mf_char c)
+{
+ /* Just a speed optimization, spaces would be ignored anyway. */
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t')
+ return false;
+
+ /* Do not kern against digits, in order to keep values in tables nicely
+ * aligned. Most fonts have constant width for digits. */
+ if (c >= '0' && c <= '9')
+ return false;
+
+ return true;
+}
+
+static int16_t min16(int16_t a, int16_t b) { return (a < b) ? a : b; }
+static int16_t max16(int16_t a, int16_t b) { return (a > b) ? a : b; }
+static int16_t avg16(int16_t a, int16_t b) { return (a + b) / 2; }
+
+int8_t mf_compute_kerning(const struct mf_font_s *font,
+ mf_char c1, mf_char c2)
+{
+ struct kerning_state_s leftedge, rightedge;
+ uint8_t w1, w2, i, min_space;
+ int16_t normal_space, adjust, max_adjust;
+
+ if (font->flags & MF_FONT_FLAG_MONOSPACE)
+ return 0; /* No kerning for monospace fonts */
+
+ if (!do_kerning(c1) || !do_kerning(c2))
+ return 0;
+
+ /* Compute the height of one kerning zone in pixels */
+ i = (font->height + MF_KERNING_ZONES - 1) / MF_KERNING_ZONES;
+ if (i < 1) i = 1;
+
+ /* Initialize structures */
+ leftedge.zoneheight = rightedge.zoneheight = i;
+ for (i = 0; i < MF_KERNING_ZONES; i++)
+ {
+ leftedge.edgepos[i] = 255;
+ rightedge.edgepos[i] = 0;
+ }
+
+ /* Analyze the edges of both glyphs. */
+ w1 = mf_render_character(font, 0, 0, c1, fit_rightedge, &rightedge);
+ w2 = mf_render_character(font, 0, 0, c2, fit_leftedge, &leftedge);
+
+ /* Find the minimum horizontal space between the glyphs. */
+ min_space = 255;
+ for (i = 0; i < MF_KERNING_ZONES; i++)
+ {
+ uint8_t space;
+ if (leftedge.edgepos[i] == 255 || rightedge.edgepos[i] == 0)
+ continue; /* Outside glyph area. */
+
+ space = w1 - rightedge.edgepos[i] + leftedge.edgepos[i];
+ if (space < min_space)
+ min_space = space;
+ }
+
+ if (min_space == 255)
+ return 0; /* One of the characters is space, or both are punctuation. */
+
+ /* Compute the adjustment of the glyph position. */
+ normal_space = avg16(w1, w2) * MF_KERNING_SPACE_PERCENT / 100;
+ normal_space += MF_KERNING_SPACE_PIXELS;
+ adjust = normal_space - min_space;
+ max_adjust = -max16(w1, w2) * MF_KERNING_LIMIT / 100;
+
+ if (adjust > 0) adjust = 0;
+ if (adjust < max_adjust) adjust = max_adjust;
+
+ return adjust;
+}
+
+#endif
diff --git a/src/gdisp/mcufont/mf_kerning.h b/src/gdisp/mcufont/mf_kerning.h
new file mode 100644
index 00000000..ed885162
--- /dev/null
+++ b/src/gdisp/mcufont/mf_kerning.h
@@ -0,0 +1,29 @@
+/* Automatic kerning for font rendering. This solves the issue where some
+ * fonts (especially serif fonts) have too much space between specific
+ * character pairs, like WA or L'.
+ */
+
+#ifndef _MF_KERNING_H_
+#define _MF_KERNING_H_
+
+#include "mf_config.h"
+#include "mf_rlefont.h"
+
+/* Compute the kerning adjustment when c1 is followed by c2.
+ *
+ * font: Pointer to the font definition.
+ * c1: The previous character.
+ * c2: The next character to render.
+ *
+ * Returns the offset to add to the x position for c2.
+ */
+#if MF_USE_KERNING
+MF_EXTERN int8_t mf_compute_kerning(const struct mf_font_s *font,
+ mf_char c1, mf_char c2);
+#else
+static int8_t mf_compute_kerning(const struct mf_font_s *font,
+ mf_char c1, mf_char c2)
+{ return 0; }
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/gdisp/mcufont/mf_rlefont.c b/src/gdisp/mcufont/mf_rlefont.c
new file mode 100644
index 00000000..8afaa9c2
--- /dev/null
+++ b/src/gdisp/mcufont/mf_rlefont.c
@@ -0,0 +1,282 @@
+#include "mf_rlefont.h"
+
+/* Number of reserved codes before the dictionary entries. */
+#define DICT_START 24
+
+/* Special reference to mean "fill with zeros to the end of the glyph" */
+#define REF_FILLZEROS 16
+
+/* RLE codes */
+#define RLE_CODEMASK 0xC0
+#define RLE_VALMASK 0x3F
+#define RLE_ZEROS 0x00
+#define RLE_64ZEROS 0x40
+#define RLE_ONES 0x80
+#define RLE_SHADE 0xC0
+
+/* Dictionary "fill entries" for encoding bits directly. */
+#define DICT_START7BIT 4
+#define DICT_START6BIT 132
+#define DICT_START5BIT 196
+#define DICT_START4BIT 228
+#define DICT_START3BIT 244
+#define DICT_START2BIT 252
+
+/* Find a pointer to the glyph matching a given character by searching
+ * through the character ranges. If the character is not found, return
+ * pointer to the default glyph.
+ */
+static const uint8_t *find_glyph(const struct mf_rlefont_s *font,
+ uint16_t character)
+{
+ unsigned i, index;
+ const struct mf_rlefont_char_range_s *range;
+ for (i = 0; i < font->char_range_count; i++)
+ {
+ range = &font->char_ranges[i];
+ index = character - range->first_char;
+ if (character >= range->first_char && index < range->char_count)
+ {
+ uint16_t offset = range->glyph_offsets[index];
+ return &range->glyph_data[offset];
+ }
+ }
+
+ return 0;
+}
+
+/* Structure to keep track of coordinates of the next pixel to be written,
+ * and also the bounds of the character. */
+struct renderstate_r
+{
+ int16_t x_begin;
+ int16_t x_end;
+ int16_t x;
+ int16_t y;
+ int16_t y_end;
+ mf_pixel_callback_t callback;
+ void *state;
+};
+
+/* Call the callback to write one pixel to screen, and advance to next
+ * pixel position. */
+static void write_pixels(struct renderstate_r *rstate, uint16_t count,
+ uint8_t alpha)
+{
+ uint8_t rowlen;
+
+ /* Write row-by-row if the run spans multiple rows. */
+ while (rstate->x + count >= rstate->x_end)
+ {
+ rowlen = rstate->x_end - rstate->x;
+ rstate->callback(rstate->x, rstate->y, rowlen, alpha, rstate->state);
+ count -= rowlen;
+ rstate->x = rstate->x_begin;
+ rstate->y++;
+ }
+
+ /* Write the remaining part */
+ if (count)
+ {
+ rstate->callback(rstate->x, rstate->y, count, alpha, rstate->state);
+ rstate->x += count;
+ }
+}
+
+/* Skip the given number of pixels (0 alpha) */
+static void skip_pixels(struct renderstate_r *rstate, uint16_t count)
+{
+ rstate->x += count;
+ while (rstate->x >= rstate->x_end)
+ {
+ rstate->x -= rstate->x_end - rstate->x_begin;
+ rstate->y++;
+ }
+}
+
+/* Decode and write out a RLE-encoded dictionary entry. */
+static void write_rle_dictentry(const struct mf_rlefont_s *font,
+ struct renderstate_r *rstate,
+ uint8_t index)
+{
+ uint16_t offset = font->dictionary_offsets[index];
+ uint16_t length = font->dictionary_offsets[index + 1] - offset;
+ uint16_t i;
+
+ for (i = 0; i < length; i++)
+ {
+ uint8_t code = font->dictionary_data[offset + i];
+ if ((code & RLE_CODEMASK) == RLE_ZEROS)
+ {
+ skip_pixels(rstate, code & RLE_VALMASK);
+ }
+ else if ((code & RLE_CODEMASK) == RLE_64ZEROS)
+ {
+ skip_pixels(rstate, ((code & RLE_VALMASK) + 1) * 64);
+ }
+ else if ((code & RLE_CODEMASK) == RLE_ONES)
+ {
+ write_pixels(rstate, (code & RLE_VALMASK) + 1, 255);
+ }
+ else if ((code & RLE_CODEMASK) == RLE_SHADE)
+ {
+ uint8_t count, alpha;
+ count = ((code & RLE_VALMASK) >> 4) + 1;
+ alpha = ((code & RLE_VALMASK) & 0xF) * 0x11;
+ write_pixels(rstate, count, alpha);
+ }
+ }
+}
+
+/* Get bit count for the "fill entries" */
+static uint8_t fillentry_bitcount(uint8_t index)
+{
+ if (index >= DICT_START2BIT)
+ return 2;
+ else if (index >= DICT_START3BIT)
+ return 3;
+ else if (index >= DICT_START4BIT)
+ return 4;
+ else if (index >= DICT_START5BIT)
+ return 5;
+ else if (index >= DICT_START6BIT)
+ return 6;
+ else
+ return 7;
+}
+
+/* Decode and write out a direct binary codeword */
+static void write_bin_codeword(const struct mf_rlefont_s *font,
+ struct renderstate_r *rstate,
+ uint8_t code)
+{
+ uint8_t bitcount = fillentry_bitcount(code);
+ uint8_t byte = code - DICT_START7BIT;
+ uint8_t runlen = 0;
+
+ while (bitcount--)
+ {
+ if (byte & 1)
+ {
+ runlen++;
+ }
+ else
+ {
+ if (runlen)
+ {
+ write_pixels(rstate, runlen, 255);
+ runlen = 0;
+ }
+
+ skip_pixels(rstate, 1);
+ }
+
+ byte >>= 1;
+ }
+
+ if (runlen)
+ write_pixels(rstate, runlen, 255);
+}
+
+/* Decode and write out a reference codeword */
+static void write_ref_codeword(const struct mf_rlefont_s *font,
+ struct renderstate_r *rstate,
+ uint8_t code)
+{
+ if (code <= 15)
+ {
+ write_pixels(rstate, 1, 0x11 * code);
+ }
+ else if (code == REF_FILLZEROS)
+ {
+ /* Fill with zeroes to end */
+ rstate->y = rstate->y_end;
+ }
+ else if (code < DICT_START)
+ {
+ /* Reserved */
+ }
+ else if (code < DICT_START + font->rle_entry_count)
+ {
+ write_rle_dictentry(font, rstate, code - DICT_START);
+ }
+ else
+ {
+ write_bin_codeword(font, rstate, code);
+ }
+}
+
+/* Decode and write out a reference encoded dictionary entry. */
+static void write_ref_dictentry(const struct mf_rlefont_s *font,
+ struct renderstate_r *rstate,
+ uint8_t index)
+{
+ uint16_t offset = font->dictionary_offsets[index];
+ uint16_t length = font->dictionary_offsets[index + 1] - offset;
+ uint16_t i;
+
+ for (i = 0; i < length; i++)
+ {
+ uint8_t code = font->dictionary_data[offset + i];
+ write_ref_codeword(font, rstate, code);
+ }
+}
+
+/* Decode and write out an arbitrary glyph codeword */
+static void write_glyph_codeword(const struct mf_rlefont_s *font,
+ struct renderstate_r *rstate,
+ uint8_t code)
+{
+ if (code >= DICT_START + font->rle_entry_count &&
+ code < DICT_START + font->dict_entry_count)
+ {
+ write_ref_dictentry(font, rstate, code - DICT_START);
+ }
+ else
+ {
+ write_ref_codeword(font, rstate, code);
+ }
+}
+
+
+uint8_t mf_rlefont_render_character(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ uint16_t character,
+ mf_pixel_callback_t callback,
+ void *state)
+{
+ const uint8_t *p;
+ uint8_t width;
+
+ struct renderstate_r rstate;
+ rstate.x_begin = x0;
+ rstate.x_end = x0 + font->width;
+ rstate.x = x0;
+ rstate.y = y0;
+ rstate.y_end = y0 + font->height;
+ rstate.callback = callback;
+ rstate.state = state;
+
+ p = find_glyph((struct mf_rlefont_s*)font, character);
+ if (!p)
+ return 0;
+
+ width = *p++;
+ while (rstate.y < rstate.y_end)
+ {
+ write_glyph_codeword((struct mf_rlefont_s*)font, &rstate, *p++);
+ }
+
+ return width;
+}
+
+uint8_t mf_rlefont_character_width(const struct mf_font_s *font,
+ uint16_t character)
+{
+ const uint8_t *p;
+ p = find_glyph((struct mf_rlefont_s*)font, character);
+ if (!p)
+ return 0;
+
+ return *p;
+}
diff --git a/src/gdisp/mcufont/mf_rlefont.h b/src/gdisp/mcufont/mf_rlefont.h
new file mode 100644
index 00000000..d8275d5c
--- /dev/null
+++ b/src/gdisp/mcufont/mf_rlefont.h
@@ -0,0 +1,74 @@
+/* A compressed font format based on run length encoding and dictionary
+ * compression.
+ */
+
+#ifndef _MF_RLEFONT_H_
+#define _MF_RLEFONT_H_
+
+#include "mf_font.h"
+
+/* Versions of the RLE font format that are supported. */
+#define MF_RLEFONT_VERSION_4_SUPPORTED 1
+
+/* Structure for a range of characters. This implements a sparse storage of
+ * character indices, so that you can e.g. pick a 100 characters in the middle
+ * of the UTF16 range and just store them. */
+struct mf_rlefont_char_range_s
+{
+ /* The number of the first character in this range. */
+ uint16_t first_char;
+
+ /* The total count of characters in this range. */
+ uint16_t char_count;
+
+ /* Lookup table with the start indices into glyph_data. */
+ const uint16_t *glyph_offsets;
+
+ /* The encoded glyph data for glyphs in this range. */
+ const uint8_t *glyph_data;
+};
+
+/* Structure for a single encoded font. */
+struct mf_rlefont_s
+{
+ struct mf_font_s font;
+
+ /* Version of the font definition used. */
+ const uint8_t version;
+
+ /* Big array of the data for all the dictionary entries. */
+ const uint8_t *dictionary_data;
+
+ /* Lookup table with the start indices into dictionary_data.
+ * Contains N+1 entries, so that the length of the entry can
+ * be determined by subtracting from the next offset. */
+ const uint16_t *dictionary_offsets;
+
+ /* Number of dictionary entries using the RLE encoding.
+ * Entries starting at this index use the dictionary encoding. */
+ const uint8_t rle_entry_count;
+
+ /* Total number of dictionary entries.
+ * Entries after this are nonexistent. */
+ const uint8_t dict_entry_count;
+
+ /* Number of discontinuous character ranges */
+ const uint8_t char_range_count;
+
+ /* Array of the character ranges */
+ const struct mf_rlefont_char_range_s *char_ranges;
+};
+
+#ifdef MF_RLEFONT_INTERNALS
+/* Internal functions, don't use these directly. */
+MF_EXTERN uint8_t mf_rlefont_render_character(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ mf_char character,
+ mf_pixel_callback_t callback,
+ void *state);
+
+MF_EXTERN uint8_t mf_rlefont_character_width(const struct mf_font_s *font,
+ mf_char character);
+#endif
+
+#endif
diff --git a/src/gdisp/mcufont/mf_scaledfont.c b/src/gdisp/mcufont/mf_scaledfont.c
new file mode 100644
index 00000000..da2b31fe
--- /dev/null
+++ b/src/gdisp/mcufont/mf_scaledfont.c
@@ -0,0 +1,83 @@
+#include "mf_scaledfont.h"
+
+struct scaled_renderstate
+{
+ mf_pixel_callback_t orig_callback;
+ void *orig_state;
+ uint8_t x_scale;
+ uint8_t y_scale;
+ int16_t x0;
+ int16_t y0;
+};
+
+static void scaled_pixel_callback(int16_t x, int16_t y, uint8_t count,
+ uint8_t alpha, void *state)
+{
+ struct scaled_renderstate *rstate = state;
+ uint8_t dy;
+
+ count *= rstate->x_scale;
+ x = rstate->x0 + x * rstate->x_scale;
+ y = rstate->y0 + y * rstate->y_scale;
+
+ for (dy = 0; dy < rstate->y_scale; dy++)
+ {
+ rstate->orig_callback(x, y + dy, count, alpha, rstate->orig_state);
+ }
+}
+
+static uint8_t scaled_character_width(const struct mf_font_s *font,
+ mf_char character)
+{
+ struct mf_scaledfont_s *sfont = (struct mf_scaledfont_s*)font;
+ uint8_t basewidth;
+
+ basewidth = sfont->basefont->character_width(sfont->basefont, character);
+
+ return sfont->x_scale * basewidth;
+}
+
+static uint8_t scaled_render_character(const struct mf_font_s *font,
+ int16_t x0, int16_t y0,
+ mf_char character,
+ mf_pixel_callback_t callback,
+ void *state)
+{
+ struct mf_scaledfont_s *sfont = (struct mf_scaledfont_s*)font;
+ struct scaled_renderstate rstate;
+ uint8_t basewidth;
+
+ rstate.orig_callback = callback;
+ rstate.orig_state = state;
+ rstate.x_scale = sfont->x_scale;
+ rstate.y_scale = sfont->y_scale;
+ rstate.x0 = x0;
+ rstate.y0 = y0;
+
+ basewidth = sfont->basefont->render_character(sfont->basefont, 0, 0,
+ character, scaled_pixel_callback, &rstate);
+
+ return sfont->x_scale * basewidth;
+}
+
+void mf_scale_font(struct mf_scaledfont_s *newfont,
+ const struct mf_font_s *basefont,
+ uint8_t x_scale, uint8_t y_scale)
+{
+ newfont->font = *basefont;
+ newfont->basefont = basefont;
+
+ newfont->font.width *= x_scale;
+ newfont->font.height *= y_scale;
+ newfont->font.baseline_x *= x_scale;
+ newfont->font.baseline_y *= y_scale;
+ newfont->font.min_x_advance *= x_scale;
+ newfont->font.max_x_advance *= x_scale;
+ newfont->font.line_height *= y_scale;
+ newfont->font.character_width = &scaled_character_width;
+ newfont->font.render_character = &scaled_render_character;
+
+ newfont->x_scale = x_scale;
+ newfont->y_scale = y_scale;
+}
+
diff --git a/src/gdisp/mcufont/mf_scaledfont.h b/src/gdisp/mcufont/mf_scaledfont.h
new file mode 100644
index 00000000..f6607010
--- /dev/null
+++ b/src/gdisp/mcufont/mf_scaledfont.h
@@ -0,0 +1,23 @@
+/* Generate scaled (nearest-neighbor) fonts. This can be used for displaying
+ * larger text without spending the memory required for including larger fonts.
+ */
+
+#ifndef _MF_SCALEDFONT_H_
+#define _MF_SCALEDFONT_H_
+
+#include "mf_font.h"
+
+struct mf_scaledfont_s
+{
+ struct mf_font_s font;
+
+ const struct mf_font_s *basefont;
+ uint8_t x_scale;
+ uint8_t y_scale;
+};
+
+MF_EXTERN void mf_scale_font(struct mf_scaledfont_s *newfont,
+ const struct mf_font_s *basefont,
+ uint8_t x_scale, uint8_t y_scale);
+
+#endif
diff --git a/src/gdisp/mcufont/mf_wordwrap.c b/src/gdisp/mcufont/mf_wordwrap.c
new file mode 100644
index 00000000..cc735a4e
--- /dev/null
+++ b/src/gdisp/mcufont/mf_wordwrap.c
@@ -0,0 +1,290 @@
+#include "mf_wordwrap.h"
+
+/* Returns true if the line can be broken at this character. */
+static bool is_wrap_space(uint16_t c)
+{
+ return c == ' ' || c == '\n' || c == '\t' || c == '\r' || c == '-';
+}
+
+#if MF_USE_ADVANCED_WORDWRAP
+
+/* Represents a single word and the whitespace after it. */
+struct wordlen_s
+{
+ int16_t word; /* Length of the word in pixels. */
+ int16_t space; /* Length of the whitespace in pixels. */
+ uint16_t chars; /* Number of characters in word + space, combined. */
+};
+
+/* Take the next word from the string and compute its width.
+ * Returns true if the word ends in a linebreak. */
+static bool get_wordlen(const struct mf_font_s *font, mf_str *text,
+ struct wordlen_s *result)
+{
+ mf_char c;
+ mf_str prev;
+
+ result->word = 0;
+ result->space = 0;
+ result->chars = 0;
+
+ c = mf_getchar(text);
+ while (c && !is_wrap_space(c))
+ {
+ result->chars++;
+ result->word += mf_character_width(font, c);
+ c = mf_getchar(text);
+ }
+
+ prev = *text;
+ while (c && is_wrap_space(c))
+ {
+ result->chars++;
+
+ if (c == ' ')
+ result->space += mf_character_width(font, c);
+ else if (c == '\t')
+ result->space += mf_character_width(font, 'm') * MF_TABSIZE;
+ else if (c == '\n')
+ break;
+
+ prev = *text;
+ c = mf_getchar(text);
+ }
+
+ /* The last loop reads the first character of next word, put it back. */
+ if (c)
+ *text = prev;
+
+ return (c == '\0' || c == '\n');
+}
+
+/* Represents the rendered length for a single line. */
+struct linelen_s
+{
+ mf_str start; /* Start of the text for line. */
+ uint16_t chars; /* Total number of characters on the line. */
+ int16_t width; /* Total length of all words + whitespace on the line in pixels. */
+ bool linebreak; /* True if line ends in a linebreak */
+ struct wordlen_s last_word; /* Last word on the line. */
+ struct wordlen_s last_word_2; /* Second to last word on the line. */
+};
+
+/* Append word onto the line if it fits. If it would overflow, don't add and
+ * return false. */
+static bool append_word(const struct mf_font_s *font, int16_t width,
+ struct linelen_s *current, mf_str *text)
+{
+ mf_str tmp = *text;
+ struct wordlen_s wordlen;
+ bool linebreak;
+
+ linebreak = get_wordlen(font, &tmp, &wordlen);
+
+ if (current->width + wordlen.word <= width)
+ {
+ *text = tmp;
+ current->last_word_2 = current->last_word;
+ current->last_word = wordlen;
+ current->linebreak = linebreak;
+ current->chars += wordlen.chars;
+ current->width += wordlen.word + wordlen.space;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/* Append a character to the line if it fits. */
+static bool append_char(const struct mf_font_s *font, int16_t width,
+ struct linelen_s *current, mf_str *text)
+{
+ mf_str tmp = *text;
+ mf_char c;
+ uint16_t w;
+
+ c = mf_getchar(&tmp);
+ w = mf_character_width(font, c);
+
+ if (current->width + w <= width)
+ {
+ *text = tmp;
+ current->chars++;
+ current->width += w;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+static int16_t abs16(int16_t x) { return (x > 0) ? x : -x; }
+static int32_t sq16(int16_t x) { return (int32_t)x * x; }
+
+/* Try to balance the lines by potentially moving one word from the previous
+ * line to the the current one. */
+static void tune_lines(struct linelen_s *current, struct linelen_s *previous,
+ int16_t max_width)
+{
+ int16_t curw1, prevw1;
+ int16_t curw2, prevw2;
+ int32_t delta1, delta2;
+
+ /* If the lines are rendered as is */
+ curw1 = current->width - current->last_word.space;
+ prevw1 = previous->width - previous->last_word.space;
+ delta1 = sq16(max_width - prevw1) + sq16(max_width - curw1);
+
+ /* If the last word is moved */
+ curw2 = current->width + previous->last_word.word;
+ prevw2 = previous->width - previous->last_word.word
+ - previous->last_word.space
+ - previous->last_word_2.space;
+ delta2 = sq16(max_width - prevw2) + sq16(max_width - curw2);
+
+ if (delta1 > delta2 && curw2 <= max_width)
+ {
+ /* Do the change. */
+ uint16_t chars;
+
+ chars = previous->last_word.chars;
+ previous->chars -= chars;
+ current->chars += chars;
+ previous->width -= previous->last_word.word + previous->last_word.space;
+ current->width += previous->last_word.word + previous->last_word.space;
+ previous->last_word = previous->last_word_2;
+
+ while (chars--) mf_rewind(&current->start);
+ }
+}
+
+void mf_wordwrap(const struct mf_font_s *font, int16_t width,
+ mf_str text, mf_line_callback_t callback, void *state)
+{
+ struct linelen_s current = {};
+ struct linelen_s previous = {};
+ bool full;
+
+ current.start = text;
+
+ while (*text)
+ {
+ full = !append_word(font, width, &current, &text);
+
+ if (full || current.linebreak)
+ {
+ if (!current.chars)
+ {
+ /* We have a very long word. We must just cut it off at some
+ * point. */
+ while (append_char(font, width, &current, &text));
+ }
+
+ if (previous.chars)
+ {
+ /* Tune the length and dispatch the previous line. */
+ if (!previous.linebreak && !current.linebreak)
+ tune_lines(&current, &previous, width);
+
+ if (!callback(previous.start, previous.chars, state))
+ return;
+ }
+
+ previous = current;
+ current.start = text;
+ current.chars = 0;
+ current.width = 0;
+ current.linebreak = false;
+ current.last_word.word = 0;
+ current.last_word.space = 0;
+ current.last_word.chars = 0;
+ }
+ }
+
+ /* Dispatch the last lines. */
+ if (previous.chars)
+ {
+ if (!callback(previous.start, previous.chars, state))
+ return;
+ }
+
+ if (current.chars)
+ callback(current.start, current.chars, state);
+}
+
+#else
+
+void mf_wordwrap(const struct mf_font_s *font, int16_t width,
+ mf_str text, mf_line_callback_t callback, void *state)
+{
+ mf_str orig = text;
+ mf_str linestart;
+
+ /* Current line width and character count */
+ int16_t lw_cur = 0, cc_cur = 0;
+
+ /* Previous wrap point */
+ int16_t cc_prev;
+ mf_str ls_prev;
+
+ linestart = text;
+
+ while (*text)
+ {
+ cc_prev = 0;
+ ls_prev = text;
+
+ while (*text)
+ {
+ mf_char c;
+ int16_t new_width;
+ mf_str tmp;
+
+ tmp = text;
+ c = mf_getchar(&text);
+ new_width = lw_cur + mf_character_width(font, c);
+
+ if (c == '\n')
+ {
+ cc_prev = cc_cur + 1;
+ ls_prev = text;
+ break;
+ }
+
+ if (new_width > width)
+ {
+ text = tmp;
+ break;
+ }
+
+ cc_cur++;
+ lw_cur = new_width;
+
+ if (is_wrap_space(c))
+ {
+ cc_prev = cc_cur;
+ ls_prev = text;
+ }
+ }
+
+ /* Handle unbreakable words */
+ if (cc_prev == 0)
+ {
+ cc_prev = cc_cur;
+ ls_prev = text;
+ }
+
+ if (!callback(linestart, cc_prev, state))
+ return;
+
+ linestart = ls_prev;
+ text = linestart;
+ lw_cur = 0;
+ cc_cur = 0;
+ }
+}
+
+#endif
diff --git a/src/gdisp/mcufont/mf_wordwrap.h b/src/gdisp/mcufont/mf_wordwrap.h
new file mode 100644
index 00000000..bf3dbcb7
--- /dev/null
+++ b/src/gdisp/mcufont/mf_wordwrap.h
@@ -0,0 +1,32 @@
+/* Word wrapping algorithm with UTF-8 support. More than just a basic greedy
+ * word-wrapper: it attempts to balance consecutive lines as pairs.
+ */
+
+#ifndef _MF_WORDWRAP_H_
+#define _MF_WORDWRAP_H_
+
+#include "mf_rlefont.h"
+#include <stdbool.h>
+
+/* Callback function for handling each line.
+ *
+ * line: Pointer to the beginning of the string for this line.
+ * count: Number of characters on the line.
+ * state: Free variable that was passed to wordwrap().
+ *
+ * Returns: true to continue, false to stop after this line.
+ */
+typedef bool (*mf_line_callback_t) (mf_str line, uint16_t count,
+ void *state);
+
+/* Word wrap a piece of text. Calls the callback function for each line.
+ *
+ * font: Font to use for metrics.
+ * width: Maximum line width in pixels.
+ * text: Pointer to the start of the text to process.
+ * state: Free variable for caller to use (can be NULL).
+ */
+MF_EXTERN void mf_wordwrap(const struct mf_font_s *font, int16_t width,
+ mf_str text, mf_line_callback_t callback, void *state);
+
+#endif