Faster TFTColor processing (#10814)

This commit is contained in:
Jonathan Bennett
2026-06-29 21:50:09 -05:00
committed by GitHub
parent fd8ba4fa4e
commit 21dbe21745
3 changed files with 65 additions and 7 deletions

View File

@@ -792,6 +792,20 @@ void clearTFTColorRegions()
colorRegionCount = 0;
}
// Per-row culling fast path (see TFTColorRegions.h / resolveTFTColorPixelRow()).
uint8_t tftColorRowRegions[MAX_TFT_COLOR_REGIONS];
uint8_t tftColorRowCount = 0;
void beginTFTColorRow(int16_t y)
{
tftColorRowCount = 0;
for (uint8_t i = 0; i < colorRegionCount; i++) {
const TFTColorRegion &r = colorRegions[i];
if (y >= r.y && y < r.y + r.height)
tftColorRowRegions[tftColorRowCount++] = i;
}
}
uint16_t resolveTFTColorPixel(int16_t x, int16_t y, bool isset, uint16_t defaultOnColor, uint16_t defaultOffColor)
{
for (int i = static_cast<int>(colorRegionCount) - 1; i >= 0; i--) {

View File

@@ -67,6 +67,29 @@ uint16_t resolveTFTColorPixel(int16_t x, int16_t y, bool isset, uint16_t default
// Resolve effective region-mapped OFF color at a coordinate in native-endian RGB565.
uint16_t resolveTFTOffColorAt(int16_t x, int16_t y, uint16_t defaultOffColor);
// -- Per-row fast path for the hot pixel loops in TFTDisplay::display() --------
// resolveTFTColorPixel() is O(regions) per pixel; for a full 800x480 repaint that
// dominates redraw time. Regions are vertically localized, so cull to the regions
// overlapping the current row once per row, then resolve each pixel against only
// those (inlined here, so no per-pixel cross-TU call).
extern uint8_t tftColorRowRegions[MAX_TFT_COLOR_REGIONS]; // indices into colorRegions[], ascending
extern uint8_t tftColorRowCount;
// Build tftColorRowRegions for row y. Call once per row before resolveTFTColorPixelRow().
void beginTFTColorRow(int16_t y);
// Resolve one pixel against the current row's active regions (set by beginTFTColorRow()).
// Highest-index region wins, matching resolveTFTColorPixel()'s precedence.
inline uint16_t resolveTFTColorPixelRow(int16_t x, bool isset, uint16_t defaultOnColor, uint16_t defaultOffColor)
{
for (int j = static_cast<int>(tftColorRowCount) - 1; j >= 0; j--) {
const TFTColorRegion &r = colorRegions[tftColorRowRegions[j]];
if (x >= r.x && x < r.x + r.width)
return isset ? r.onColorBe : r.offColorBe;
}
return isset ? defaultOnColor : defaultOffColor;
}
// -- Theme engine ------------------------------------------------------
// Each theme has four fields that work together:
//

View File

@@ -1270,13 +1270,30 @@ void TFTDisplay::display(bool fromBlank)
y_byteMask = (1 << (y & 7));
uint16_t *chunkRow = repaintChunkBuffer + (row * displayWidth);
// Step 1: fill the whole row with the default colors. No per-pixel
// region scan, so background pixels (the bulk of the screen) are O(1).
for (x = 0; x < displayWidth; x++) {
isset = (buffer[x + y_byteIndex] & y_byteMask) != 0;
if (hasColorRegions) {
chunkRow[x] = graphics::resolveTFTColorPixel(static_cast<int16_t>(x), static_cast<int16_t>(y), isset,
colorTftWhite, colorTftBlack);
} else {
chunkRow[x] = isset ? colorTftWhite : colorTftBlack;
chunkRow[x] = isset ? colorTftWhite : colorTftBlack;
}
// Step 2: overprint each region overlapping this row, applied in
// ascending index order so the highest-index region wins (matches
// resolveTFTColorPixel precedence). Only region-covered pixels are
// re-touched, so total cost is ~screen + sum of region spans.
if (hasColorRegions) {
graphics::beginTFTColorRow(static_cast<int16_t>(y));
for (uint8_t k = 0; k < graphics::tftColorRowCount; k++) {
const graphics::TFTColorRegion &r = graphics::colorRegions[graphics::tftColorRowRegions[k]];
int32_t xs = r.x > 0 ? r.x : 0;
int32_t xe = r.x + r.width;
if (xe > (int32_t)displayWidth)
xe = (int32_t)displayWidth;
for (int32_t xx = xs; xx < xe; xx++) {
isset = (buffer[xx + y_byteIndex] & y_byteMask) != 0;
chunkRow[xx] = isset ? r.onColorBe : r.offColorBe;
}
}
}
}
@@ -1363,12 +1380,16 @@ void TFTDisplay::display(bool fromBlank)
}
// Step 3: Copy only the changed span into the pixel line buffer.
#if GRAPHICS_TFT_COLORING_ENABLED
if (hasColorRegions)
graphics::beginTFTColorRow(static_cast<int16_t>(y));
#endif
for (x = x_FirstPixelUpdate; x <= x_LastPixelUpdate; x++) {
isset = buffer[x + y_byteIndex] & y_byteMask;
#if GRAPHICS_TFT_COLORING_ENABLED
if (hasColorRegions) {
linePixelBuffer[x] = graphics::resolveTFTColorPixel(static_cast<int16_t>(x), static_cast<int16_t>(y), isset,
colorTftWhite, colorTftBlack);
linePixelBuffer[x] =
graphics::resolveTFTColorPixelRow(static_cast<int16_t>(x), isset, colorTftWhite, colorTftBlack);
} else {
linePixelBuffer[x] = isset ? colorTftWhite : colorTftBlack;
}