mirror of
https://github.com/pelgraine/Meck.git
synced 2026-08-06 16:52:46 +02:00
updated emoji sprites, ui word wrapping and formatting
This commit is contained in:
@@ -725,47 +725,81 @@ void drawComposeScreen() {
|
||||
display.setCursor(0, 14);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
|
||||
// Word wrap the compose buffer - calculate chars per line based on actual font width
|
||||
// Word wrap the compose buffer with word-boundary awareness
|
||||
// Uses advance width (cursor movement) not bounding box width for px tracking.
|
||||
// Advance = getTextWidth("cc") - getTextWidth("c") to get true cursor step.
|
||||
int y = 14;
|
||||
char charStr[2] = {0, 0}; // Buffer for single character as string
|
||||
char charStr[2] = {0, 0};
|
||||
char dblStr[3] = {0, 0, 0};
|
||||
|
||||
// Track position in pixels for tight emoji placement
|
||||
int px = 0; // Current pixel X position
|
||||
int px = 0;
|
||||
int lineW = display.width();
|
||||
bool atWordBoundary = true;
|
||||
|
||||
for (int i = 0; i < composePos; i++) {
|
||||
uint8_t b = (uint8_t)composeBuffer[i];
|
||||
|
||||
// Skip emoji padding bytes (used to match wire cost)
|
||||
if (b == EMOJI_PAD_BYTE) continue;
|
||||
|
||||
// Word wrap: when starting a new text word, check if it fits on this line
|
||||
if (atWordBoundary && b != ' ' && !isEmojiEscape(b) && px > 0) {
|
||||
int wordW = 0;
|
||||
for (int j = i; j < composePos; j++) {
|
||||
uint8_t wb = (uint8_t)composeBuffer[j];
|
||||
if (wb == EMOJI_PAD_BYTE) continue;
|
||||
if (wb == ' ' || isEmojiEscape(wb)) break;
|
||||
dblStr[0] = dblStr[1] = (char)wb;
|
||||
charStr[0] = (char)wb;
|
||||
wordW += display.getTextWidth(dblStr) - display.getTextWidth(charStr);
|
||||
}
|
||||
if (px + wordW > lineW) {
|
||||
px = 0;
|
||||
y += 12;
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmojiEscape(b)) {
|
||||
// Check if emoji fits on this line
|
||||
if (px + EMOJI_LG_W > lineW) {
|
||||
px = 0;
|
||||
y += 11;
|
||||
display.setCursor(0, y);
|
||||
y += 12;
|
||||
}
|
||||
const uint8_t* sprite = getEmojiSpriteLg(b);
|
||||
if (sprite) {
|
||||
display.drawXbm(px, y - 1, sprite, EMOJI_LG_W, EMOJI_LG_H);
|
||||
display.drawXbm(px, y, sprite, EMOJI_LG_W, EMOJI_LG_H);
|
||||
}
|
||||
px += EMOJI_LG_W + 1; // sprite width + 1px gap
|
||||
px += EMOJI_LG_W + 1;
|
||||
display.setCursor(px, y);
|
||||
atWordBoundary = true;
|
||||
} else if (b == ' ') {
|
||||
charStr[0] = ' ';
|
||||
dblStr[0] = dblStr[1] = ' ';
|
||||
int adv = display.getTextWidth(dblStr) - display.getTextWidth(charStr);
|
||||
if (px + adv > lineW) {
|
||||
px = 0;
|
||||
y += 12;
|
||||
} else {
|
||||
display.setCursor(px, y);
|
||||
display.print(charStr);
|
||||
px += adv;
|
||||
}
|
||||
atWordBoundary = true;
|
||||
} else {
|
||||
charStr[0] = (char)b;
|
||||
int cw = display.getTextWidth(charStr);
|
||||
if (px + cw > lineW) {
|
||||
dblStr[0] = dblStr[1] = (char)b;
|
||||
int adv = display.getTextWidth(dblStr) - display.getTextWidth(charStr);
|
||||
if (px + adv > lineW) {
|
||||
px = 0;
|
||||
y += 11;
|
||||
display.setCursor(0, y);
|
||||
y += 12;
|
||||
}
|
||||
display.setCursor(px, y);
|
||||
display.print(charStr);
|
||||
px += cw;
|
||||
px += adv;
|
||||
atWordBoundary = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Show cursor
|
||||
display.setCursor(px, y);
|
||||
display.print("_");
|
||||
|
||||
// Status bar
|
||||
|
||||
@@ -182,45 +182,93 @@ public:
|
||||
int maxLinesPerMsg = 8;
|
||||
char charStr[2] = {0, 0};
|
||||
|
||||
// Track position in pixels for tight emoji placement
|
||||
// Track position in pixels for emoji placement
|
||||
// Uses advance width (cursor movement) not bounding box for px tracking
|
||||
int lineW = display.width();
|
||||
int px = display.getTextWidth(tmp); // Pixel X after timestamp
|
||||
|
||||
// Cursor already positioned after timestamp print - don't reset it
|
||||
char dblStr[3] = {0, 0, 0};
|
||||
|
||||
while (pos < textLen && linesForThisMsg < maxLinesPerMsg && y + lineHeight <= maxY) {
|
||||
uint8_t b = (uint8_t)msg->text[pos];
|
||||
|
||||
if (b == EMOJI_PAD_BYTE) { pos++; continue; }
|
||||
|
||||
// Word wrap: when starting a new text word, check if it fits
|
||||
if (b != ' ' && !isEmojiEscape(b) && px > 0) {
|
||||
bool boundary = (pos == 0);
|
||||
if (!boundary) {
|
||||
for (int bp = pos - 1; bp >= 0; bp--) {
|
||||
uint8_t pb = (uint8_t)msg->text[bp];
|
||||
if (pb == EMOJI_PAD_BYTE) continue;
|
||||
boundary = (pb == ' ' || isEmojiEscape(pb));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (boundary) {
|
||||
int wordW = 0;
|
||||
for (int j = pos; j < textLen; j++) {
|
||||
uint8_t wb = (uint8_t)msg->text[j];
|
||||
if (wb == EMOJI_PAD_BYTE) continue;
|
||||
if (wb == ' ' || isEmojiEscape(wb)) break;
|
||||
charStr[0] = (char)wb;
|
||||
dblStr[0] = dblStr[1] = (char)wb;
|
||||
wordW += display.getTextWidth(dblStr) - display.getTextWidth(charStr);
|
||||
}
|
||||
if (px + wordW > lineW) {
|
||||
px = 0;
|
||||
linesForThisMsg++;
|
||||
y += lineHeight;
|
||||
if (linesForThisMsg >= maxLinesPerMsg || y + lineHeight > maxY) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmojiEscape(b)) {
|
||||
// Check if emoji fits on this line
|
||||
if (px + EMOJI_SM_W > lineW) {
|
||||
px = 0;
|
||||
linesForThisMsg++;
|
||||
y += lineHeight;
|
||||
if (linesForThisMsg >= maxLinesPerMsg || y + lineHeight > maxY) break;
|
||||
display.setCursor(0, y);
|
||||
}
|
||||
|
||||
const uint8_t* sprite = getEmojiSpriteSm(b);
|
||||
if (sprite) {
|
||||
display.drawXbm(px, y - 1, sprite, EMOJI_SM_W, EMOJI_SM_H);
|
||||
display.drawXbm(px, y, sprite, EMOJI_SM_W, EMOJI_SM_H);
|
||||
}
|
||||
pos++;
|
||||
px += EMOJI_SM_W + 1; // sprite width + 1px gap
|
||||
px += EMOJI_SM_W + 1;
|
||||
display.setCursor(px, y);
|
||||
} else {
|
||||
charStr[0] = (char)b;
|
||||
int cw = display.getTextWidth(charStr);
|
||||
if (px + cw > lineW) {
|
||||
} else if (b == ' ') {
|
||||
charStr[0] = ' ';
|
||||
dblStr[0] = dblStr[1] = ' ';
|
||||
int adv = display.getTextWidth(dblStr) - display.getTextWidth(charStr);
|
||||
if (px + adv > lineW) {
|
||||
px = 0;
|
||||
linesForThisMsg++;
|
||||
y += lineHeight;
|
||||
if (linesForThisMsg < maxLinesPerMsg && y + lineHeight <= maxY) {
|
||||
display.setCursor(0, y);
|
||||
// skip trailing space at wrap
|
||||
} else break;
|
||||
} else {
|
||||
display.setCursor(px, y);
|
||||
display.print(charStr);
|
||||
px += adv;
|
||||
}
|
||||
pos++;
|
||||
} else {
|
||||
charStr[0] = (char)b;
|
||||
dblStr[0] = dblStr[1] = (char)b;
|
||||
int adv = display.getTextWidth(dblStr) - display.getTextWidth(charStr);
|
||||
if (px + adv > lineW) {
|
||||
px = 0;
|
||||
linesForThisMsg++;
|
||||
y += lineHeight;
|
||||
if (linesForThisMsg < maxLinesPerMsg && y + lineHeight <= maxY) {
|
||||
// continue to print below
|
||||
} else break;
|
||||
}
|
||||
display.setCursor(px, y);
|
||||
display.print(charStr);
|
||||
px += cw;
|
||||
px += adv;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,263 +3,437 @@
|
||||
// Emoji sprites for e-ink display - dual size
|
||||
// Large (12x12) for compose/picker, Small (10x10) for channel view
|
||||
// MSB-first, 2 bytes per row
|
||||
// 47 total emoji: joy/thumbsup/frown first, then 44 original
|
||||
|
||||
#include <stdint.h>
|
||||
#ifdef ESP32
|
||||
#include <pgmspace.h>
|
||||
#endif
|
||||
|
||||
// Large sprites for compose screen and picker
|
||||
#define EMOJI_LG_W 12
|
||||
#define EMOJI_LG_H 12
|
||||
|
||||
// Small sprites for channel message view
|
||||
#define EMOJI_SM_W 10
|
||||
#define EMOJI_SM_H 10
|
||||
|
||||
#define EMOJI_COUNT 20
|
||||
#define EMOJI_COUNT 47
|
||||
|
||||
// Escape codes used in sanitized message text
|
||||
// Bytes 0x01 through 0x14 map to emoji indices 0-19
|
||||
#define EMOJI_ESCAPE_START 0x01
|
||||
#define EMOJI_ESCAPE_END 0x14
|
||||
#define EMOJI_PAD_BYTE 0x15 // Padding byte after escape to fill buffer to UTF-8 wire cost
|
||||
// Escape codes in 0x80+ range - safe from keyboard ASCII (32-126)
|
||||
#define EMOJI_ESCAPE_START 0x80
|
||||
#define EMOJI_ESCAPE_END 0xAE // 0x80 + 46
|
||||
#define EMOJI_PAD_BYTE 0x7F // DEL, not typeable (key < 127 guard)
|
||||
|
||||
// ======== LARGE 12x12 SPRITES ========
|
||||
|
||||
// [0] joy (most common mesh emoji)
|
||||
static const uint8_t emoji_lg_joy[] PROGMEM = {
|
||||
0x1F,0x80, 0x20,0x40, 0x59,0xA0, 0x59,0xA0, 0x80,0x10, 0xA0,0x50, 0x9F,0x90, 0x40,0x20, 0x20,0x40, 0x1F,0x80, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [1] thumbsup
|
||||
static const uint8_t emoji_lg_thumbsup[] PROGMEM = {
|
||||
0x00,0x00, 0x70,0x00, 0x70,0x00, 0x70,0x00, 0x7F,0x80, 0xFF,0x80, 0xFF,0x80, 0x7F,0x80, 0x3F,0x80, 0x1F,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [2] frown
|
||||
static const uint8_t emoji_lg_frown[] PROGMEM = {
|
||||
0x1F,0x80, 0x20,0x40, 0x59,0xA0, 0x59,0xA0, 0x80,0x10, 0x9F,0x90, 0xA0,0x50, 0x40,0x20, 0x20,0x40, 0x1F,0x80, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [3] wireless
|
||||
static const uint8_t emoji_lg_wireless[] PROGMEM = {
|
||||
0x00,0x00, 0x3F,0xC0, 0x60,0x60, 0xC0,0x30,
|
||||
0x0F,0x00, 0x19,0x80, 0x30,0xC0, 0x00,0x00,
|
||||
0x06,0x00, 0x0F,0x00, 0x06,0x00, 0x00,0x00,
|
||||
0x00,0x00, 0x3F,0xC0, 0x60,0x60, 0xC0,0x30, 0x0F,0x00, 0x19,0x80, 0x30,0xC0, 0x00,0x00, 0x06,0x00, 0x0F,0x00, 0x06,0x00, 0x00,0x00,
|
||||
};
|
||||
// [4] infinity
|
||||
static const uint8_t emoji_lg_infinity[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0x61,0x80, 0x92,0x40,
|
||||
0x8C,0x40, 0x8C,0x40, 0x92,0x40, 0x61,0x80,
|
||||
0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00,
|
||||
0x00,0x00, 0x00,0x00, 0x61,0x80, 0x92,0x40, 0x8C,0x40, 0x8C,0x40, 0x92,0x40, 0x61,0x80, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [5] trex
|
||||
static const uint8_t emoji_lg_trex[] PROGMEM = {
|
||||
0x03,0xE0, 0x06,0xA0, 0x07,0xE0, 0x0C,0x00,
|
||||
0x5C,0x00, 0x7C,0x00, 0x3C,0x00, 0x38,0x00,
|
||||
0x3C,0x00, 0x36,0x00, 0x22,0x00, 0x33,0x00,
|
||||
0x03,0xE0, 0x06,0xA0, 0x07,0xE0, 0x0C,0x00, 0x5C,0x00, 0x7C,0x00, 0x3C,0x00, 0x38,0x00, 0x3C,0x00, 0x36,0x00, 0x22,0x00, 0x33,0x00,
|
||||
};
|
||||
// [6] skull
|
||||
static const uint8_t emoji_lg_skull[] PROGMEM = {
|
||||
0x1F,0x80, 0x20,0x40, 0x59,0xA0, 0x59,0xA0,
|
||||
0x40,0x20, 0x49,0x20, 0x2F,0x40, 0x1F,0x80,
|
||||
0x96,0x90, 0x66,0x60, 0x36,0xC0, 0x96,0x90,
|
||||
0x1F,0x80, 0x20,0x40, 0x59,0xA0, 0x59,0xA0, 0x40,0x20, 0x49,0x20, 0x2F,0x40, 0x1F,0x80, 0x96,0x90, 0x66,0x60, 0x36,0xC0, 0x96,0x90,
|
||||
};
|
||||
// [7] cross
|
||||
static const uint8_t emoji_lg_cross[] PROGMEM = {
|
||||
0x0F,0x00, 0x0F,0x00, 0x0F,0x00, 0x3F,0xC0,
|
||||
0x3F,0xC0, 0x3F,0xC0, 0x0F,0x00, 0x0F,0x00,
|
||||
0x0F,0x00, 0x0F,0x00, 0x0F,0x00, 0x0F,0x00,
|
||||
0x0F,0x00, 0x0F,0x00, 0x0F,0x00, 0x3F,0xC0, 0x3F,0xC0, 0x3F,0xC0, 0x0F,0x00, 0x0F,0x00, 0x0F,0x00, 0x0F,0x00, 0x0F,0x00, 0x0F,0x00,
|
||||
};
|
||||
// [8] lightning
|
||||
static const uint8_t emoji_lg_lightning[] PROGMEM = {
|
||||
0x03,0x00, 0x07,0x00, 0x0E,0x00, 0x1C,0x00,
|
||||
0x3F,0x80, 0x01,0x80, 0x03,0x00, 0x06,0x00,
|
||||
0x0C,0x00, 0x18,0x00, 0x30,0x00, 0x00,0x00,
|
||||
0x03,0x00, 0x07,0x00, 0x0E,0x00, 0x1C,0x00, 0x3F,0x80, 0x01,0x80, 0x03,0x00, 0x06,0x00, 0x0C,0x00, 0x18,0x00, 0x30,0x00, 0x00,0x00,
|
||||
};
|
||||
// [9] tophat
|
||||
static const uint8_t emoji_lg_tophat[] PROGMEM = {
|
||||
0x00,0x00, 0x1F,0x80, 0x3F,0xC0, 0x3F,0xC0,
|
||||
0x3F,0xC0, 0x3F,0xC0, 0x3F,0xC0, 0x3F,0xC0,
|
||||
0x7F,0xE0, 0xFF,0xF0, 0xFF,0xF0, 0x00,0x00,
|
||||
0x00,0x00, 0x1F,0x80, 0x3F,0xC0, 0x3F,0xC0, 0x3F,0xC0, 0x3F,0xC0, 0x20,0x40, 0x3F,0xC0, 0x7F,0xE0, 0xFF,0xF0, 0xFF,0xF0, 0x00,0x00,
|
||||
};
|
||||
// [10] motorcycle
|
||||
static const uint8_t emoji_lg_motorcycle[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0x03,0x80, 0x1F,0xC0,
|
||||
0x3F,0xC0, 0x7F,0xC0, 0xFF,0xE0, 0xDF,0x60,
|
||||
0x51,0x40, 0xE0,0xE0, 0x40,0x40, 0x00,0x00,
|
||||
0x00,0x00, 0x00,0x00, 0x0F,0x00, 0x1F,0x80, 0x7F,0xE0, 0xDF,0xB0, 0xD0,0xB0, 0xD0,0xB0, 0xDF,0xB0, 0x60,0x60, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [11] seedling
|
||||
static const uint8_t emoji_lg_seedling[] PROGMEM = {
|
||||
0x00,0x00, 0x30,0x00, 0x79,0x80, 0x7B,0xC0,
|
||||
0x33,0xC0, 0x1F,0x80, 0x06,0x00, 0x06,0x00,
|
||||
0x06,0x00, 0x06,0x00, 0x00,0x00, 0x00,0x00,
|
||||
0x00,0x00, 0x30,0x00, 0x79,0x80, 0x7B,0xC0, 0x33,0xC0, 0x1F,0x80, 0x06,0x00, 0x06,0x00, 0x06,0x00, 0x06,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [12] flag_au
|
||||
static const uint8_t emoji_lg_flag_au[] PROGMEM = {
|
||||
0x00,0x00, 0x32,0x40, 0x4A,0x40, 0x4A,0x40,
|
||||
0x7A,0x40, 0x4A,0x40, 0x49,0x80, 0x00,0x00,
|
||||
0xFF,0xF0, 0x00,0x00, 0xFF,0xF0, 0x00,0x00,
|
||||
0x00,0x00, 0x32,0x40, 0x4A,0x40, 0x4A,0x40, 0x7A,0x40, 0x4A,0x40, 0x49,0x80, 0x00,0x00, 0xFF,0xF0, 0x00,0x00, 0xFF,0xF0, 0x00,0x00,
|
||||
};
|
||||
// [13] umbrella
|
||||
static const uint8_t emoji_lg_umbrella[] PROGMEM = {
|
||||
0x06,0x00, 0x1F,0x80, 0x3F,0xC0, 0x7F,0xE0,
|
||||
0xFF,0xF0, 0xDB,0x70, 0x06,0x00, 0x06,0x00,
|
||||
0x06,0x00, 0x06,0x00, 0x46,0x00, 0x3C,0x00,
|
||||
0x06,0x00, 0x1F,0x80, 0x3F,0xC0, 0x7F,0xE0, 0xFF,0xF0, 0xDB,0x70, 0x06,0x00, 0x06,0x00, 0x06,0x00, 0x06,0x00, 0x46,0x00, 0x3C,0x00,
|
||||
};
|
||||
// [14] nazar
|
||||
static const uint8_t emoji_lg_nazar[] PROGMEM = {
|
||||
0x1F,0x80, 0x20,0x40, 0x4F,0x20, 0x99,0x90,
|
||||
0xB6,0xD0, 0xB6,0xD0, 0xB6,0xD0, 0x99,0x90,
|
||||
0x4F,0x20, 0x20,0x40, 0x1F,0x80, 0x00,0x00,
|
||||
0x1F,0x80, 0x20,0x40, 0x4F,0x20, 0x99,0x90, 0xB6,0xD0, 0xB6,0xD0, 0xB6,0xD0, 0x99,0x90, 0x4F,0x20, 0x20,0x40, 0x1F,0x80, 0x00,0x00,
|
||||
};
|
||||
// [15] globe
|
||||
static const uint8_t emoji_lg_globe[] PROGMEM = {
|
||||
0x1F,0x80, 0x34,0xC0, 0x66,0x60, 0x4F,0x20,
|
||||
0x8E,0x10, 0x86,0x10, 0x80,0x30, 0x46,0x60,
|
||||
0x43,0xE0, 0x30,0xC0, 0x1F,0x80, 0x00,0x00,
|
||||
0x1F,0x80, 0x34,0xC0, 0x66,0x60, 0x4F,0x20, 0x8E,0x10, 0x86,0x10, 0x80,0x30, 0x46,0x60, 0x43,0xE0, 0x30,0xC0, 0x1F,0x80, 0x00,0x00,
|
||||
};
|
||||
// [16] radioactive
|
||||
static const uint8_t emoji_lg_radioactive[] PROGMEM = {
|
||||
0x00,0x00, 0x22,0x40, 0x32,0xC0, 0x32,0xC0,
|
||||
0x1B,0x40, 0x00,0x00, 0x0F,0x00, 0x0F,0x00,
|
||||
0x00,0x00, 0x60,0x20, 0x39,0xC0, 0x0F,0x00,
|
||||
0x00,0x00, 0x22,0x40, 0x32,0xC0, 0x32,0xC0, 0x1B,0x40, 0x00,0x00, 0x0F,0x00, 0x0F,0x00, 0x00,0x00, 0x60,0x20, 0x39,0xC0, 0x0F,0x00,
|
||||
};
|
||||
// [17] cow
|
||||
static const uint8_t emoji_lg_cow[] PROGMEM = {
|
||||
0x00,0x00, 0xC0,0x60, 0x6E,0xC0, 0x3F,0x80,
|
||||
0x2A,0x80, 0x3F,0x80, 0x3F,0x80, 0x7F,0xC0,
|
||||
0x5F,0x40, 0x5F,0x40, 0x11,0x00, 0x31,0x80,
|
||||
0x00,0x00, 0xC0,0x60, 0x6E,0xC0, 0x3F,0x80, 0x2A,0x80, 0x3F,0x80, 0x3F,0x80, 0x7F,0xC0, 0x5F,0x40, 0x5F,0x40, 0x11,0x00, 0x31,0x80,
|
||||
};
|
||||
// [18] alien
|
||||
static const uint8_t emoji_lg_alien[] PROGMEM = {
|
||||
0x1F,0x80, 0x3F,0xC0, 0x7F,0xE0, 0x76,0xE0,
|
||||
0xF6,0xF0, 0x96,0x90, 0x7F,0xE0, 0x36,0xC0,
|
||||
0x3F,0xC0, 0x16,0x80, 0x0F,0x00, 0x06,0x00,
|
||||
0x1F,0x80, 0x3F,0xC0, 0x7F,0xE0, 0x76,0xE0, 0xF6,0xF0, 0x96,0x90, 0x7F,0xE0, 0x36,0xC0, 0x3F,0xC0, 0x16,0x80, 0x0F,0x00, 0x06,0x00,
|
||||
};
|
||||
// [19] invader
|
||||
static const uint8_t emoji_lg_invader[] PROGMEM = {
|
||||
0x10,0x80, 0x09,0x00, 0x1F,0x80, 0x36,0xC0,
|
||||
0x7F,0xE0, 0x5F,0xA0, 0x50,0xA0, 0x50,0xA0,
|
||||
0x19,0x80, 0x19,0x80, 0x30,0xC0, 0x00,0x00,
|
||||
0x10,0x80, 0x09,0x00, 0x1F,0x80, 0x36,0xC0, 0x7F,0xE0, 0x5F,0xA0, 0x50,0xA0, 0x50,0xA0, 0x19,0x80, 0x19,0x80, 0x30,0xC0, 0x00,0x00,
|
||||
};
|
||||
// [20] dagger
|
||||
static const uint8_t emoji_lg_dagger[] PROGMEM = {
|
||||
0x00,0x20, 0x00,0x60, 0x00,0xC0, 0x01,0x80,
|
||||
0x03,0x00, 0x06,0x00, 0x0C,0x00, 0x18,0x00,
|
||||
0x38,0x00, 0x58,0x00, 0x28,0x00, 0x18,0x00,
|
||||
0x00,0x20, 0x00,0x60, 0x00,0xC0, 0x01,0x80, 0x03,0x00, 0x06,0x00, 0x0C,0x00, 0x18,0x00, 0x38,0x00, 0x58,0x00, 0x28,0x00, 0x18,0x00,
|
||||
};
|
||||
// [21] grimace
|
||||
static const uint8_t emoji_lg_grimace[] PROGMEM = {
|
||||
0x1F,0x80, 0x20,0x40, 0x59,0xA0, 0x59,0xA0,
|
||||
0x40,0x20, 0x40,0x20, 0x5F,0xA0, 0x55,0x40,
|
||||
0x5F,0xA0, 0x20,0x40, 0x1F,0x80, 0x00,0x00,
|
||||
0x1F,0x80, 0x20,0x40, 0x59,0xA0, 0x59,0xA0, 0x40,0x20, 0x40,0x20, 0x5F,0xA0, 0x55,0x40, 0x5F,0xA0, 0x20,0x40, 0x1F,0x80, 0x00,0x00,
|
||||
};
|
||||
// [22] telephone
|
||||
static const uint8_t emoji_lg_telephone[] PROGMEM = {
|
||||
0x00,0x00, 0x7F,0xE0, 0xC0,0x30, 0xC0,0x30,
|
||||
0x60,0x60, 0x30,0xC0, 0x1F,0x80, 0x0F,0x00,
|
||||
0x1F,0x80, 0x3F,0xC0, 0x7F,0xE0, 0x00,0x00,
|
||||
0x00,0x00, 0x7F,0xE0, 0xC0,0x30, 0xC0,0x30, 0x60,0x60, 0x30,0xC0, 0x1F,0x80, 0x0F,0x00, 0x1F,0x80, 0x3F,0xC0, 0x7F,0xE0, 0x00,0x00,
|
||||
};
|
||||
// [23] mountain
|
||||
static const uint8_t emoji_lg_mountain[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0x06,0x00, 0x0F,0x00, 0x19,0x80, 0x30,0xC0, 0x66,0x60, 0xCF,0x30, 0x9F,0x90, 0xFF,0xF0, 0xFF,0xF0, 0x00,0x00,
|
||||
};
|
||||
// [24] end_arrow
|
||||
static const uint8_t emoji_lg_end_arrow[] PROGMEM = {
|
||||
0x00,0x00, 0x7B,0x60, 0x43,0x60, 0x42,0xA0, 0x72,0xA0, 0x43,0x60, 0x43,0x60, 0x7B,0x60, 0x00,0x00, 0x06,0x00, 0x0F,0x00, 0x06,0x00,
|
||||
};
|
||||
// [25] hollow_circle
|
||||
static const uint8_t emoji_lg_hollow_circle[] PROGMEM = {
|
||||
0x1F,0x80, 0x20,0x40, 0x40,0x20, 0x80,0x10, 0x80,0x10, 0x80,0x10, 0x80,0x10, 0x80,0x10, 0x40,0x20, 0x20,0x40, 0x1F,0x80, 0x00,0x00,
|
||||
};
|
||||
// [26] dragon
|
||||
static const uint8_t emoji_lg_dragon[] PROGMEM = {
|
||||
0x60,0x00, 0xF0,0x00, 0x76,0x00, 0x3F,0x00, 0x1F,0x00, 0x0F,0x00, 0x1F,0x80, 0x3F,0xC0, 0x79,0xE0, 0x30,0xC0, 0x20,0x40, 0x30,0xC0,
|
||||
};
|
||||
// [27] globe_meridians
|
||||
static const uint8_t emoji_lg_globe_meridians[] PROGMEM = {
|
||||
0x1F,0x80, 0x26,0x40, 0x46,0x20, 0x86,0x10, 0xFF,0xF0, 0x86,0x10, 0x86,0x10, 0x46,0x20, 0x26,0x40, 0x1F,0x80, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [28] eggplant
|
||||
static const uint8_t emoji_lg_eggplant[] PROGMEM = {
|
||||
0x01,0x80, 0x03,0x00, 0x07,0x00, 0x0F,0x00, 0x1F,0x00, 0x3F,0x00, 0x3F,0x00, 0x7E,0x00, 0x7C,0x00, 0x78,0x00, 0x30,0x00, 0x00,0x00,
|
||||
};
|
||||
// [29] shield
|
||||
static const uint8_t emoji_lg_shield[] PROGMEM = {
|
||||
0x00,0x00, 0x7F,0xE0, 0x7F,0xE0, 0x6F,0x60, 0x6F,0x60, 0x6F,0x60, 0x36,0xC0, 0x3F,0xC0, 0x1F,0x80, 0x0F,0x00, 0x06,0x00, 0x00,0x00,
|
||||
};
|
||||
// [30] goggles
|
||||
static const uint8_t emoji_lg_goggles[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0x00,0x00, 0x79,0xE0, 0xCF,0x30, 0x86,0x10, 0x86,0x10, 0xCF,0x30, 0x79,0xE0, 0x00,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [31] lizard
|
||||
static const uint8_t emoji_lg_lizard[] PROGMEM = {
|
||||
0x00,0x60, 0x00,0xF0, 0x01,0xB0, 0x03,0x00, 0x06,0x00, 0x7C,0x00, 0xC0,0x00, 0x60,0x00, 0x30,0x00, 0x18,0x00, 0x0E,0x00, 0x06,0x00,
|
||||
};
|
||||
// [32] zany_face
|
||||
static const uint8_t emoji_lg_zany_face[] PROGMEM = {
|
||||
0x1F,0x80, 0x20,0x40, 0x59,0x20, 0x58,0xA0, 0x40,0x20, 0x40,0x20, 0x4F,0x20, 0x50,0xA0, 0x20,0x40, 0x1F,0x80, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [33] kangaroo
|
||||
static const uint8_t emoji_lg_kangaroo[] PROGMEM = {
|
||||
0x0E,0x00, 0x1F,0x00, 0x1F,0x00, 0x0E,0x00, 0x0F,0x00, 0x07,0x80, 0x47,0x80, 0x65,0x80, 0x3C,0x80, 0x18,0x80, 0x10,0xC0, 0x18,0xF0,
|
||||
};
|
||||
// [34] feather
|
||||
static const uint8_t emoji_lg_feather[] PROGMEM = {
|
||||
0x00,0x20, 0x00,0x60, 0x00,0xC0, 0x01,0x80, 0x03,0x00, 0x06,0x00, 0x0C,0x00, 0x18,0x00, 0x30,0x00, 0x60,0x00, 0x70,0x00, 0x00,0x00,
|
||||
};
|
||||
// [35] bright
|
||||
static const uint8_t emoji_lg_bright[] PROGMEM = {
|
||||
0x06,0x00, 0x26,0x40, 0x16,0x80, 0x0F,0x00, 0x6F,0x60, 0x6F,0x60, 0x0F,0x00, 0x16,0x80, 0x26,0x40, 0x06,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [36] part_alt
|
||||
static const uint8_t emoji_lg_part_alt[] PROGMEM = {
|
||||
0x60,0xC0, 0x71,0xC0, 0x3B,0x80, 0x1F,0x00, 0x0E,0x00, 0x0E,0x00, 0x1F,0x00, 0x3B,0x80, 0x71,0xC0, 0x60,0xC0, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [37] motorboat
|
||||
static const uint8_t emoji_lg_motorboat[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0x02,0x00, 0x07,0x00, 0x0F,0x80, 0x1F,0xC0, 0xFF,0xF0, 0x7F,0xE0, 0x3F,0xC0, 0x1F,0x80, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [38] playing_card
|
||||
static const uint8_t emoji_lg_playing_card[] PROGMEM = {
|
||||
0x7F,0x80, 0x40,0x80, 0x4C,0x80, 0x52,0x80, 0x52,0x80, 0x52,0x80, 0x4C,0x80, 0x40,0x80, 0x7F,0x80, 0x00,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [39] satellite
|
||||
static const uint8_t emoji_lg_satellite[] PROGMEM = {
|
||||
0x78,0x00, 0xCC,0x00, 0x84,0x00, 0xCD,0x00, 0x7B,0x00, 0x03,0x80, 0x01,0xC0, 0x00,0xE0, 0x00,0x60, 0x00,0x20, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [40] customs
|
||||
static const uint8_t emoji_lg_customs[] PROGMEM = {
|
||||
0x1F,0x80, 0x20,0x40, 0x40,0x20, 0x4F,0x20, 0x50,0xA0, 0x50,0xA0, 0x4F,0x20, 0x42,0x20, 0x22,0x40, 0x1F,0x80, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [41] cowboy
|
||||
static const uint8_t emoji_lg_cowboy[] PROGMEM = {
|
||||
0x0F,0x00, 0x0F,0x00, 0x7F,0xE0, 0xFF,0xF0, 0x00,0x00, 0x3F,0xC0, 0x59,0xA0, 0x40,0x20, 0x4F,0x20, 0x20,0x40, 0x1F,0x80, 0x00,0x00,
|
||||
};
|
||||
// [42] wheel
|
||||
static const uint8_t emoji_lg_wheel[] PROGMEM = {
|
||||
0x1F,0x80, 0x26,0x40, 0x46,0x20, 0x9F,0x90, 0xB6,0xD0, 0xFF,0xF0, 0xB6,0xD0, 0x9F,0x90, 0x46,0x20, 0x26,0x40, 0x1F,0x80, 0x00,0x00,
|
||||
};
|
||||
// [43] koala
|
||||
static const uint8_t emoji_lg_koala[] PROGMEM = {
|
||||
0x60,0x60, 0xF0,0xF0, 0xF0,0xF0, 0x76,0xE0, 0x26,0x40, 0x2F,0x40, 0x26,0x40, 0x30,0xC0, 0x1F,0x80, 0x00,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
// [44] control_knobs
|
||||
static const uint8_t emoji_lg_control_knobs[] PROGMEM = {
|
||||
0x00,0x00, 0x33,0x30, 0x33,0x30, 0x33,0x30, 0x33,0x30, 0x33,0x30, 0x33,0x30, 0x7B,0x30, 0x37,0xB0, 0x33,0x70, 0x33,0x30, 0x00,0x00,
|
||||
};
|
||||
// [45] peach
|
||||
static const uint8_t emoji_lg_peach[] PROGMEM = {
|
||||
0x06,0x00, 0x0C,0x00, 0x1E,0x00, 0x3F,0x00, 0x7F,0x80, 0x7B,0xC0, 0x7B,0xC0, 0x7B,0xC0, 0x3F,0xC0, 0x1F,0x80, 0x0F,0x00, 0x00,0x00,
|
||||
};
|
||||
// [46] racing_car
|
||||
static const uint8_t emoji_lg_racing_car[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0x00,0x00, 0x07,0x80, 0x0F,0xC0, 0x7F,0xE0, 0xFF,0xF0, 0xFF,0xF0, 0x6F,0x60, 0x49,0x20, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
|
||||
static const uint8_t* const EMOJI_SPRITES_LG[] PROGMEM = {
|
||||
emoji_lg_joy, emoji_lg_thumbsup, emoji_lg_frown,
|
||||
emoji_lg_wireless, emoji_lg_infinity, emoji_lg_trex, emoji_lg_skull, emoji_lg_cross,
|
||||
emoji_lg_lightning, emoji_lg_tophat, emoji_lg_motorcycle, emoji_lg_seedling, emoji_lg_flag_au,
|
||||
emoji_lg_umbrella, emoji_lg_nazar, emoji_lg_globe, emoji_lg_radioactive, emoji_lg_cow,
|
||||
emoji_lg_alien, emoji_lg_invader, emoji_lg_dagger, emoji_lg_grimace, emoji_lg_telephone,
|
||||
emoji_lg_mountain, emoji_lg_end_arrow, emoji_lg_hollow_circle, emoji_lg_dragon, emoji_lg_globe_meridians,
|
||||
emoji_lg_eggplant, emoji_lg_shield, emoji_lg_goggles, emoji_lg_lizard, emoji_lg_zany_face,
|
||||
emoji_lg_kangaroo, emoji_lg_feather, emoji_lg_bright, emoji_lg_part_alt, emoji_lg_motorboat,
|
||||
emoji_lg_playing_card, emoji_lg_satellite, emoji_lg_customs, emoji_lg_cowboy, emoji_lg_wheel,
|
||||
emoji_lg_koala, emoji_lg_control_knobs, emoji_lg_peach, emoji_lg_racing_car,
|
||||
};
|
||||
|
||||
// ======== SMALL 10x10 SPRITES ========
|
||||
|
||||
static const uint8_t emoji_sm_joy[] PROGMEM = {
|
||||
0x3F,0x00, 0x61,0x80, 0xF3,0xC0, 0x80,0x40, 0xA1,0x40, 0x9E,0x40, 0x40,0x80, 0x3F,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_thumbsup[] PROGMEM = {
|
||||
0x70,0x00, 0x70,0x00, 0x70,0x00, 0x7F,0x00, 0xFF,0x00, 0xFF,0x00, 0x7F,0x00, 0x3E,0x00, 0x1C,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_frown[] PROGMEM = {
|
||||
0x3F,0x00, 0x61,0x80, 0xF3,0xC0, 0x80,0x40, 0x9E,0x40, 0xA1,0x40, 0x40,0x80, 0x3F,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_wireless[] PROGMEM = {
|
||||
0x00,0x00, 0x7F,0x80, 0xC0,0xC0, 0x1E,0x00,
|
||||
0x33,0x00, 0x21,0x00, 0x00,0x00, 0x0C,0x00,
|
||||
0x0C,0x00, 0x00,0x00,
|
||||
0x00,0x00, 0x7F,0x80, 0xC0,0xC0, 0x1E,0x00, 0x33,0x00, 0x21,0x00, 0x00,0x00, 0x0C,0x00, 0x0C,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_infinity[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0xE7,0x00, 0x99,0x00,
|
||||
0x99,0x00, 0xA5,0x00, 0x42,0x00, 0x00,0x00,
|
||||
0x00,0x00, 0x00,0x00,
|
||||
0x00,0x00, 0x00,0x00, 0xE7,0x00, 0x99,0x00, 0x99,0x00, 0xA5,0x00, 0x42,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_trex[] PROGMEM = {
|
||||
0x07,0x80, 0x0F,0x80, 0x0F,0x80, 0x58,0x00,
|
||||
0x78,0x00, 0x38,0x00, 0x38,0x00, 0x3C,0x00,
|
||||
0x24,0x00, 0x26,0x00,
|
||||
0x07,0x80, 0x0F,0x80, 0x0F,0x80, 0x58,0x00, 0x78,0x00, 0x38,0x00, 0x38,0x00, 0x3C,0x00, 0x24,0x00, 0x26,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_skull[] PROGMEM = {
|
||||
0x3F,0x00, 0x61,0x80, 0x73,0x80, 0x40,0x80,
|
||||
0x52,0x80, 0x3F,0x00, 0x3F,0x00, 0xED,0xC0,
|
||||
0x6D,0x80, 0xAD,0x40,
|
||||
0x3F,0x00, 0x61,0x80, 0x73,0x80, 0x40,0x80, 0x52,0x80, 0x3F,0x00, 0x3F,0x00, 0xED,0xC0, 0x6D,0x80, 0xAD,0x40,
|
||||
};
|
||||
static const uint8_t emoji_sm_cross[] PROGMEM = {
|
||||
0x1E,0x00, 0x1E,0x00, 0x3F,0x00, 0x3F,0x00,
|
||||
0x3F,0x00, 0x1E,0x00, 0x1E,0x00, 0x1E,0x00,
|
||||
0x1E,0x00, 0x1E,0x00,
|
||||
0x1E,0x00, 0x1E,0x00, 0x3F,0x00, 0x3F,0x00, 0x3F,0x00, 0x1E,0x00, 0x1E,0x00, 0x1E,0x00, 0x1E,0x00, 0x1E,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_lightning[] PROGMEM = {
|
||||
0x06,0x00, 0x0E,0x00, 0x1C,0x00, 0x3E,0x00,
|
||||
0x03,0x00, 0x06,0x00, 0x0C,0x00, 0x18,0x00,
|
||||
0x30,0x00, 0x00,0x00,
|
||||
0x06,0x00, 0x0E,0x00, 0x1C,0x00, 0x3E,0x00, 0x03,0x00, 0x06,0x00, 0x0C,0x00, 0x18,0x00, 0x30,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_tophat[] PROGMEM = {
|
||||
0x00,0x00, 0x3F,0x00, 0x3F,0x00, 0x3F,0x00,
|
||||
0x3F,0x00, 0x3F,0x00, 0x7F,0x80, 0xFF,0xC0,
|
||||
0xFF,0xC0, 0x00,0x00,
|
||||
0x00,0x00, 0x3F,0x00, 0x3F,0x00, 0x3F,0x00, 0x21,0x00, 0x3F,0x00, 0x7F,0x80, 0xFF,0xC0, 0xFF,0xC0, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_motorcycle[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0x1F,0x00, 0x3F,0x00,
|
||||
0x7F,0x00, 0xFF,0x80, 0xFF,0x80, 0xE3,0x80,
|
||||
0xC1,0x80, 0x00,0x00,
|
||||
0x00,0x00, 0x1E,0x00, 0x7F,0x80, 0xDE,0xC0, 0xD2,0xC0, 0xD2,0xC0, 0xDE,0xC0, 0x61,0x80, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_seedling[] PROGMEM = {
|
||||
0x00,0x00, 0x70,0x00, 0x77,0x00, 0x77,0x00,
|
||||
0x3F,0x00, 0x0C,0x00, 0x0C,0x00, 0x0C,0x00,
|
||||
0x00,0x00, 0x00,0x00,
|
||||
0x00,0x00, 0x70,0x00, 0x77,0x00, 0x77,0x00, 0x3F,0x00, 0x0C,0x00, 0x0C,0x00, 0x0C,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_flag_au[] PROGMEM = {
|
||||
0x00,0x00, 0x75,0x00, 0x55,0x00, 0x75,0x00,
|
||||
0x55,0x00, 0x53,0x00, 0x00,0x00, 0xFF,0xC0,
|
||||
0xFF,0xC0, 0x00,0x00,
|
||||
0x00,0x00, 0x75,0x00, 0x55,0x00, 0x75,0x00, 0x55,0x00, 0x53,0x00, 0x00,0x00, 0xFF,0xC0, 0xFF,0xC0, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_umbrella[] PROGMEM = {
|
||||
0x0C,0x00, 0x3F,0x00, 0x7F,0x80, 0xFF,0xC0,
|
||||
0xF7,0xC0, 0x0C,0x00, 0x0C,0x00, 0x0C,0x00,
|
||||
0x4C,0x00, 0x78,0x00,
|
||||
0x0C,0x00, 0x3F,0x00, 0x7F,0x80, 0xFF,0xC0, 0xF7,0xC0, 0x0C,0x00, 0x0C,0x00, 0x0C,0x00, 0x4C,0x00, 0x78,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_nazar[] PROGMEM = {
|
||||
0x3F,0x00, 0x40,0x80, 0x9E,0x40, 0xBF,0x40,
|
||||
0xAD,0x40, 0xBF,0x40, 0x9E,0x40, 0x4C,0x80,
|
||||
0x3F,0x00, 0x00,0x00,
|
||||
0x3F,0x00, 0x40,0x80, 0x9E,0x40, 0xBF,0x40, 0xAD,0x40, 0xBF,0x40, 0x9E,0x40, 0x4C,0x80, 0x3F,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_globe[] PROGMEM = {
|
||||
0x3F,0x00, 0x69,0x80, 0x4C,0x80, 0x9C,0x40,
|
||||
0x8C,0x40, 0x80,0xC0, 0x4D,0x80, 0x67,0x80,
|
||||
0x3F,0x00, 0x00,0x00,
|
||||
0x3F,0x00, 0x69,0x80, 0x4C,0x80, 0x9C,0x40, 0x8C,0x40, 0x80,0xC0, 0x4D,0x80, 0x67,0x80, 0x3F,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_radioactive[] PROGMEM = {
|
||||
0x00,0x00, 0x25,0x00, 0x25,0x00, 0x37,0x00,
|
||||
0x00,0x00, 0x1E,0x00, 0x1E,0x00, 0x40,0x00,
|
||||
0x73,0x80, 0x1E,0x00,
|
||||
0x00,0x00, 0x25,0x00, 0x25,0x00, 0x37,0x00, 0x00,0x00, 0x1E,0x00, 0x1E,0x00, 0x40,0x00, 0x73,0x80, 0x1E,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_cow[] PROGMEM = {
|
||||
0x00,0x00, 0xC1,0x80, 0x7F,0x00, 0x3F,0x00,
|
||||
0x3F,0x00, 0x7F,0x00, 0x7F,0x00, 0x7F,0x00,
|
||||
0x36,0x00, 0x23,0x00,
|
||||
0x00,0x00, 0xC1,0x80, 0x7F,0x00, 0x3F,0x00, 0x3F,0x00, 0x7F,0x00, 0x7F,0x00, 0x7F,0x00, 0x36,0x00, 0x23,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_alien[] PROGMEM = {
|
||||
0x3F,0x00, 0x7F,0x80, 0x7F,0x80, 0xED,0xC0,
|
||||
0xAD,0x40, 0x7F,0x80, 0x3F,0x00, 0x3F,0x00,
|
||||
0x1E,0x00, 0x0C,0x00,
|
||||
0x3F,0x00, 0x7F,0x80, 0x7F,0x80, 0xED,0xC0, 0xAD,0x40, 0x7F,0x80, 0x3F,0x00, 0x3F,0x00, 0x1E,0x00, 0x0C,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_invader[] PROGMEM = {
|
||||
0x33,0x00, 0x1E,0x00, 0x3F,0x00, 0x7F,0x80,
|
||||
0x7F,0x80, 0x61,0x80, 0x73,0x80, 0x33,0x00,
|
||||
0x33,0x00, 0x00,0x00,
|
||||
0x33,0x00, 0x1E,0x00, 0x3F,0x00, 0x7F,0x80, 0x7F,0x80, 0x61,0x80, 0x73,0x80, 0x33,0x00, 0x33,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_dagger[] PROGMEM = {
|
||||
0x00,0x80, 0x01,0x80, 0x03,0x00, 0x06,0x00,
|
||||
0x0C,0x00, 0x18,0x00, 0x30,0x00, 0x70,0x00,
|
||||
0x70,0x00, 0x30,0x00,
|
||||
0x00,0x80, 0x01,0x80, 0x03,0x00, 0x06,0x00, 0x0C,0x00, 0x18,0x00, 0x30,0x00, 0x70,0x00, 0x70,0x00, 0x30,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_grimace[] PROGMEM = {
|
||||
0x3F,0x00, 0x61,0x80, 0x73,0x80, 0x40,0x80,
|
||||
0x40,0x80, 0x7F,0x80, 0x55,0x00, 0x7F,0x80,
|
||||
0x3F,0x00, 0x00,0x00,
|
||||
0x3F,0x00, 0x61,0x80, 0x73,0x80, 0x40,0x80, 0x40,0x80, 0x7F,0x80, 0x55,0x00, 0x7F,0x80, 0x3F,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_telephone[] PROGMEM = {
|
||||
0x00,0x00, 0xFF,0xC0, 0xC0,0xC0, 0xC0,0xC0,
|
||||
0x61,0x80, 0x3F,0x00, 0x1E,0x00, 0x3F,0x00,
|
||||
0x7F,0x80, 0x00,0x00,
|
||||
0x00,0x00, 0xFF,0xC0, 0xC0,0xC0, 0xC0,0xC0, 0x61,0x80, 0x3F,0x00, 0x1E,0x00, 0x3F,0x00, 0x7F,0x80, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_mountain[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0x0C,0x00, 0x1E,0x00, 0x33,0x00, 0x6D,0x80, 0xDE,0xC0, 0xFF,0xC0, 0xFF,0xC0, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_end_arrow[] PROGMEM = {
|
||||
0x00,0x00, 0x77,0x80, 0x47,0x80, 0x65,0x80, 0x47,0x80, 0x47,0x80, 0x76,0x80, 0x0C,0x00, 0x1E,0x00, 0x0C,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_hollow_circle[] PROGMEM = {
|
||||
0x3F,0x00, 0x40,0x80, 0x80,0x40, 0x80,0x40, 0x80,0x40, 0x80,0x40, 0x80,0x40, 0x40,0x80, 0x3F,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_dragon[] PROGMEM = {
|
||||
0x60,0x00, 0xE0,0x00, 0x7C,0x00, 0x3E,0x00, 0x1E,0x00, 0x3F,0x00, 0x7F,0x80, 0x73,0x80, 0x21,0x00, 0x21,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_globe_meridians[] PROGMEM = {
|
||||
0x3F,0x00, 0x4C,0x80, 0x8C,0x40, 0xFF,0xC0, 0x8C,0x40, 0x8C,0x40, 0x4C,0x80, 0x3F,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_eggplant[] PROGMEM = {
|
||||
0x03,0x00, 0x06,0x00, 0x0E,0x00, 0x1E,0x00, 0x3E,0x00, 0x7E,0x00, 0x7C,0x00, 0x78,0x00, 0x70,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_shield[] PROGMEM = {
|
||||
0x00,0x00, 0xFF,0xC0, 0xFF,0xC0, 0xDE,0xC0, 0xDE,0xC0, 0x6D,0x80, 0x7F,0x80, 0x3F,0x00, 0x1E,0x00, 0x0C,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_goggles[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0x73,0x80, 0xDE,0xC0, 0x8C,0x40, 0x8C,0x40, 0xDE,0xC0, 0x73,0x80, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_lizard[] PROGMEM = {
|
||||
0x01,0x80, 0x03,0xC0, 0x06,0x40, 0x0C,0x00, 0x78,0x00, 0xC0,0x00, 0x60,0x00, 0x30,0x00, 0x1C,0x00, 0x0C,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_zany_face[] PROGMEM = {
|
||||
0x3F,0x00, 0x60,0x80, 0x72,0x80, 0x40,0x80, 0x40,0x80, 0x5E,0x80, 0x61,0x80, 0x3F,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_kangaroo[] PROGMEM = {
|
||||
0x1C,0x00, 0x3E,0x00, 0x1C,0x00, 0x1E,0x00, 0x0F,0x00, 0x4F,0x00, 0x6B,0x00, 0x39,0x00, 0x31,0x00, 0x31,0xC0,
|
||||
};
|
||||
static const uint8_t emoji_sm_feather[] PROGMEM = {
|
||||
0x00,0x80, 0x01,0x80, 0x03,0x00, 0x06,0x00, 0x0C,0x00, 0x18,0x00, 0x30,0x00, 0x60,0x00, 0x60,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_bright[] PROGMEM = {
|
||||
0x0C,0x00, 0x2D,0x00, 0x1E,0x00, 0x5E,0x80, 0x7F,0x80, 0x1E,0x00, 0x2D,0x00, 0x0C,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_part_alt[] PROGMEM = {
|
||||
0x63,0x00, 0x77,0x00, 0x3E,0x00, 0x1C,0x00, 0x1C,0x00, 0x3E,0x00, 0x77,0x00, 0x63,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_motorboat[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0x0C,0x00, 0x1E,0x00, 0x3F,0x00, 0xFF,0xC0, 0x7F,0x80, 0x3F,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_playing_card[] PROGMEM = {
|
||||
0x7F,0x00, 0x41,0x00, 0x5D,0x00, 0x65,0x00, 0x65,0x00, 0x59,0x00, 0x43,0x00, 0x7E,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_satellite[] PROGMEM = {
|
||||
0x70,0x00, 0xD8,0x00, 0x88,0x00, 0xFE,0x00, 0x07,0x00, 0x03,0x80, 0x01,0x80, 0x00,0x80, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_customs[] PROGMEM = {
|
||||
0x3F,0x00, 0x40,0x80, 0x4C,0x80, 0x52,0x80, 0x61,0x80, 0x5E,0x80, 0x44,0x80, 0x3F,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_cowboy[] PROGMEM = {
|
||||
0x1E,0x00, 0x1E,0x00, 0xFF,0xC0, 0x00,0x00, 0x3F,0x00, 0x73,0x80, 0x40,0x80, 0x4C,0x80, 0x3F,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_wheel[] PROGMEM = {
|
||||
0x3F,0x00, 0x4C,0x80, 0x9E,0x40, 0xBF,0x40, 0xFF,0xC0, 0xBF,0x40, 0x9E,0x40, 0x4C,0x80, 0x3F,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_koala[] PROGMEM = {
|
||||
0x61,0x80, 0xE1,0xC0, 0xED,0xC0, 0x6D,0x80, 0x3F,0x00, 0x2D,0x00, 0x33,0x00, 0x1E,0x00, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_control_knobs[] PROGMEM = {
|
||||
0x00,0x00, 0x26,0xC0, 0x26,0xC0, 0x26,0xC0, 0x26,0xC0, 0x76,0xC0, 0x7E,0xC0, 0x2F,0xC0, 0x26,0xC0, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_peach[] PROGMEM = {
|
||||
0x0C,0x00, 0x18,0x00, 0x3C,0x00, 0x7E,0x00, 0x77,0x00, 0x77,0x00, 0x7F,0x00, 0x3F,0x00, 0x1E,0x00, 0x00,0x00,
|
||||
};
|
||||
static const uint8_t emoji_sm_racing_car[] PROGMEM = {
|
||||
0x00,0x00, 0x00,0x00, 0x0E,0x00, 0x1F,0x00, 0x7F,0x80, 0xFF,0xC0, 0xFF,0xC0, 0x5E,0x80, 0x00,0x00, 0x00,0x00,
|
||||
};
|
||||
|
||||
static const uint8_t* const EMOJI_SPRITES_SM[] PROGMEM = {
|
||||
emoji_sm_joy, emoji_sm_thumbsup, emoji_sm_frown,
|
||||
emoji_sm_wireless, emoji_sm_infinity, emoji_sm_trex, emoji_sm_skull, emoji_sm_cross,
|
||||
emoji_sm_lightning, emoji_sm_tophat, emoji_sm_motorcycle, emoji_sm_seedling, emoji_sm_flag_au,
|
||||
emoji_sm_umbrella, emoji_sm_nazar, emoji_sm_globe, emoji_sm_radioactive, emoji_sm_cow,
|
||||
emoji_sm_alien, emoji_sm_invader, emoji_sm_dagger, emoji_sm_grimace, emoji_sm_telephone,
|
||||
emoji_sm_mountain, emoji_sm_end_arrow, emoji_sm_hollow_circle, emoji_sm_dragon, emoji_sm_globe_meridians,
|
||||
emoji_sm_eggplant, emoji_sm_shield, emoji_sm_goggles, emoji_sm_lizard, emoji_sm_zany_face,
|
||||
emoji_sm_kangaroo, emoji_sm_feather, emoji_sm_bright, emoji_sm_part_alt, emoji_sm_motorboat,
|
||||
emoji_sm_playing_card, emoji_sm_satellite, emoji_sm_customs, emoji_sm_cowboy, emoji_sm_wheel,
|
||||
emoji_sm_koala, emoji_sm_control_knobs, emoji_sm_peach, emoji_sm_racing_car,
|
||||
};
|
||||
|
||||
// ---- Codepoint lookup for UTF-8 detection ----
|
||||
struct EmojiCodepoint {
|
||||
uint32_t cp;
|
||||
uint32_t cp2;
|
||||
uint8_t escape;
|
||||
};
|
||||
// ---- Codepoint lookup for UTF-8 conversion ----
|
||||
struct EmojiCodepoint { uint32_t cp; uint32_t cp2; uint8_t escape; };
|
||||
|
||||
static const EmojiCodepoint EMOJI_CODEPOINTS[20] = {
|
||||
{ 0x1F6DC, 0x0000, 0x01 }, { 0x267E, 0x0000, 0x02 }, { 0x1F996, 0x0000, 0x03 },
|
||||
{ 0x2620, 0x0000, 0x04 }, { 0x271D, 0x0000, 0x05 }, { 0x26A1, 0x0000, 0x06 },
|
||||
{ 0x1F3A9, 0x0000, 0x07 }, { 0x1F3CD, 0x0000, 0x08 }, { 0x1F331, 0x0000, 0x09 },
|
||||
{ 0x1F1E6, 0x1F1FA, 0x0A }, { 0x2602, 0x0000, 0x0B }, { 0x1F9FF, 0x0000, 0x0C },
|
||||
{ 0x1F30F, 0x0000, 0x0D }, { 0x2622, 0x0000, 0x0E }, { 0x1F404, 0x0000, 0x0F },
|
||||
{ 0x1F47D, 0x0000, 0x10 }, { 0x1F47E, 0x0000, 0x11 }, { 0x1F5E1, 0x0000, 0x12 },
|
||||
{ 0x1F62C, 0x0000, 0x13 }, { 0x260E, 0x0000, 0x14 },
|
||||
static const EmojiCodepoint EMOJI_CODEPOINTS[EMOJI_COUNT] = {
|
||||
{ 0x1F602, 0x0000, 0x80 }, // joy
|
||||
{ 0x1F44D, 0x0000, 0x81 }, // thumbsup
|
||||
{ 0x2639, 0x0000, 0x82 }, // frown
|
||||
{ 0x1F6DC, 0x0000, 0x83 }, // wireless
|
||||
{ 0x267E, 0x0000, 0x84 }, // infinity
|
||||
{ 0x1F996, 0x0000, 0x85 }, // trex
|
||||
{ 0x2620, 0x0000, 0x86 }, // skull
|
||||
{ 0x271D, 0x0000, 0x87 }, // cross
|
||||
{ 0x26A1, 0x0000, 0x88 }, // lightning
|
||||
{ 0x1F3A9, 0x0000, 0x89 }, // tophat
|
||||
{ 0x1F3CD, 0x0000, 0x8A }, // motorcycle
|
||||
{ 0x1F331, 0x0000, 0x8B }, // seedling
|
||||
{ 0x1F1E6, 0x1F1FA, 0x8C }, // flag_au
|
||||
{ 0x2602, 0x0000, 0x8D }, // umbrella
|
||||
{ 0x1F9FF, 0x0000, 0x8E }, // nazar
|
||||
{ 0x1F30F, 0x0000, 0x8F }, // globe
|
||||
{ 0x2622, 0x0000, 0x90 }, // radioactive
|
||||
{ 0x1F404, 0x0000, 0x91 }, // cow
|
||||
{ 0x1F47D, 0x0000, 0x92 }, // alien
|
||||
{ 0x1F47E, 0x0000, 0x93 }, // invader
|
||||
{ 0x1F5E1, 0x0000, 0x94 }, // dagger
|
||||
{ 0x1F62C, 0x0000, 0x95 }, // grimace
|
||||
{ 0x260E, 0x0000, 0x96 }, // telephone
|
||||
{ 0x26F0, 0x0000, 0x97 }, // mountain
|
||||
{ 0x1F51A, 0x0000, 0x98 }, // end_arrow
|
||||
{ 0x2B55, 0x0000, 0x99 }, // hollow_circle
|
||||
{ 0x1F409, 0x0000, 0x9A }, // dragon
|
||||
{ 0x1F310, 0x0000, 0x9B }, // globe_meridians
|
||||
{ 0x1F346, 0x0000, 0x9C }, // eggplant
|
||||
{ 0x1F6E1, 0x0000, 0x9D }, // shield
|
||||
{ 0x1F97D, 0x0000, 0x9E }, // goggles
|
||||
{ 0x1F98E, 0x0000, 0x9F }, // lizard
|
||||
{ 0x1F92A, 0x0000, 0xA0 }, // zany_face
|
||||
{ 0x1F998, 0x0000, 0xA1 }, // kangaroo
|
||||
{ 0x1FAB6, 0x0000, 0xA2 }, // feather
|
||||
{ 0x1F506, 0x0000, 0xA3 }, // bright
|
||||
{ 0x303D, 0x0000, 0xA4 }, // part_alt
|
||||
{ 0x1F6E5, 0x0000, 0xA5 }, // motorboat
|
||||
{ 0x1F0CE, 0x0000, 0xA6 }, // playing_card
|
||||
{ 0x1F4E1, 0x0000, 0xA7 }, // satellite
|
||||
{ 0x1F6C3, 0x0000, 0xA8 }, // customs
|
||||
{ 0x1F920, 0x0000, 0xA9 }, // cowboy
|
||||
{ 0x1F6DE, 0x0000, 0xAA }, // wheel
|
||||
{ 0x1F428, 0x0000, 0xAB }, // koala
|
||||
{ 0x1F39B, 0x0000, 0xAC }, // control_knobs
|
||||
{ 0x1F351, 0x0000, 0xAD }, // peach
|
||||
{ 0x1F3CE, 0x0000, 0xAE }, // racing_car
|
||||
};
|
||||
|
||||
// ---- Helper functions ----
|
||||
@@ -283,18 +457,20 @@ static uint32_t emojiDecodeUtf8(const uint8_t* s, int remaining, int* bytes_cons
|
||||
return 0xFFFD;
|
||||
}
|
||||
|
||||
// Convert UTF-8 text to internal format (emoji codepoints -> escape bytes)
|
||||
// Now handles ALL multi-byte UTF-8 (>= 0x80) to prevent raw high bytes in buffer
|
||||
static void emojiSanitize(const char* src, char* dst, int dstLen) {
|
||||
const uint8_t* s = (const uint8_t*)src;
|
||||
int si = 0, di = 0;
|
||||
int srcLen = strlen(src);
|
||||
while (si < srcLen && di < dstLen - 1) {
|
||||
uint8_t b = s[si];
|
||||
if (b >= 0xE0) {
|
||||
if (b >= 0x80) {
|
||||
int consumed;
|
||||
uint32_t cp = emojiDecodeUtf8(s + si, srcLen - si, &consumed);
|
||||
if (cp == 0xFE0F) { si += consumed; continue; }
|
||||
bool found = false;
|
||||
for (int e = 0; e < 20; e++) {
|
||||
for (int e = 0; e < EMOJI_COUNT; e++) {
|
||||
if (EMOJI_CODEPOINTS[e].cp == cp) {
|
||||
if (EMOJI_CODEPOINTS[e].cp2 != 0) {
|
||||
int consumed2;
|
||||
@@ -310,11 +486,12 @@ static void emojiSanitize(const char* src, char* dst, int dstLen) {
|
||||
}
|
||||
dst[di++] = EMOJI_CODEPOINTS[e].escape;
|
||||
si += consumed;
|
||||
// Skip trailing variation selector U+FE0F
|
||||
if (si + 2 < srcLen && s[si] == 0xEF && s[si+1] == 0xB8 && s[si+2] == 0x8F) si += 3;
|
||||
found = true; break;
|
||||
}
|
||||
}
|
||||
if (!found) si += consumed;
|
||||
if (!found) si += consumed; // Skip unknown multi-byte chars
|
||||
} else {
|
||||
dst[di++] = (char)b;
|
||||
si++;
|
||||
@@ -327,78 +504,46 @@ static inline bool isEmojiEscape(uint8_t b) {
|
||||
return b >= EMOJI_ESCAPE_START && b <= EMOJI_ESCAPE_END;
|
||||
}
|
||||
|
||||
// Encode a Unicode codepoint as UTF-8 into dst, return bytes written
|
||||
static int emojiEncodeUtf8(uint32_t cp, uint8_t* dst) {
|
||||
if (cp < 0x80) {
|
||||
dst[0] = (uint8_t)cp;
|
||||
return 1;
|
||||
} else if (cp < 0x800) {
|
||||
dst[0] = 0xC0 | (cp >> 6);
|
||||
dst[1] = 0x80 | (cp & 0x3F);
|
||||
return 2;
|
||||
} else if (cp < 0x10000) {
|
||||
dst[0] = 0xE0 | (cp >> 12);
|
||||
dst[1] = 0x80 | ((cp >> 6) & 0x3F);
|
||||
dst[2] = 0x80 | (cp & 0x3F);
|
||||
return 3;
|
||||
} else {
|
||||
dst[0] = 0xF0 | (cp >> 18);
|
||||
dst[1] = 0x80 | ((cp >> 12) & 0x3F);
|
||||
dst[2] = 0x80 | ((cp >> 6) & 0x3F);
|
||||
dst[3] = 0x80 | (cp & 0x3F);
|
||||
return 4;
|
||||
}
|
||||
if (cp < 0x80) { dst[0] = (uint8_t)cp; return 1; }
|
||||
if (cp < 0x800) { dst[0] = 0xC0|(cp>>6); dst[1] = 0x80|(cp&0x3F); return 2; }
|
||||
if (cp < 0x10000) { dst[0] = 0xE0|(cp>>12); dst[1] = 0x80|((cp>>6)&0x3F); dst[2] = 0x80|(cp&0x3F); return 3; }
|
||||
dst[0] = 0xF0|(cp>>18); dst[1] = 0x80|((cp>>12)&0x3F); dst[2] = 0x80|((cp>>6)&0x3F); dst[3] = 0x80|(cp&0x3F); return 4;
|
||||
}
|
||||
|
||||
// Reverse of emojiSanitize: convert escape bytes back to UTF-8 emoji sequences
|
||||
// Used before sending over mesh/BLE so other devices and apps see real emoji
|
||||
// dst must be large enough (worst case: srcLen * 8 for all flag emoji)
|
||||
static void emojiUnescape(const char* src, char* dst, int dstLen) {
|
||||
int si = 0, di = 0;
|
||||
int srcLen = strlen(src);
|
||||
while (si < srcLen && di < dstLen - 1) {
|
||||
uint8_t b = (uint8_t)src[si];
|
||||
if (b == EMOJI_PAD_BYTE) {
|
||||
si++; // Skip padding bytes
|
||||
continue;
|
||||
}
|
||||
if (b == EMOJI_PAD_BYTE) { si++; continue; }
|
||||
if (isEmojiEscape(b)) {
|
||||
int idx = b - EMOJI_ESCAPE_START;
|
||||
if (idx < 20) {
|
||||
if (idx < EMOJI_COUNT) {
|
||||
uint8_t utf8[8];
|
||||
int len = emojiEncodeUtf8(EMOJI_CODEPOINTS[idx].cp, utf8);
|
||||
// Encode second codepoint if present (flag sequences)
|
||||
if (EMOJI_CODEPOINTS[idx].cp2 != 0) {
|
||||
if (EMOJI_CODEPOINTS[idx].cp2 != 0)
|
||||
len += emojiEncodeUtf8(EMOJI_CODEPOINTS[idx].cp2, utf8 + len);
|
||||
}
|
||||
if (di + len < dstLen) {
|
||||
memcpy(dst + di, utf8, len);
|
||||
di += len;
|
||||
} else break; // No room
|
||||
if (di + len < dstLen) { memcpy(dst + di, utf8, len); di += len; } else break;
|
||||
}
|
||||
si++;
|
||||
} else {
|
||||
dst[di++] = src[si++];
|
||||
}
|
||||
} else { dst[di++] = src[si++]; }
|
||||
}
|
||||
dst[di] = '\0';
|
||||
}
|
||||
|
||||
// Get large sprite (for compose/picker)
|
||||
static inline const uint8_t* getEmojiSpriteLg(uint8_t escape_byte) {
|
||||
if (!isEmojiEscape(escape_byte)) return nullptr;
|
||||
return (const uint8_t*)pgm_read_ptr(&EMOJI_SPRITES_LG[escape_byte - EMOJI_ESCAPE_START]);
|
||||
}
|
||||
|
||||
// Get small sprite (for channel view)
|
||||
static inline const uint8_t* getEmojiSpriteSm(uint8_t escape_byte) {
|
||||
if (!isEmojiEscape(escape_byte)) return nullptr;
|
||||
return (const uint8_t*)pgm_read_ptr(&EMOJI_SPRITES_SM[escape_byte - EMOJI_ESCAPE_START]);
|
||||
}
|
||||
|
||||
// Get the UTF-8 wire cost in bytes for an escape byte (4 for most emoji, 8 for flags)
|
||||
static inline int emojiUtf8Cost(uint8_t escape_byte) {
|
||||
if (!isEmojiEscape(escape_byte)) return 1; // not an emoji, regular char
|
||||
if (!isEmojiEscape(escape_byte)) return 1;
|
||||
int idx = escape_byte - EMOJI_ESCAPE_START;
|
||||
uint32_t cp = EMOJI_CODEPOINTS[idx].cp;
|
||||
int cost = (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4;
|
||||
|
||||
@@ -1,78 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
// Emoji Picker for compose mode
|
||||
// Shows a grid of available emoji sprites, navigable with WASD + Enter to select
|
||||
// Requires EmojiSprites.h to be included before this file
|
||||
// Emoji Picker with scrolling grid and scroll bar
|
||||
// 5 columns, 4 visible rows, scrollable through all 47 emoji
|
||||
// WASD navigation, Enter to select, $/Q/Backspace to cancel
|
||||
|
||||
#include <helpers/ui/DisplayDriver.h>
|
||||
#include "EmojiSprites.h"
|
||||
|
||||
// Grid layout: 5 columns x 4 rows = 20 emojis
|
||||
#define EMOJI_PICKER_COLS 5
|
||||
#define EMOJI_PICKER_ROWS 4
|
||||
#define EMOJI_PICKER_VISIBLE_ROWS 4
|
||||
#define EMOJI_PICKER_TOTAL_ROWS ((EMOJI_COUNT + EMOJI_PICKER_COLS - 1) / EMOJI_PICKER_COLS)
|
||||
|
||||
// Short labels shown alongside sprites for identification
|
||||
static const char* EMOJI_LABELS[EMOJI_COUNT] = {
|
||||
"WiFi", // 0 🛜
|
||||
"Inf", // 1 ♾️
|
||||
"Rex", // 2 🦖
|
||||
"Skul", // 3 ☠️
|
||||
"Cros", // 4 ✝️
|
||||
"Bolt", // 5 ⚡
|
||||
"Hat", // 6 🎩
|
||||
"Moto", // 7 🏍️
|
||||
"Leaf", // 8 🌱
|
||||
"AU", // 9 🇦🇺
|
||||
"Umbr", // 10 ☂️
|
||||
"Eye", // 11 🧿
|
||||
"Glob", // 12 🌏
|
||||
"Rad", // 13 ☢️
|
||||
"Cow", // 14 🐄
|
||||
"ET", // 15 👽
|
||||
"Inv", // 16 👾
|
||||
"Dagr", // 17 🗡️
|
||||
"Grim", // 18 😬
|
||||
"Fone", // 19 ☎️
|
||||
"Lol", // 0 joy
|
||||
"Like", // 1 thumbsup
|
||||
"Sad", // 2 frown
|
||||
"WiFi", // 3 wireless
|
||||
"Inf", // 4 infinity
|
||||
"Rex", // 5 trex
|
||||
"Skul", // 6 skull
|
||||
"Cros", // 7 cross
|
||||
"Bolt", // 8 lightning
|
||||
"Hat", // 9 tophat
|
||||
"Moto", // 10 motorcycle
|
||||
"Leaf", // 11 seedling
|
||||
"AU", // 12 flag_au
|
||||
"Umbr", // 13 umbrella
|
||||
"Eye", // 14 nazar
|
||||
"Glob", // 15 globe
|
||||
"Rad", // 16 radioactive
|
||||
"Cow", // 17 cow
|
||||
"ET", // 18 alien
|
||||
"Inv", // 19 invader
|
||||
"Dagr", // 20 dagger
|
||||
"Grim", // 21 grimace
|
||||
"Fone", // 22 telephone
|
||||
"Mtn", // 23 mountain
|
||||
"End", // 24 end_arrow
|
||||
"Ring", // 25 hollow_circle
|
||||
"Drag", // 26 dragon
|
||||
"Web", // 27 globe_meridians
|
||||
"Eggp", // 28 eggplant
|
||||
"Shld", // 29 shield
|
||||
"Gogl", // 30 goggles
|
||||
"Lzrd", // 31 lizard
|
||||
"Zany", // 32 zany_face
|
||||
"Roo", // 33 kangaroo
|
||||
"Fthr", // 34 feather
|
||||
"Sun", // 35 bright
|
||||
"Wave", // 36 part_alt
|
||||
"Boat", // 37 motorboat
|
||||
"Card", // 38 playing_card
|
||||
"Dish", // 39 satellite
|
||||
"Pass", // 40 customs
|
||||
"Cowb", // 41 cowboy
|
||||
"Whl", // 42 wheel
|
||||
"Koal", // 43 koala
|
||||
"Knob", // 44 control_knobs
|
||||
"Pch", // 45 peach
|
||||
"Race", // 46 racing_car
|
||||
};
|
||||
|
||||
struct EmojiPicker {
|
||||
int cursor; // 0 to EMOJI_COUNT-1
|
||||
int cursor;
|
||||
int scrollRow;
|
||||
|
||||
EmojiPicker() : cursor(0) {}
|
||||
EmojiPicker() : cursor(0), scrollRow(0) {}
|
||||
|
||||
void reset() { cursor = 0; }
|
||||
void reset() { cursor = 0; scrollRow = 0; }
|
||||
|
||||
// Returns the emoji escape byte for the selected emoji, or 0 if cancelled
|
||||
// Navigate: W=up, S=down, A=left, D=right, Enter=select, Backspace/Q/$=cancel
|
||||
void ensureVisible() {
|
||||
int cursorRow = cursor / EMOJI_PICKER_COLS;
|
||||
if (cursorRow < scrollRow) scrollRow = cursorRow;
|
||||
else if (cursorRow >= scrollRow + EMOJI_PICKER_VISIBLE_ROWS)
|
||||
scrollRow = cursorRow - EMOJI_PICKER_VISIBLE_ROWS + 1;
|
||||
int maxScroll = EMOJI_PICKER_TOTAL_ROWS - EMOJI_PICKER_VISIBLE_ROWS;
|
||||
if (maxScroll < 0) maxScroll = 0;
|
||||
if (scrollRow > maxScroll) scrollRow = maxScroll;
|
||||
if (scrollRow < 0) scrollRow = 0;
|
||||
}
|
||||
|
||||
// Returns emoji escape byte, 0xFF for cancel, 0 for no action
|
||||
uint8_t handleInput(char key) {
|
||||
int col = cursor % EMOJI_PICKER_COLS;
|
||||
int row = cursor / EMOJI_PICKER_COLS;
|
||||
int col = cursor % EMOJI_PICKER_COLS;
|
||||
|
||||
switch (key) {
|
||||
case 'w': case 'W': case 0xF2: // Up
|
||||
case 'w': case 'W': case 0xF2:
|
||||
if (row > 0) cursor -= EMOJI_PICKER_COLS;
|
||||
return 0;
|
||||
case 's': case 'S': case 0xF1: // Down
|
||||
if (row < EMOJI_PICKER_ROWS - 1) cursor += EMOJI_PICKER_COLS;
|
||||
return 0;
|
||||
case 'a': case 'A': // Left
|
||||
if (col > 0) cursor--;
|
||||
else if (row > 0) cursor -= 1; // Wrap to end of previous row
|
||||
return 0;
|
||||
case 'd': case 'D': // Right
|
||||
break;
|
||||
case 's': case 'S': case 0xF1:
|
||||
if (cursor + EMOJI_PICKER_COLS < EMOJI_COUNT)
|
||||
cursor += EMOJI_PICKER_COLS;
|
||||
else if (row < EMOJI_PICKER_TOTAL_ROWS - 1)
|
||||
cursor = EMOJI_COUNT - 1;
|
||||
break;
|
||||
case 'a': case 'A':
|
||||
if (cursor > 0) cursor--;
|
||||
break;
|
||||
case 'd': case 'D':
|
||||
if (cursor + 1 < EMOJI_COUNT) cursor++;
|
||||
return 0;
|
||||
case '\r': // Enter - select
|
||||
break;
|
||||
case '\r':
|
||||
ensureVisible();
|
||||
return (uint8_t)(EMOJI_ESCAPE_START + cursor);
|
||||
case '\b': case 'q': case 'Q': case '$': // Cancel
|
||||
return 0xFF; // Sentinel for "cancelled"
|
||||
case '\b': case 'q': case 'Q': case '$':
|
||||
return 0xFF;
|
||||
default:
|
||||
return 0; // No action
|
||||
return 0;
|
||||
}
|
||||
ensureVisible();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void draw(DisplayDriver& display) {
|
||||
// Header
|
||||
display.setTextSize(1);
|
||||
display.setCursor(0, 0);
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
@@ -81,55 +122,77 @@ struct EmojiPicker {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawRect(0, 11, display.width(), 1);
|
||||
|
||||
// Grid area
|
||||
display.setTextSize(0); // Tiny font for labels
|
||||
display.setTextSize(0);
|
||||
|
||||
int startY = 14;
|
||||
int cellW = display.width() / EMOJI_PICKER_COLS;
|
||||
int cellH = (display.height() - startY - 14) / EMOJI_PICKER_ROWS; // Leave room for footer
|
||||
int scrollBarW = 4;
|
||||
int gridW = display.width() - scrollBarW - 1;
|
||||
int cellW = gridW / EMOJI_PICKER_COLS;
|
||||
int footerHeight = 14;
|
||||
int gridH = display.height() - startY - footerHeight;
|
||||
int cellH = gridH / EMOJI_PICKER_VISIBLE_ROWS;
|
||||
|
||||
for (int i = 0; i < EMOJI_COUNT; i++) {
|
||||
int col = i % EMOJI_PICKER_COLS;
|
||||
int row = i / EMOJI_PICKER_COLS;
|
||||
int cx = col * cellW;
|
||||
int cy = startY + row * cellH;
|
||||
for (int vr = 0; vr < EMOJI_PICKER_VISIBLE_ROWS; vr++) {
|
||||
int absRow = scrollRow + vr;
|
||||
if (absRow >= EMOJI_PICKER_TOTAL_ROWS) break;
|
||||
|
||||
// Draw selection border around highlighted cell
|
||||
if (i == cursor) {
|
||||
for (int col = 0; col < EMOJI_PICKER_COLS; col++) {
|
||||
int idx = absRow * EMOJI_PICKER_COLS + col;
|
||||
if (idx >= EMOJI_COUNT) break;
|
||||
|
||||
int cx = col * cellW;
|
||||
int cy = startY + vr * cellH;
|
||||
|
||||
if (idx == cursor) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawRect(cx, cy, cellW, cellH);
|
||||
display.drawRect(cx + 1, cy + 1, cellW - 2, cellH - 2);
|
||||
}
|
||||
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawRect(cx, cy, cellW, cellH);
|
||||
display.drawRect(cx + 1, cy + 1, cellW - 2, cellH - 2); // Double border for visibility
|
||||
const uint8_t* sprite = (const uint8_t*)pgm_read_ptr(&EMOJI_SPRITES_LG[idx]);
|
||||
if (sprite) {
|
||||
int spriteX = cx + (cellW - EMOJI_LG_W) / 2;
|
||||
int spriteY = cy + 1;
|
||||
display.drawXbm(spriteX, spriteY, sprite, EMOJI_LG_W, EMOJI_LG_H);
|
||||
}
|
||||
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
uint16_t labelW = display.getTextWidth(EMOJI_LABELS[idx]);
|
||||
int labelX = cx + (cellW - (int)labelW) / 2;
|
||||
if (labelX < cx) labelX = cx;
|
||||
display.setCursor(labelX, cy + 14);
|
||||
display.print(EMOJI_LABELS[idx]);
|
||||
}
|
||||
|
||||
// Draw sprite centered in cell
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
const uint8_t* sprite = (const uint8_t*)pgm_read_ptr(&EMOJI_SPRITES_LG[i]);
|
||||
if (sprite) {
|
||||
int spriteX = cx + (cellW - EMOJI_LG_W) / 2;
|
||||
int spriteY = cy + 1;
|
||||
display.drawXbm(spriteX, spriteY, sprite, EMOJI_LG_W, EMOJI_LG_H);
|
||||
}
|
||||
|
||||
// Label below sprite
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
|
||||
// Center label text in cell
|
||||
uint16_t labelW = display.getTextWidth(EMOJI_LABELS[i]);
|
||||
int labelX = cx + (cellW * 7 - labelW) / (7 * 2); // Approximate centering
|
||||
display.setCursor(labelX, cy + 12);
|
||||
display.print(EMOJI_LABELS[i]);
|
||||
}
|
||||
|
||||
// Scroll bar
|
||||
int sbX = display.width() - scrollBarW;
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawRect(sbX, startY, scrollBarW, gridH);
|
||||
|
||||
if (EMOJI_PICKER_TOTAL_ROWS > EMOJI_PICKER_VISIBLE_ROWS) {
|
||||
int thumbH = (EMOJI_PICKER_VISIBLE_ROWS * gridH) / EMOJI_PICKER_TOTAL_ROWS;
|
||||
if (thumbH < 4) thumbH = 4;
|
||||
int maxScroll = EMOJI_PICKER_TOTAL_ROWS - EMOJI_PICKER_VISIBLE_ROWS;
|
||||
int thumbY = startY + (scrollRow * (gridH - thumbH)) / maxScroll;
|
||||
for (int y = thumbY + 1; y < thumbY + thumbH - 1; y++)
|
||||
display.drawRect(sbX + 1, y, scrollBarW - 2, 1);
|
||||
} else {
|
||||
for (int y = startY + 1; y < startY + gridH - 1; y++)
|
||||
display.drawRect(sbX + 1, y, scrollBarW - 2, 1);
|
||||
}
|
||||
|
||||
// Footer
|
||||
display.setTextSize(1);
|
||||
int footerY = display.height() - 12;
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawRect(0, footerY - 2, display.width(), 1);
|
||||
display.setCursor(0, footerY);
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
display.print("WASD:Nav Ent:Pick");
|
||||
|
||||
const char* cancelText = "$:Back";
|
||||
display.setCursor(display.width() - display.getTextWidth(cancelText) - 2, footerY);
|
||||
display.print(cancelText);
|
||||
const char* ct = "$:Back";
|
||||
display.setCursor(display.width() - display.getTextWidth(ct) - 2, footerY);
|
||||
display.print(ct);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user