From 45f793b3385202aab0f5c4b2447c7b90d413a85e Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:57:55 +0100 Subject: [PATCH] =?UTF-8?q?first=20light=20=F0=9F=91=8D=F0=9F=98=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * idea seems to work * scrolling is broken, probably I need to create a unicode-aware ``strlen`` --- wled00/FX.cpp | 23 ++++++++++++++++++--- wled00/FX.h | 2 +- wled00/FX_2Dfcn.cpp | 50 ++++++++++++++++++++++++++++----------------- 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index e9d8c01e..e7104156 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -6810,9 +6810,15 @@ uint16_t mode_2Dscrollingtext(void) { case 5: letterWidth = 5; letterHeight = 12; break; } const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2; - char text[33] = {'\0'}; - unsigned maxLen = (SEGMENT.name) ? min(32, (int)strlen(SEGMENT.name)) : 0; // WLEDMM make it robust against too long segment names - if (SEGMENT.name) for (size_t i=0,j=0; i31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; + char text[33] = {'\0'}; // ToDO use WLED_MAX_SEGNAME_LEN + 1 + unsigned maxLen = (SEGMENT.name) ? min(32, (int)strlen(SEGMENT.name)) : 0; // WLEDMM make it robust against too long segment names, ToDO use WLED_MAX_SEGNAME_LEN + +#if !defined(WLED_ENABLE_FULL_FONTS) + if (SEGMENT.name) for (size_t i=0,j=0; i31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; // unicode killer +#else + if (SEGMENT.name) for (size_t i=0,j=0; i= 0) || (abs(delayTime) > 1500)) { // WLEDMM keep on scrolling if timebase jumps (supersync, or brightness off, or wifi delay) + //WLEDMM ToDO: adjust scolling logic for unicode (single UTF-8 letters need up to 4 chars) if ((numberOfLetters * letterWidth) > cols) ++SEGENV.aux0 %= (numberOfLetters * letterWidth) + cols; // offset else SEGENV.aux0 = (cols + (numberOfLetters * letterWidth))/2; SEGENV.aux1 = (SEGENV.aux1 + 1) & 0xFF; // color shift // WLEDMM changed to prevent overflow @@ -6875,6 +6882,7 @@ uint16_t mode_2Dscrollingtext(void) { } if (SEGENV.check2 && ((numberOfLetters * letterWidth) > cols)) drawShadow = true; // scrolling overlay is easier to read with shadow +#if !defined(WLED_ENABLE_FULL_FONTS) for (int i = 0; i < numberOfLetters; i++) { if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0); @@ -6885,6 +6893,15 @@ uint16_t mode_2Dscrollingtext(void) { } SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2, drawShadow); } +#else + uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0); + uint32_t col2 = BLACK; + if (SEGMENT.check1 && SEGMENT.palette == 0) { + col1 = SEGCOLOR(0); + col2 = SEGCOLOR(2); + } + SEGMENT.drawText((unsigned char*)text, maxLen, numberOfLetters, int(cols) - int(SEGENV.aux0), yoffset, letterWidth, letterHeight, col1, col2, drawShadow); +#endif return FRAMETIME; } diff --git a/wled00/FX.h b/wled00/FX.h index ee39887f..1c4695f8 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -827,7 +827,7 @@ typedef struct Segment { void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, bool drawShadow = false); inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, uint32_t(c) & 0x00FFFFFF, uint32_t(c2) & 0x00FFFFFF); } // automatic inline // unicode-aware wrapper for drawCharacter(), to be called from mode_2Dscrollingtext() - void drawText(const unsigned char* text, size_t maxLen, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, bool drawShadow = false); + void drawText(const unsigned char* text, size_t maxLen, int maxLetters, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, bool drawShadow = false); // #if !WLED_ENABLE_FULL_FONTS => drawText() will fall back to just forwarding each char to drawCharacter() void wu_pixel(uint32_t x, uint32_t y, CRGB c); diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 27605c22..f2883a20 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -892,25 +892,33 @@ bool Segment::jsonToPixels(char * name, uint8_t fileNr) { #endif // unicode-aware wrapper for drawCharacter(), to be called from mode_2Dscrollingtext() -void Segment::drawText(const unsigned char* text, size_t maxLen, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, bool drawShadow) { - size_t textLength = min(maxLen, size_t(WLED_MAX_SEGNAME_LEN)); +void Segment::drawText(const unsigned char* text, size_t maxLen, int maxLetters, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, bool drawShadow) { + if (!isActive()) return; // not active // ToDO: find font _first/_last, based on width / height #if defined(WLED_ENABLE_FULL_FONTS) uint16_t decoded_text[WLED_MAX_SEGNAME_LEN+1] = { 0 }; // UTF-16 converted text. Cannot be longer than WLED_MAX_SEGNAME_LEN size_t utf16_index = 0; - for(const unsigned char* now = text; now != nullptr && now[0] != 0; now = nextUnicode(now, maxLen)) { - decoded_text[utf16_index] = unicodeToWchar16(now, maxLen); // UTF-8 decode into decoded_text - decoded_text[utf16_index] = wchar16ToCodepage437(decoded_text[utf16_index]); // decoded_text to CP437 (in-place conversion) - // toDo: ensure that decoded_text[i] is between console_font_YxZ_first and console_font_YxZ_last - // if (chr < 32 || chr > 126) --> clamp chr - // chr -= 32; // align with font table entries - utf16_index++; + for(const unsigned char* now = text; now != nullptr && now[0] != '\0'; now = nextUnicode(now, maxLen)) { + if (utf16_index <= WLED_MAX_SEGNAME_LEN) { + decoded_text[utf16_index] = unicodeToWchar16(now, maxLen); // UTF-8 decode into decoded_text + decoded_text[utf16_index] = wchar16ToCodepage437(decoded_text[utf16_index]); // decoded_text to CP437 (in-place conversion) + // toDo: ensure that decoded_text[i] is between console_font_YxZ_first and console_font_YxZ_last + // if (chr < 32 || chr > 126) --> clamp chr + // chr -= 32; // align with font table entries + utf16_index++; + } } decoded_text[utf16_index] = 0; // NUL terminate string + size_t textLength = min(utf16_index, size_t(maxLetters)); #else const unsigned char* decoded_text = text; // fallback + size_t textLength = min(strnlen((char*)text, maxLen), size_t(maxLetters)); #endif - // ToDO: pass decoded characters to drawCharacter() + + // pass characters to drawCharacter() + for (int i = 0; i < textLength; i++) { + SEGMENT.drawCharacter((unsigned char) decoded_text[i], x + w*i, y, w, h, color, col2, drawShadow); + } } // draws a raster font character on canvas @@ -918,10 +926,14 @@ void Segment::drawText(const unsigned char* text, size_t maxLen, int16_t x, int1 void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, bool drawShadow) { if (!isActive()) return; // not active #if !defined(WLED_ENABLE_FULL_FONTS) + constexpr int space = 0; // font index of " " (space) if (chr < 32 || chr > 126) return; // legacy mode - only ASCII 32-126 supported chr -= 32; // align with font table entries #else - // ToDO: clamp to font limits + constexpr int space = 31; // font index of " " + // ToDO: clamp to actual font limits + if (chr < 1 || chr > 254) return; // sanity check // ToDO needs improvements + chr = chr -1; // all fonts start at 1 // ToDO needs improvements #endif const uint16_t cols = virtualWidth(); @@ -935,7 +947,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, //if (w<5 || w>6 || h!=8) return; if (drawShadow) w++; // one more column for shadow on right side for (int i = 0; i= rows) break; // drawing off-screen uint8_t bits = 0; @@ -945,19 +957,19 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, switch (font) { // font = w * h case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); - if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_4x6[(chr * h) + i -1]); - break; // 5x8 font + if ((i>space) && drawShadow) bits_up = pgm_read_byte_near(&console_font_4x6[(chr * h) + i -1]); + break; // 4x6 font case 40: bits = pgm_read_byte_near(&console_font_5x8[(chr * h) + i]); - if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_5x8[(chr * h) + i -1]); + if ((i>space) && drawShadow) bits_up = pgm_read_byte_near(&console_font_5x8[(chr * h) + i -1]); break; // 5x8 font case 48: bits = pgm_read_byte_near(&console_font_6x8[(chr * h) + i]); - if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_6x8[(chr * h) + i -1]); + if ((i>space) && drawShadow) bits_up = pgm_read_byte_near(&console_font_6x8[(chr * h) + i -1]); break; // 6x8 font case 63: bits = pgm_read_byte_near(&console_font_7x9[(chr * h) + i]); - if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_7x9[(chr * h) + i -1]); + if ((i>space) && drawShadow) bits_up = pgm_read_byte_near(&console_font_7x9[(chr * h) + i -1]); break; // 7x9 font case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); - if ((i>0) && drawShadow) bits_up = pgm_read_byte_near(&console_font_5x12[(chr * h) + i -1]); + if ((i>space) && drawShadow) bits_up = pgm_read_byte_near(&console_font_5x12[(chr * h) + i -1]); break; // 5x12 font default: return; } @@ -965,7 +977,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint32_t fgCol = uint32_t(col) & 0x00FFFFFF; // WLEDMM cache color value for (int j = 0; j 0 && x0 < cols" if ((bits>>(j+(8-w))) & 0x01) { // bit set & drawing on-screen setPixelColorXY(x0, y0, fgCol);