Public Access
Icons: small images of their own size for pages
Bitmaps that only pages use (backgrounds, images by state) are now icons: own width and height, stored in /icons, not in the page lists or the Home Assistant dropdowns. Page backgrounds and image elements use icons or bitmaps; the renderer gets icons expanded at the top left, so pages look the same. Web UI: Icons tab with an editor, Make icon / Make bitmap, Save as icon on the Draw tab, icons in the backup. A one-time migration in time-boxed steps moves the uploaded bitmaps that only pages use, after the new firmware is marked good. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1704cbf17b
commit
ab001e5d18
37 files changed
+955
-78
No files matched your search
@@ -0,0 +1,103 @@
|
||||
#include "logic/icon_format.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
bool validIconSize(int width, int height)
|
||||
{
|
||||
return width >= 1 && height >= 1 && width <= ICON_MAX_WIDTH && height <= ICON_MAX_HEIGHT;
|
||||
}
|
||||
|
||||
bool iconPixel(const Icon &icon, int x, int y)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= icon.width || y >= icon.height)
|
||||
return false;
|
||||
return icon.data[y * iconRowBytes(icon.width) + x / 8] & (0x80 >> (x & 7));
|
||||
}
|
||||
|
||||
void setIconPixel(Icon &icon, int x, int y, bool on)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= icon.width || y >= icon.height)
|
||||
return;
|
||||
uint8_t &byte = icon.data[y * iconRowBytes(icon.width) + x / 8];
|
||||
if (on)
|
||||
byte |= 0x80 >> (x & 7);
|
||||
else
|
||||
byte &= ~(0x80 >> (x & 7));
|
||||
}
|
||||
|
||||
static int hexValue(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool iconFromHex(int width, int height, const char *hex, Icon &icon)
|
||||
{
|
||||
if (!validIconSize(width, height) || hex == nullptr)
|
||||
return false;
|
||||
int bytes = iconBytes(width, height);
|
||||
if (strlen(hex) != (size_t)bytes * 2)
|
||||
return false;
|
||||
Icon result;
|
||||
result.width = width;
|
||||
result.height = height;
|
||||
for (int i = 0; i < bytes; i++)
|
||||
{
|
||||
int high = hexValue(hex[2 * i]);
|
||||
int low = hexValue(hex[2 * i + 1]);
|
||||
if (high < 0 || low < 0)
|
||||
return false;
|
||||
result.data[i] = (high << 4) | low;
|
||||
}
|
||||
// The unused bits at the end of each row stay off
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = width; x < iconRowBytes(width) * 8; x++)
|
||||
result.data[y * iconRowBytes(width) + x / 8] &= ~(0x80 >> (x & 7));
|
||||
icon = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
void iconToHex(const Icon &icon, char *out)
|
||||
{
|
||||
static const char digits[] = "0123456789abcdef";
|
||||
int bytes = iconBytes(icon.width, icon.height);
|
||||
for (int i = 0; i < bytes; i++)
|
||||
{
|
||||
out[2 * i] = digits[icon.data[i] >> 4];
|
||||
out[2 * i + 1] = digits[icon.data[i] & 0x0F];
|
||||
}
|
||||
out[bytes * 2] = '\0';
|
||||
}
|
||||
|
||||
void iconToBitmap(const Icon &icon, uint8_t *bitmap)
|
||||
{
|
||||
memset(bitmap, 0, BITMAP_BYTES);
|
||||
for (int y = 0; y < icon.height; y++)
|
||||
for (int x = 0; x < icon.width; x++)
|
||||
if (iconPixel(icon, x, y))
|
||||
setBitmapPixel(bitmap, x, y, true);
|
||||
}
|
||||
|
||||
void iconFromBitmap(const uint8_t *bitmap, Icon &icon)
|
||||
{
|
||||
int right = 0, bottom = 0;
|
||||
for (int y = 0; y < BITMAP_HEIGHT; y++)
|
||||
for (int x = 0; x < BITMAP_WIDTH; x++)
|
||||
if (bitmapPixel(bitmap, x, y))
|
||||
{
|
||||
right = x > right ? x : right;
|
||||
bottom = y > bottom ? y : bottom;
|
||||
}
|
||||
icon = Icon();
|
||||
icon.width = right + 1;
|
||||
icon.height = bottom + 1;
|
||||
for (int y = 0; y < icon.height; y++)
|
||||
for (int x = 0; x < icon.width; x++)
|
||||
if (bitmapPixel(bitmap, x, y))
|
||||
setIconPixel(icon, x, y, true);
|
||||
}
|
||||
Reference in new issue
Block a user