fix: eliminate diagonal dither pattern from display driver
Root cause: Panel_ED2208's epd_quality mode applies _dither_row_rgb_pair (diagonal bias pattern with dither=140) during _exec_transfer(). Since our ImagePipeline already does proper Floyd-Steinberg dithering, the driver's additional dithering was creating visible diagonal artifacts. Fix: setEpdMode(epd_fastest) selects _dither_row_none (clean nearest-color lookup, no spatial bias). On Panel_ED2208 this only affects the dither function — NOT refresh quality or waveform. Also: default dither_noise setting to OFF since error scatter was targeting the wrong layer (it made gradient areas fuzzier without fixing the real problem in the display driver). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -22,7 +22,14 @@ void DisplayManager::begin(PowerManager& power) {
|
||||
// Set rotation for landscape (device physically rotated)
|
||||
_display->setRotation(3);
|
||||
|
||||
Serial.printf("[display] Initialized: %dx%d, rotation=%d\n",
|
||||
// Disable the panel driver's internal dithering (epd_quality applies a diagonal
|
||||
// bias pattern via _dither_row_rgb_pair). Since our ImagePipeline already does
|
||||
// proper Floyd-Steinberg dithering to the 6-color palette, the driver should
|
||||
// just do a clean nearest-color lookup with no additional spatial bias.
|
||||
// On Panel_ED2208 this only affects the dither function — NOT refresh quality.
|
||||
_display->setEpdMode(epd_mode_t::epd_fastest);
|
||||
|
||||
Serial.printf("[display] Initialized: %dx%d, rotation=%d, epd_mode=fastest (no driver dither)\n",
|
||||
_display->width(), _display->height(), _display->getRotation());
|
||||
}
|
||||
|
||||
@@ -235,44 +242,114 @@ void DisplayManager::showMetadataInRegion(const AssetInfo& info, uint8_t metaFla
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayManager::drawBadge(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
|
||||
_display->fillRect(x, y, w, h, TFT_BLACK);
|
||||
}
|
||||
|
||||
void DisplayManager::showSleepIndicator(MetaPosition metaPos) {
|
||||
_power->enableEPDPower();
|
||||
|
||||
// Pick a corner opposite to metadata position
|
||||
static constexpr uint16_t MARGIN = 6;
|
||||
uint16_t tx, ty;
|
||||
lgfx::textdatum_t datum;
|
||||
static constexpr uint16_t MARGIN = 8;
|
||||
static constexpr uint16_t PAD_X = 6;
|
||||
static constexpr uint16_t PAD_Y = 4;
|
||||
|
||||
_display->setTextSize(1);
|
||||
uint16_t textW = _display->textWidth("SLP");
|
||||
uint16_t textH = 14;
|
||||
uint16_t badgeW = textW + PAD_X * 2;
|
||||
uint16_t badgeH = textH + PAD_Y * 2;
|
||||
|
||||
// Pick a corner opposite to metadata position
|
||||
uint16_t bx, by;
|
||||
switch (metaPos) {
|
||||
case MetaPosition::OverlayTopLeft:
|
||||
case MetaPosition::CaptionTop:
|
||||
tx = DISPLAY_WIDTH - MARGIN;
|
||||
ty = DISPLAY_HEIGHT - MARGIN;
|
||||
datum = bottom_right;
|
||||
bx = DISPLAY_WIDTH - badgeW - MARGIN;
|
||||
by = DISPLAY_HEIGHT - badgeH - MARGIN;
|
||||
break;
|
||||
case MetaPosition::OverlayTopRight:
|
||||
tx = MARGIN;
|
||||
ty = DISPLAY_HEIGHT - MARGIN;
|
||||
datum = bottom_left;
|
||||
bx = MARGIN;
|
||||
by = DISPLAY_HEIGHT - badgeH - MARGIN;
|
||||
break;
|
||||
case MetaPosition::OverlayBottomLeft:
|
||||
tx = DISPLAY_WIDTH - MARGIN;
|
||||
ty = MARGIN;
|
||||
datum = top_right;
|
||||
bx = DISPLAY_WIDTH - badgeW - MARGIN;
|
||||
by = MARGIN;
|
||||
break;
|
||||
case MetaPosition::OverlayBottomRight:
|
||||
case MetaPosition::CaptionBottom:
|
||||
default:
|
||||
tx = MARGIN;
|
||||
ty = MARGIN;
|
||||
datum = top_left;
|
||||
bx = MARGIN;
|
||||
by = MARGIN;
|
||||
break;
|
||||
}
|
||||
|
||||
drawBadge(bx, by, badgeW, badgeH);
|
||||
_display->setTextColor(TFT_WHITE);
|
||||
_display->setTextDatum(middle_center);
|
||||
_display->drawString("SLP", bx + badgeW / 2, by + badgeH / 2);
|
||||
}
|
||||
|
||||
void DisplayManager::showBatteryIndicator(uint8_t percent, MetaPosition metaPos) {
|
||||
static constexpr uint16_t MARGIN = 8;
|
||||
static constexpr uint16_t PAD_X = 6;
|
||||
static constexpr uint16_t PAD_Y = 4;
|
||||
|
||||
// Battery icon dimensions
|
||||
static constexpr uint16_t BATT_W = 20;
|
||||
static constexpr uint16_t BATT_H = 10;
|
||||
static constexpr uint16_t NUB_W = 3;
|
||||
static constexpr uint16_t NUB_H = 5;
|
||||
static constexpr uint16_t GAP = 4;
|
||||
|
||||
// Build percentage text
|
||||
char pctText[5];
|
||||
snprintf(pctText, sizeof(pctText), "%d%%", percent);
|
||||
|
||||
_display->setTextSize(1);
|
||||
_display->setTextColor(TFT_BLACK);
|
||||
_display->setTextDatum(datum);
|
||||
_display->drawString("SLP", tx, ty);
|
||||
uint16_t textW = _display->textWidth(pctText);
|
||||
uint16_t textH = 14;
|
||||
|
||||
// Total badge size: icon + gap + text + padding
|
||||
uint16_t contentW = BATT_W + NUB_W + GAP + textW;
|
||||
uint16_t contentH = max(BATT_H, textH);
|
||||
uint16_t badgeW = contentW + PAD_X * 2;
|
||||
uint16_t badgeH = contentH + PAD_Y * 2;
|
||||
|
||||
// Position: top-right by default, top-left if metadata occupies top-right
|
||||
uint16_t bx, by;
|
||||
if (metaPos == MetaPosition::OverlayTopRight) {
|
||||
bx = MARGIN;
|
||||
by = MARGIN;
|
||||
} else {
|
||||
bx = DISPLAY_WIDTH - badgeW - MARGIN;
|
||||
by = MARGIN;
|
||||
}
|
||||
|
||||
// Draw badge background
|
||||
drawBadge(bx, by, badgeW, badgeH);
|
||||
|
||||
// Draw battery outline (white rect with 1px border)
|
||||
uint16_t iconX = bx + PAD_X;
|
||||
uint16_t iconY = by + (badgeH - BATT_H) / 2;
|
||||
_display->drawRect(iconX, iconY, BATT_W, BATT_H, TFT_WHITE);
|
||||
|
||||
// Draw nub on right side of battery
|
||||
uint16_t nubX = iconX + BATT_W;
|
||||
uint16_t nubY = iconY + (BATT_H - NUB_H) / 2;
|
||||
_display->fillRect(nubX, nubY, NUB_W, NUB_H, TFT_WHITE);
|
||||
|
||||
// Draw fill level inside battery (1px inset)
|
||||
uint16_t fillMaxW = BATT_W - 2;
|
||||
uint16_t fillW = (fillMaxW * percent) / 100;
|
||||
if (fillW > 0) {
|
||||
_display->fillRect(iconX + 1, iconY + 1, fillW, BATT_H - 2, TFT_WHITE);
|
||||
}
|
||||
|
||||
// Draw percentage text
|
||||
uint16_t textX = iconX + BATT_W + NUB_W + GAP;
|
||||
_display->setTextColor(TFT_WHITE);
|
||||
_display->setTextDatum(middle_left);
|
||||
_display->drawString(pctText, textX, by + badgeH / 2);
|
||||
}
|
||||
|
||||
void DisplayManager::refresh() {
|
||||
|
||||
Reference in New Issue
Block a user