Skip to content

Fix distorted animated icons on ESPHome 2026.4.0 (#331)#333

Merged
lubeda merged 1 commit into
lubeda:2026.4.0from
andrewjswan:fix-esphome-2026.4
Apr 17, 2026
Merged

Fix distorted animated icons on ESPHome 2026.4.0 (#331)#333
lubeda merged 1 commit into
lubeda:2026.4.0from
andrewjswan:fix-esphome-2026.4

Conversation

@andrewjswan
Copy link
Copy Markdown

Root cause

ESPHome 2026.4.0 reworked ImageRGB565 in two ways that together break every RGB565-with-transparency icon produced by EHMTXv2:

  1. Default byte order flipped from big-endian to little-endian for the Python encoder, but the C++ Image::get_rgb565_pixel_ decoder still reads big-endian. Every pixel comes out byte-swapped.
  2. Alpha channel moved from per-pixel interleaved to an appended block via a new encoder.end_image() call. For multi-frame animations the new layout is actually broken end-to-end: write_image() calls end_image() per frame, each call appends the entire growing alpha buffer, and Animation::update_data_start_() advances data_start_ by only w*h*2 bytes (the RGB-only frame size). The decoder then reads "alpha" bytes that fall inside the next frame's RGB data — producing the pixel-soup reported in the issue.

Fix

Fixes #331.
Two small changes in components/ehmtxv2/__init__.py:

  • Call encoder.set_big_endian(True) so RGB565 bytes match what the C++ decoder reads. Guarded with hasattr for backwards compatibility; no-op on older ESPHome where BE was already the default.
  • Switch icon transparency from ALPHA_CHANNEL to CHROMA_KEY. Chroma-key stores the transparent-pixel marker (0x0020) inline in the RGB565 stream, so every frame is exactly w*h*2 bytes and Animation frame indexing works correctly on every ESPHome version. The only semantic change is that alpha values between 1 and 127 now render fully transparent instead of partially opaque — 8x8 pixel-art icons (LaMetric style) are binary-alpha in practice, so this matches real usage.

Testing

Verified locally on ESPHome 2026.4.0 against a physical Ulanzi display with both static and animated icons — previously distorted animations now render correctly. Byte-level verification of the encoder output confirms each frame is w*h*2 bytes and the chroma-key sentinel 0x0020 is placed at transparent pixels in big-endian form.

Compatibility

  • ESPHome 2026.4.0+: fixes distorted icons.
  • ESPHome 2025.10.x – 2026.3.x: unchanged output. set_big_endian(True) matches the old default; CHROMA_KEY worked identically here.
  • ESPHome <2025.10: set_big_endian didn't exist yet; hasattr guard makes this a no-op and byte order defaulted to BE anyway.

Notes

ESPHome 2026.4.0 reworked ImageRGB565 in two ways that together broke every RGB565-with-transparency icon produced by EHMTXv2:

1. The Python encoder's default byte order flipped from big-endian to little-endian, but the C++ Image::get_rgb565_pixel_ decoder still reads big-endian — every pixel comes out byte-swapped.

2. The alpha channel moved from per-pixel interleaved to an appended block via a new encoder.end_image() call. For multi-frame animations the new layout is broken end-to-end: write_image() calls end_image() per frame, each call appends the full growing alpha buffer, and Animation::update_data_start_() advances data_start_ by only w*h*2 bytes (the RGB-only frame size). The decoder then reads "alpha" bytes that fall inside the next frame's RGB data — the pixel-soup in lubeda#331.

Two minimal changes:

- Call encoder.set_big_endian(True) so bytes match the C++ decoder. Guarded with hasattr for pre-2025.10 ESPHome; no-op on older releases where BE was already the default.

- Switch icon transparency from ALPHA_CHANNEL to CHROMA_KEY. Chroma-key stores the transparent-pixel marker (0x0020) inline in the RGB565 stream, so every frame is exactly w*h*2 bytes and Animation frame indexing works correctly on every ESPHome version. The only semantic change is that alpha values between 1 and 127 now render fully transparent instead of partially opaque — 8x8 LaMetric-style pixel-art icons are binary-alpha in practice, so this matches real usage.

Original PR: lubeda#332


Co-authored-by: Stefan Zier <stefan@zier.com>
@andrewjswan andrewjswan changed the title Fix distorted animated icons on ESPHome 2026.4.0 (lubeda#331) Fix distorted animated icons on ESPHome 2026.4.0 (#331) Apr 17, 2026
@lubeda lubeda changed the base branch from 2025.12.0 to 2026.4.0 April 17, 2026 12:47
@lubeda lubeda merged commit 3067b20 into lubeda:2026.4.0 Apr 17, 2026
12 checks passed
@andrewjswan andrewjswan deleted the fix-esphome-2026.4 branch April 17, 2026 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants