From 55f3a8e0ed9000df07cea255568f3cdb1a6ab10b Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Sat, 20 Mar 2021 15:36:39 +0800 Subject: [PATCH 1/3] Make font encoding use all byte seq. possible --- Translations/make_translation.py | 68 +++++++++++++++++++++++++------- source/Core/Drivers/OLED.cpp | 25 ++++++------ 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/Translations/make_translation.py b/Translations/make_translation.py index 764d9934..acbd9c1c 100755 --- a/Translations/make_translation.py +++ b/Translations/make_translation.py @@ -233,6 +233,55 @@ def getCJKGlyph(sym): return s +def getCharsFromFontIndex(index: int) -> str: + ''' + Converts the font table index into its corresponding string escape + sequence(s). + ''' + + # We want to be able to use more than 254 symbols (excluding \x00 null + # terminator and \x01 new-line) in the font table but without making all + # the chars take 2 bytes. To do this, we use \xF1 to \xFF as lead bytes + # to designate double-byte chars, and leave the remaining as single-byte + # chars. + # + # For the sake of sanity, \x00 always means the end of string, so we skip + # \xF1\x00 and others in the mapping. + # + # Mapping example: + # + # 0x02 => 2 + # 0x03 => 3 + # ... + # 0xEF => 239 + # 0xF0 => 240 + # 0xF1 0x01 => 1 * 0xFF - 15 + 1 = 241 + # 0xF1 0x02 => 1 * 0xFF - 15 + 2 = 242 + # ... + # 0xF1 0xFF => 1 * 0xFF - 15 + 255 = 495 + # 0xF2 0x01 => 2 * 0xFF - 15 + 1 = 496 + # ... + # 0xF2 0xFF => 2 * 0xFF - 15 + 255 = 750 + # 0xF3 0x01 => 3 * 0xFF - 15 + 1 = 751 + # ... + # 0xFF 0xFF => 15 * 0xFF - 15 + 255 = 4065 + + assert index >= 0 + page = int((index + 14) / 0xFF) + assert page <= 0x0F + if page == 0: + return "\\x%0.2X" % index + else: + # Into extended range + # Leader is 0xFz where z is the page number + # Following char is the remainder + leader = page + 0xF0 + value = ((index + 14) % 0xFF) + 1 + assert leader <= 0xFF + assert value <= 0xFF + return "\\x%0.2X\\x%0.2X" % (leader, value) + + def getFontMapAndTable(textList): # the text list is sorted # allocate out these in their order as number codes @@ -242,29 +291,20 @@ def getFontMapAndTable(textList): forcedFirstSymbols = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] # enforce numbers are first for sym in forcedFirstSymbols: - symbolMap[sym] = "\\x%0.2X" % index + symbolMap[sym] = getCharsFromFontIndex(index) index = index + 1 totalSymbolCount = len(set(textList) | set(forcedFirstSymbols)) # \x00 is for NULL termination and \x01 is for newline, so the maximum - # number of symbols allowed with 8 bits is `256 - 2`. - if totalSymbolCount > ((0xEE * 0x0F) - 2): + # number of symbols allowed is as follow (see also the comments in + # `getCharsFromFontIndex`): + if totalSymbolCount > (0x10 * 0xFF - 15) - 2: log(f"Error, too many used symbols for this version (total {totalSymbolCount})") sys.exit(1) log("Generating fonts for {} symbols".format(totalSymbolCount)) for sym in textList: if sym not in symbolMap: - page = int(index / 0xEF) - if page == 0: - symbolMap[sym] = "\\x%0.2X" % index - else: - # Into extended range - # Leader is 0xFz where z is the page number - # Following char is the remainder - leader = page + 0xF0 - value = (index % 0xEF) + 1 - symbolMap[sym] = "\\x%0.2X" % leader + "\\x%0.2X" % value - + symbolMap[sym] = getCharsFromFontIndex(index) index = index + 1 # Get the font table fontTableStrings = [] diff --git a/source/Core/Drivers/OLED.cpp b/source/Core/Drivers/OLED.cpp index b9f27171..752f7cf4 100644 --- a/source/Core/Drivers/OLED.cpp +++ b/source/Core/Drivers/OLED.cpp @@ -235,20 +235,21 @@ void OLED::setRotation(bool leftHanded) { } // print a string to the current cursor location -void OLED::print(const char *str) { - uint16_t page = 0; - while (str[0]) { - if (static_cast(str[0]) >= 0xF0) { - page = static_cast(str[0] & 0x0F); - } else if (page != 0) { - page *= 0xEF; - page += static_cast(str[0]) - 1; - drawChar(page); - page = 0; +void OLED::print(const char *const str) { + const uint8_t *next = reinterpret_cast(str); + while (next[0]) { + uint16_t index; + if (next[0] <= 0xF0) { + index = next[0]; + next++; } else { - drawChar(str[0]); + if (!next[1]) { + return; + } + index = (next[0] - 0xF0) * 0xFF - 15 + next[1]; + next += 2; } - str++; + drawChar(index); } } From b7780f7bfb4e6bb922e25fd2334dcc6ba9cd71b9 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Sat, 20 Mar 2021 15:37:19 +0800 Subject: [PATCH 2/3] Add font encoding test --- Translations/make_translation_test.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Translations/make_translation_test.py diff --git a/Translations/make_translation_test.py b/Translations/make_translation_test.py new file mode 100644 index 00000000..5cae449a --- /dev/null +++ b/Translations/make_translation_test.py @@ -0,0 +1,21 @@ +import unittest + + +class TestMakeTranslation(unittest.TestCase): + def test_getCharsFromFontIndex(self): + from make_translation import getCharsFromFontIndex + self.assertEqual(getCharsFromFontIndex(2), "\\x02") + self.assertEqual(getCharsFromFontIndex(239), "\\xEF") + self.assertEqual(getCharsFromFontIndex(240), "\\xF0") + self.assertEqual(getCharsFromFontIndex(241), "\\xF1\\x01") + self.assertEqual(getCharsFromFontIndex(495), "\\xF1\\xFF") + self.assertEqual(getCharsFromFontIndex(496), "\\xF2\\x01") + self.assertEqual(getCharsFromFontIndex(750), "\\xF2\\xFF") + self.assertEqual(getCharsFromFontIndex(751), "\\xF3\\x01") + self.assertEqual(getCharsFromFontIndex(0x10 * 0xFF - 15), "\\xFF\\xFF") + with self.assertRaises(AssertionError): + getCharsFromFontIndex(0x10 * 0xFF - 14) + + +if __name__ == '__main__': + unittest.main() From 17824fb376166ed44b97e008309d877d187ee3fe Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Sat, 20 Mar 2021 15:38:06 +0800 Subject: [PATCH 3/3] Fix scrolling text using incorrect length --- source/Core/Src/gui.cpp | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/source/Core/Src/gui.cpp b/source/Core/Src/gui.cpp index ceb554e4..d1a88271 100644 --- a/source/Core/Src/gui.cpp +++ b/source/Core/Src/gui.cpp @@ -12,7 +12,6 @@ #include "Translation.h" #include "cmsis_os.h" #include "main.hpp" -#include "string.h" void gui_Menu(const menuitem *menu); @@ -274,8 +273,33 @@ static void printShortDescription(uint32_t shortDescIndex, uint16_t cursorCharPo OLED::setCursor(OLED::getCursorX() - 2, 0); } +/** + * Counts the number of chars in the string excluding the null terminator. + * This is a custom version of `strlen` which takes into account our custom + * double-byte char encoding. + * @param str The input string. + * @return The length of the string. + */ +static uint16_t str_display_len(const char *const str) { + const uint8_t *next = reinterpret_cast(str); + uint16_t count = 0; + while (next[0]) { + if (next[0] <= 0xF0) { + count++; + next++; + } else { + if (!next[1]) { + break; + } + count++; + next += 2; + } + } + return count; +} + static int userConfirmation(const char *message) { - uint16_t messageWidth = FONT_12_WIDTH * (strlen(message) + 7); + uint16_t messageWidth = FONT_12_WIDTH * (str_display_len(message) + 7); uint32_t messageStart = xTaskGetTickCount(); OLED::setFont(0); @@ -1144,7 +1168,7 @@ void gui_Menu(const menuitem *menu) { if (descriptionStart == 0) descriptionStart = xTaskGetTickCount(); // lower the value - higher the speed - int16_t descriptionWidth = FONT_12_WIDTH * (strlen(menu[currentScreen].description) + 7); + int16_t descriptionWidth = FONT_12_WIDTH * (str_display_len(menu[currentScreen].description) + 7); int16_t descriptionOffset = ((xTaskGetTickCount() - descriptionStart) / (systemSettings.descriptionScrollSpeed == 1 ? (TICKS_100MS / 10) : (TICKS_100MS / 5))); descriptionOffset %= descriptionWidth; // Roll around at the end if (lastOffset != descriptionOffset) {