From 385851f664a73fb5d4a4a0e5f1a797b9d3e67887 Mon Sep 17 00:00:00 2001 From: ainyan03 <205502311+ainyan03@users.noreply.github.com> Date: Fri, 23 Oct 2026 03:05:29 +0000 Subject: [PATCH] EPD / OLED / LED panels: init without clear keeps the image on the panel With M5Unified cfg.clear_display = false (LGFX init_without_reset(false)), the image on the panel was wiped during initialization (#370): - GDEW0154D67 / SSD1677: LGFX_Device::init_impl() re-applies the invert setting after Panel::init(), and setInvert() marked the whole frame dirty while the init sequence was pending, so the endWrite() of init_impl() refreshed the panel with the (white) host buffer. Re-applying the current value no longer marks the frame dirty; a changed value is still shown. - GDEW0154D67: the frame sent by the init sequence is no longer inverted. With the inverted frame, the flip draw that follows ended inverted whenever that was the first display() after a reset (not visible before, because init() itself refreshed the panel). - TM1680: init() no longer pushes the cleared buffer, and setInvert() pushes only when the value changes. The init commands do not touch the LED RAM, so the image stays on the LEDs until the first draw. - Panel_1bitOLED / Panel_M5UnitGlass: init() clears the host buffer (and the tile dirty flags of the Unit GLASS) without pushing it. They relied on the clear() of a normal init; with an init without clear, the first partial drawing would have sent whatever the allocator left there. The host buffer cannot be read back from these panels, so the first drawing after an init without clear still replaces the whole panel with the new buffer. What changes is that the initialization itself no longer wipes it. --- src/lgfx/v1/panel/Panel_GDEW0154D67.inl | 21 +++++++++++++++------ src/lgfx/v1/panel/Panel_M5UnitGLASS.inl | 7 +++++++ src/lgfx/v1/panel/Panel_SSD1306.inl | 6 ++++++ src/lgfx/v1/panel/Panel_SSD1677.inl | 3 +++ src/lgfx/v1/panel/Panel_TM1680.inl | 11 +++++++---- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/lgfx/v1/panel/Panel_GDEW0154D67.inl b/src/lgfx/v1/panel/Panel_GDEW0154D67.inl index 39a2d443..e8437882 100644 --- a/src/lgfx/v1/panel/Panel_GDEW0154D67.inl +++ b/src/lgfx/v1/panel/Panel_GDEW0154D67.inl @@ -185,9 +185,11 @@ epd_mode が epd_qualityか否かの変化をした場合も同様にCMD_DISPLAY if (_initialize_seq) { _initialize_seq = false; // リセット直後は起動シーケンス設定およびフレームバッファの転送を行う。ここではリフレッシュは行わない。 + // Send the normal frame here: with the inverted one, the flip draw below ended inverted + // (white and black swapped) whenever this was the first display() after a reset. _bus->writeCommand(cmd_display_update_control_2, 8); _bus->writeData(0xF8, 8); - _exec_transfer(cmd_write_ram_bw, _range_mod, true); + _exec_transfer(cmd_write_ram_bw, _range_mod); _bus->writeCommand(cmd_master_activation, 8); _send_msec = millis(); } @@ -243,16 +245,23 @@ epd_mode が epd_qualityか否かの変化をした場合も同様にCMD_DISPLAY void Panel_GDEW0154D67::setInvert(bool invert) { if (_invert == invert && !_initialize_seq) { return; } + const bool changed = (_invert != invert); _invert = invert; startWrite(); _wait_busy(); _bus->writeCommand(cmd_display_update_control_1, 8); _bus->writeData((invert ^ _cfg.invert) ? 0x88 : 0x00, 8); - _need_flip_draw = true; - _range_mod.top = 0; - _range_mod.left = 0; - _range_mod.right = _width - 1; - _range_mod.bottom = _height - 1; + // Re-applying the current value right after a reset (LGFX_Device::init_impl does) must not mark + // the frame dirty: the first display() sends the whole frame anyway, and a refresh here would wipe + // the image kept on the panel (init without clear). A changed value is still shown at once. + if (changed || !_initialize_seq) + { + _need_flip_draw = true; + _range_mod.top = 0; + _range_mod.left = 0; + _range_mod.right = _width - 1; + _range_mod.bottom = _height - 1; + } endWrite(); } diff --git a/src/lgfx/v1/panel/Panel_M5UnitGLASS.inl b/src/lgfx/v1/panel/Panel_M5UnitGLASS.inl index d49f6cb4..20947f73 100644 --- a/src/lgfx/v1/panel/Panel_M5UnitGLASS.inl +++ b/src/lgfx/v1/panel/Panel_M5UnitGLASS.inl @@ -24,6 +24,8 @@ Contributors: #include "../misc/pixelcopy.hpp" #include "../misc/colortype.hpp" +#include + namespace lgfx { inline namespace v1 @@ -36,6 +38,11 @@ namespace lgfx { return false; } + // Panel_HasBuffer::init leaves the buffer uninitialized, and the per-tile dirty flags are only + // ever OR-ed / cleared in display(); both must start known for the first display() after an init + // without clear to send just what was drawn. + memset(_buf, 0, _get_buffer_length()); + memset(_modified_flags, 0, sizeof(_modified_flags)); bool connected = false; uint8_t res = 0xFFu; diff --git a/src/lgfx/v1/panel/Panel_SSD1306.inl b/src/lgfx/v1/panel/Panel_SSD1306.inl index 038f88d9..e329dcde 100644 --- a/src/lgfx/v1/panel/Panel_SSD1306.inl +++ b/src/lgfx/v1/panel/Panel_SSD1306.inl @@ -24,6 +24,8 @@ Contributors: #include "../misc/pixelcopy.hpp" #include "../misc/dither.hpp" +#include + #ifdef min #undef min #endif @@ -70,6 +72,10 @@ namespace lgfx { return false; } + // Panel_HasBuffer::init does not clear the allocated buffer. Nothing is pushed here, so the image + // on the panel survives an init without clear; the first display() then sends a known buffer + // (black) around the drawn area instead of whatever the allocator left in it. + memset(_buf, 0, _get_buffer_length()); startWrite(true); _bus->beginRead(); diff --git a/src/lgfx/v1/panel/Panel_SSD1677.inl b/src/lgfx/v1/panel/Panel_SSD1677.inl index 42e9c3d3..50c08f53 100644 --- a/src/lgfx/v1/panel/Panel_SSD1677.inl +++ b/src/lgfx/v1/panel/Panel_SSD1677.inl @@ -439,6 +439,9 @@ namespace lgfx void Panel_SSD1677::setInvert(bool invert) { + // Right after a reset the first display() sends the whole frame anyway; marking it dirty here + // would make init() refresh the panel and wipe the image kept on it (init without clear). + if (_invert == invert && _initialize_seq) { return; } _invert = invert; _range_mod.top = 0; _range_mod.left = 0; diff --git a/src/lgfx/v1/panel/Panel_TM1680.inl b/src/lgfx/v1/panel/Panel_TM1680.inl index 529ff75c..b817c3bb 100644 --- a/src/lgfx/v1/panel/Panel_TM1680.inl +++ b/src/lgfx/v1/panel/Panel_TM1680.inl @@ -38,10 +38,11 @@ namespace lgfx { return false; } - // Panel_HasBuffer::init does not clear the allocated buffer; - // clear it here so setInvert below does not push garbage to the LEDs. + // Panel_HasBuffer::init does not clear the allocated buffer. memset(_buf, 0, _get_buffer_length()); + // The init commands do not touch the LED RAM, and nothing is pushed here, so the + // image on the LEDs survives init() (init without clear keeps it until the first draw). startWrite(true); for (size_t i = 0; auto cmds = getInitCommands(i); i++) { @@ -49,7 +50,6 @@ namespace lgfx while (cmds[idx] != 0xFF || cmds[idx + 1] != 0xFF) ++idx; if (idx) { _bus->writeBytes(cmds, idx, false, true); } } - setInvert(_invert); setRotation(_rotation); endWrite(); @@ -58,8 +58,11 @@ namespace lgfx void Panel_TM1680::setInvert(bool invert) { - _invert = invert; // The TM1680 has no display-invert command; invert while packing in display(). + // Only a change is pushed: LGFX_Device::init_impl() re-applies the current value, + // and pushing the cleared buffer there would wipe the LEDs on init without clear. + if (_invert == invert) { return; } + _invert = invert; if (_buf) { startWrite();