Fix X3 issue

This commit is contained in:
jpirnay
2026-04-14 19:36:55 +02:00
parent 47dcf23f2c
commit ef31e4c1c1
2 changed files with 94 additions and 86 deletions
+90 -82
View File
@@ -232,7 +232,8 @@ static inline uint8_t bitmapExtract(const uint8_t* bitmap, const int bitPos, con
// bits — MSB-aligned; bit 7 = pixel at phyBitPos, lower (8-count) bits are zero. // bits — MSB-aligned; bit 7 = pixel at phyBitPos, lower (8-count) bits are zero.
// phyBitPos — physical X of the MSB pixel; may be negative for left-edge partial chunks. // phyBitPos — physical X of the MSB pixel; may be negative for left-edge partial chunks.
// pixelState true → black (clear bits to 0), false → white (set bits to 1). // pixelState true → black (clear bits to 0), false → white (set bits to 1).
static inline void writeRowBits(uint8_t* const row, const int phyBitPos, const uint8_t bits, const bool pixelState) { static inline void writeRowBits(uint8_t* const row, const int phyBitPos, const uint8_t bits, const bool pixelState,
const int widthBytes) {
uint8_t effectiveBits = bits; uint8_t effectiveBits = bits;
int byteIdx; int byteIdx;
int shift; int shift;
@@ -251,12 +252,10 @@ static inline void writeRowBits(uint8_t* const row, const int phyBitPos, const u
} }
if (pixelState) { if (pixelState) {
row[byteIdx] &= ~(effectiveBits >> shift); row[byteIdx] &= ~(effectiveBits >> shift);
if (shift > 0 && byteIdx + 1 < HalDisplay::DISPLAY_WIDTH_BYTES) if (shift > 0 && byteIdx + 1 < widthBytes) row[byteIdx + 1] &= ~(uint8_t)(effectiveBits << (8 - shift));
row[byteIdx + 1] &= ~(uint8_t)(effectiveBits << (8 - shift));
} else { } else {
row[byteIdx] |= (effectiveBits >> shift); row[byteIdx] |= (effectiveBits >> shift);
if (shift > 0 && byteIdx + 1 < HalDisplay::DISPLAY_WIDTH_BYTES) if (shift > 0 && byteIdx + 1 < widthBytes) row[byteIdx + 1] |= (uint8_t)(effectiveBits << (8 - shift));
row[byteIdx + 1] |= (uint8_t)(effectiveBits << (8 - shift));
} }
} }
@@ -284,33 +283,34 @@ static inline uint64_t extractGlyphBlock(const uint8_t* const bitmap, const int
// Each column k maps to row (phyYBase + k*phyYStride) via writeRowBits. // Each column k maps to row (phyYBase + k*phyYStride) via writeRowBits.
static inline void scatterBlockToFrameBuffer(uint8_t* const frameBuffer, const uint64_t pack, const int colCount, static inline void scatterBlockToFrameBuffer(uint8_t* const frameBuffer, const uint64_t pack, const int colCount,
const int phyYBase, const int phyYStride, const int phyBitPos, const int phyYBase, const int phyYStride, const int phyBitPos,
const bool pixelState) { const bool pixelState, const int displayHeight, const int widthBytes) {
for (int k = 0; k < colCount; k++) { for (int k = 0; k < colCount; k++) {
const uint8_t cols_k = static_cast<uint8_t>(pack >> (56 - 8 * k)); const uint8_t cols_k = static_cast<uint8_t>(pack >> (56 - 8 * k));
if (cols_k == 0) continue; if (cols_k == 0) continue;
const int phyY = phyYBase + k * phyYStride; const int phyY = phyYBase + k * phyYStride;
if (phyY < 0 || phyY >= HalDisplay::DISPLAY_HEIGHT) continue; if (phyY < 0 || phyY >= displayHeight) continue;
writeRowBits(frameBuffer + phyY * HalDisplay::DISPLAY_WIDTH_BYTES, phyBitPos, cols_k, pixelState); writeRowBits(frameBuffer + phyY * widthBytes, phyBitPos, cols_k, pixelState, widthBytes);
} }
} }
static void renderGlyphFastBW(uint8_t* const frameBuffer, const uint8_t* const bitmap, const int glyphWidth, static void renderGlyphFastBW(uint8_t* const frameBuffer, const uint8_t* const bitmap, const int glyphWidth,
const int glyphHeight, const int screenXBase, const int screenYBase, const int glyphHeight, const int screenXBase, const int screenYBase,
const bool pixelState, const GfxRenderer::Orientation orientation) { const bool pixelState, const GfxRenderer::Orientation orientation, const int displayWidth,
const int displayHeight, const int widthBytes) {
switch (orientation) { switch (orientation) {
case GfxRenderer::LandscapeCounterClockwise: { case GfxRenderer::LandscapeCounterClockwise: {
for (int glyphY = 0; glyphY < glyphHeight; glyphY++) { for (int glyphY = 0; glyphY < glyphHeight; glyphY++) {
const int phyY = screenYBase + glyphY; const int phyY = screenYBase + glyphY;
if (phyY < 0 || phyY >= HalDisplay::DISPLAY_HEIGHT) continue; if (phyY < 0 || phyY >= displayHeight) continue;
uint8_t* const row = frameBuffer + phyY * HalDisplay::DISPLAY_WIDTH_BYTES; uint8_t* const row = frameBuffer + phyY * widthBytes;
const int rowBitStart = glyphY * glyphWidth; const int rowBitStart = glyphY * glyphWidth;
for (int glyphX = 0; glyphX < glyphWidth; glyphX += 8) { for (int glyphX = 0; glyphX < glyphWidth; glyphX += 8) {
const int count = std::min(8, glyphWidth - glyphX); const int count = std::min(8, glyphWidth - glyphX);
const uint8_t gbyte = bitmapExtract(bitmap, rowBitStart + glyphX, count); const uint8_t gbyte = bitmapExtract(bitmap, rowBitStart + glyphX, count);
if (gbyte == 0) continue; if (gbyte == 0) continue;
const int phyBitPos = screenXBase + glyphX; const int phyBitPos = screenXBase + glyphX;
if (phyBitPos + count <= 0 || phyBitPos >= HalDisplay::DISPLAY_WIDTH) continue; if (phyBitPos + count <= 0 || phyBitPos >= displayWidth) continue;
writeRowBits(row, phyBitPos, gbyte, pixelState); writeRowBits(row, phyBitPos, gbyte, pixelState, widthBytes);
} }
} }
break; break;
@@ -318,9 +318,9 @@ static void renderGlyphFastBW(uint8_t* const frameBuffer, const uint8_t* const b
case GfxRenderer::LandscapeClockwise: { case GfxRenderer::LandscapeClockwise: {
for (int glyphY = 0; glyphY < glyphHeight; glyphY++) { for (int glyphY = 0; glyphY < glyphHeight; glyphY++) {
const int phyY = HalDisplay::DISPLAY_HEIGHT - 1 - (screenYBase + glyphY); const int phyY = displayHeight - 1 - (screenYBase + glyphY);
if (phyY < 0 || phyY >= HalDisplay::DISPLAY_HEIGHT) continue; if (phyY < 0 || phyY >= displayHeight) continue;
uint8_t* const row = frameBuffer + phyY * HalDisplay::DISPLAY_WIDTH_BYTES; uint8_t* const row = frameBuffer + phyY * widthBytes;
const int rowBitStart = glyphY * glyphWidth; const int rowBitStart = glyphY * glyphWidth;
for (int chunkEnd = glyphWidth - 1; chunkEnd >= 0; chunkEnd -= 8) { for (int chunkEnd = glyphWidth - 1; chunkEnd >= 0; chunkEnd -= 8) {
const int chunkStart = std::max(0, chunkEnd - 7); const int chunkStart = std::max(0, chunkEnd - 7);
@@ -328,9 +328,9 @@ static void renderGlyphFastBW(uint8_t* const frameBuffer, const uint8_t* const b
const uint8_t gbyte_fwd = bitmapExtract(bitmap, rowBitStart + chunkStart, count); const uint8_t gbyte_fwd = bitmapExtract(bitmap, rowBitStart + chunkStart, count);
const uint8_t gbyte = reverseBits8(gbyte_fwd >> (8 - count)); const uint8_t gbyte = reverseBits8(gbyte_fwd >> (8 - count));
if (gbyte == 0) continue; if (gbyte == 0) continue;
const int phyBitPos = HalDisplay::DISPLAY_WIDTH - 1 - screenXBase - chunkEnd; const int phyBitPos = displayWidth - 1 - screenXBase - chunkEnd;
if (phyBitPos + count <= 0 || phyBitPos >= HalDisplay::DISPLAY_WIDTH) continue; if (phyBitPos + count <= 0 || phyBitPos >= displayWidth) continue;
writeRowBits(row, phyBitPos, gbyte, pixelState); writeRowBits(row, phyBitPos, gbyte, pixelState, widthBytes);
} }
} }
break; break;
@@ -340,13 +340,13 @@ static void renderGlyphFastBW(uint8_t* const frameBuffer, const uint8_t* const b
for (int glyphY = 0; glyphY < glyphHeight; glyphY += 8) { for (int glyphY = 0; glyphY < glyphHeight; glyphY += 8) {
const int rowCount = std::min(8, glyphHeight - glyphY); const int rowCount = std::min(8, glyphHeight - glyphY);
const int phyBitPos = screenYBase + glyphY; const int phyBitPos = screenYBase + glyphY;
if (phyBitPos + rowCount <= 0 || phyBitPos >= HalDisplay::DISPLAY_WIDTH) continue; if (phyBitPos + rowCount <= 0 || phyBitPos >= displayWidth) continue;
for (int glyphX = 0; glyphX < glyphWidth; glyphX += 8) { for (int glyphX = 0; glyphX < glyphWidth; glyphX += 8) {
const int colCount = std::min(8, glyphWidth - glyphX); const int colCount = std::min(8, glyphWidth - glyphX);
const uint64_t pack = const uint64_t pack =
transpose8x8(extractGlyphBlock(bitmap, glyphWidth, glyphX, glyphY, rowCount, colCount, false)); transpose8x8(extractGlyphBlock(bitmap, glyphWidth, glyphX, glyphY, rowCount, colCount, false));
scatterBlockToFrameBuffer(frameBuffer, pack, colCount, HalDisplay::DISPLAY_HEIGHT - 1 - screenXBase - glyphX, scatterBlockToFrameBuffer(frameBuffer, pack, colCount, displayHeight - 1 - screenXBase - glyphX, -1,
-1, phyBitPos, pixelState); phyBitPos, pixelState, displayHeight, widthBytes);
} }
} }
break; break;
@@ -355,13 +355,14 @@ static void renderGlyphFastBW(uint8_t* const frameBuffer, const uint8_t* const b
case GfxRenderer::PortraitInverted: { case GfxRenderer::PortraitInverted: {
for (int glyphY = 0; glyphY < glyphHeight; glyphY += 8) { for (int glyphY = 0; glyphY < glyphHeight; glyphY += 8) {
const int rowCount = std::min(8, glyphHeight - glyphY); const int rowCount = std::min(8, glyphHeight - glyphY);
const int phyBitPos = HalDisplay::DISPLAY_WIDTH - 1 - screenYBase - (glyphY + rowCount - 1); const int phyBitPos = displayWidth - 1 - screenYBase - (glyphY + rowCount - 1);
if (phyBitPos + rowCount <= 0 || phyBitPos >= HalDisplay::DISPLAY_WIDTH) continue; if (phyBitPos + rowCount <= 0 || phyBitPos >= displayWidth) continue;
for (int glyphX = 0; glyphX < glyphWidth; glyphX += 8) { for (int glyphX = 0; glyphX < glyphWidth; glyphX += 8) {
const int colCount = std::min(8, glyphWidth - glyphX); const int colCount = std::min(8, glyphWidth - glyphX);
const uint64_t pack = const uint64_t pack =
transpose8x8(extractGlyphBlock(bitmap, glyphWidth, glyphX, glyphY, rowCount, colCount, true)); transpose8x8(extractGlyphBlock(bitmap, glyphWidth, glyphX, glyphY, rowCount, colCount, true));
scatterBlockToFrameBuffer(frameBuffer, pack, colCount, screenXBase + glyphX, 1, phyBitPos, pixelState); scatterBlockToFrameBuffer(frameBuffer, pack, colCount, screenXBase + glyphX, 1, phyBitPos, pixelState,
displayHeight, widthBytes);
} }
} }
break; break;
@@ -568,19 +569,19 @@ static inline uint8_t build2BitColMask(const uint8_t* const bitmap, const int gl
template <uint8_t drawMask, bool inverted> template <uint8_t drawMask, bool inverted>
static void renderGlyphFast2BitPortrait(uint8_t* const frameBuffer, const uint8_t* const bitmap, const int glyphWidth, static void renderGlyphFast2BitPortrait(uint8_t* const frameBuffer, const uint8_t* const bitmap, const int glyphWidth,
const int glyphHeight, const int screenXBase, const int screenYBase, const int glyphHeight, const int screenXBase, const int screenYBase,
const bool writeState) { const bool writeState, const int displayWidth, const int displayHeight,
const int widthBytes) {
for (int glyphX = 0; glyphX < glyphWidth; glyphX++) { for (int glyphX = 0; glyphX < glyphWidth; glyphX++) {
const int phyY = inverted ? (screenXBase + glyphX) : (HalDisplay::DISPLAY_HEIGHT - 1 - (screenXBase + glyphX)); const int phyY = inverted ? (screenXBase + glyphX) : (displayHeight - 1 - (screenXBase + glyphX));
if (phyY < 0 || phyY >= HalDisplay::DISPLAY_HEIGHT) continue; if (phyY < 0 || phyY >= displayHeight) continue;
uint8_t* const row = frameBuffer + phyY * HalDisplay::DISPLAY_WIDTH_BYTES; uint8_t* const row = frameBuffer + phyY * widthBytes;
for (int glyphY = 0; glyphY < glyphHeight; glyphY += 8) { for (int glyphY = 0; glyphY < glyphHeight; glyphY += 8) {
const int count = std::min(8, glyphHeight - glyphY); const int count = std::min(8, glyphHeight - glyphY);
const uint8_t mask = build2BitColMask<drawMask>(bitmap, glyphWidth, glyphX, glyphY, count, inverted); const uint8_t mask = build2BitColMask<drawMask>(bitmap, glyphWidth, glyphX, glyphY, count, inverted);
if (mask == 0) continue; if (mask == 0) continue;
const int phyBitPos = const int phyBitPos = inverted ? (displayWidth - 1 - screenYBase - (glyphY + count - 1)) : (screenYBase + glyphY);
inverted ? (HalDisplay::DISPLAY_WIDTH - 1 - screenYBase - (glyphY + count - 1)) : (screenYBase + glyphY); if (phyBitPos + count <= 0 || phyBitPos >= displayWidth) continue;
if (phyBitPos + count <= 0 || phyBitPos >= HalDisplay::DISPLAY_WIDTH) continue; writeRowBits(row, phyBitPos, mask, writeState, widthBytes);
writeRowBits(row, phyBitPos, mask, writeState);
} }
} }
} }
@@ -588,7 +589,8 @@ static void renderGlyphFast2BitPortrait(uint8_t* const frameBuffer, const uint8_
template <uint8_t drawMask> template <uint8_t drawMask>
static void renderGlyphFast2Bit(uint8_t* const frameBuffer, const uint8_t* const bitmap, const int glyphWidth, static void renderGlyphFast2Bit(uint8_t* const frameBuffer, const uint8_t* const bitmap, const int glyphWidth,
const int glyphHeight, const int screenXBase, const int screenYBase, const int glyphHeight, const int screenXBase, const int screenYBase,
const bool pixelState, const GfxRenderer::Orientation orientation) { const bool pixelState, const GfxRenderer::Orientation orientation,
const int displayWidth, const int displayHeight, const int widthBytes) {
// Non-rotated text fast path for 2-bit glyphs. Writes compact masks directly to framebuffer rows. // Non-rotated text fast path for 2-bit glyphs. Writes compact masks directly to framebuffer rows.
// TextRotation::Rotated90CW keeps the legacy per-pixel fallback path for safety and readability. // TextRotation::Rotated90CW keeps the legacy per-pixel fallback path for safety and readability.
// BW (drawMask 0x0E) honors the caller's pixelState; grayscale passes always clear the bit. // BW (drawMask 0x0E) honors the caller's pixelState; grayscale passes always clear the bit.
@@ -598,8 +600,8 @@ static void renderGlyphFast2Bit(uint8_t* const frameBuffer, const uint8_t* const
case GfxRenderer::LandscapeCounterClockwise: { case GfxRenderer::LandscapeCounterClockwise: {
for (int glyphY = 0; glyphY < glyphHeight; glyphY++) { for (int glyphY = 0; glyphY < glyphHeight; glyphY++) {
const int phyY = screenYBase + glyphY; const int phyY = screenYBase + glyphY;
if (phyY < 0 || phyY >= HalDisplay::DISPLAY_HEIGHT) continue; if (phyY < 0 || phyY >= displayHeight) continue;
uint8_t* const row = frameBuffer + phyY * HalDisplay::DISPLAY_WIDTH_BYTES; uint8_t* const row = frameBuffer + phyY * widthBytes;
const int rowStartPixel = glyphY * glyphWidth; const int rowStartPixel = glyphY * glyphWidth;
for (int glyphX = 0; glyphX < glyphWidth; glyphX += 8) { for (int glyphX = 0; glyphX < glyphWidth; glyphX += 8) {
const int count = std::min(8, glyphWidth - glyphX); const int count = std::min(8, glyphWidth - glyphX);
@@ -613,21 +615,21 @@ static void renderGlyphFast2Bit(uint8_t* const frameBuffer, const uint8_t* const
} }
if (mask == 0) continue; if (mask == 0) continue;
const int phyBitPos = screenXBase + glyphX; const int phyBitPos = screenXBase + glyphX;
if (phyBitPos + count <= 0 || phyBitPos >= HalDisplay::DISPLAY_WIDTH) continue; if (phyBitPos + count <= 0 || phyBitPos >= displayWidth) continue;
writeRowBits(row, phyBitPos, mask, writeState); writeRowBits(row, phyBitPos, mask, writeState, widthBytes);
} }
} }
break; break;
} }
case GfxRenderer::LandscapeClockwise: { case GfxRenderer::LandscapeClockwise: {
// Row-outer/chunk-inner: framebuffer rows are written at stride -DISPLAY_WIDTH_BYTES // Row-outer/chunk-inner: framebuffer rows are written at stride widthBytes
// (phyY decreases as glyphY increases). Keeping row-outer preserves sequential access // (phyY decreases as glyphY increases). Keeping row-outer preserves sequential access
// within each row, which is more cache-friendly than the chunk-outer alternative. // within each row, which is more cache-friendly than the chunk-outer alternative.
for (int glyphY = 0; glyphY < glyphHeight; glyphY++) { for (int glyphY = 0; glyphY < glyphHeight; glyphY++) {
const int phyY = HalDisplay::DISPLAY_HEIGHT - 1 - (screenYBase + glyphY); const int phyY = displayHeight - 1 - (screenYBase + glyphY);
if (phyY < 0 || phyY >= HalDisplay::DISPLAY_HEIGHT) continue; if (phyY < 0 || phyY >= displayHeight) continue;
uint8_t* const row = frameBuffer + phyY * HalDisplay::DISPLAY_WIDTH_BYTES; uint8_t* const row = frameBuffer + phyY * widthBytes;
const int rowStartPixel = glyphY * glyphWidth; const int rowStartPixel = glyphY * glyphWidth;
for (int chunkEnd = glyphWidth - 1; chunkEnd >= 0; chunkEnd -= 8) { for (int chunkEnd = glyphWidth - 1; chunkEnd >= 0; chunkEnd -= 8) {
const int chunkStart = std::max(0, chunkEnd - 7); const int chunkStart = std::max(0, chunkEnd - 7);
@@ -641,9 +643,9 @@ static void renderGlyphFast2Bit(uint8_t* const frameBuffer, const uint8_t* const
mask = build2BitRowMask<drawMask>(bitmap, rowStartPixel, chunkEnd, count, true); mask = build2BitRowMask<drawMask>(bitmap, rowStartPixel, chunkEnd, count, true);
} }
if (mask == 0) continue; if (mask == 0) continue;
const int phyBitPos = HalDisplay::DISPLAY_WIDTH - 1 - screenXBase - chunkEnd; const int phyBitPos = displayWidth - 1 - screenXBase - chunkEnd;
if (phyBitPos + count <= 0 || phyBitPos >= HalDisplay::DISPLAY_WIDTH) continue; if (phyBitPos + count <= 0 || phyBitPos >= displayWidth) continue;
writeRowBits(row, phyBitPos, mask, writeState); writeRowBits(row, phyBitPos, mask, writeState, widthBytes);
} }
} }
break; break;
@@ -651,12 +653,12 @@ static void renderGlyphFast2Bit(uint8_t* const frameBuffer, const uint8_t* const
case GfxRenderer::Portrait: case GfxRenderer::Portrait:
renderGlyphFast2BitPortrait<drawMask, false>(frameBuffer, bitmap, glyphWidth, glyphHeight, screenXBase, renderGlyphFast2BitPortrait<drawMask, false>(frameBuffer, bitmap, glyphWidth, glyphHeight, screenXBase,
screenYBase, writeState); screenYBase, writeState, displayWidth, displayHeight, widthBytes);
break; break;
case GfxRenderer::PortraitInverted: case GfxRenderer::PortraitInverted:
renderGlyphFast2BitPortrait<drawMask, true>(frameBuffer, bitmap, glyphWidth, glyphHeight, screenXBase, renderGlyphFast2BitPortrait<drawMask, true>(frameBuffer, bitmap, glyphWidth, glyphHeight, screenXBase,
screenYBase, writeState); screenYBase, writeState, displayWidth, displayHeight, widthBytes);
break; break;
} }
} }
@@ -708,19 +710,23 @@ static void renderCharImpl(const GfxRenderer& renderer, GfxRenderer::RenderMode
switch (drawMask) { switch (drawMask) {
case 0x0E: // BW case 0x0E: // BW
renderGlyphFast2Bit<0x0E>(renderer.getFrameBuffer(), bitmap, width, height, innerBase, outerBase, renderGlyphFast2Bit<0x0E>(renderer.getFrameBuffer(), bitmap, width, height, innerBase, outerBase,
pixelState, renderer.getOrientation()); pixelState, renderer.getOrientation(), renderer.getDisplayWidth(),
renderer.getDisplayHeight(), renderer.getDisplayWidthBytes());
break; break;
case 0x06: // raw {1,2} case 0x06: // raw {1,2}
renderGlyphFast2Bit<0x06>(renderer.getFrameBuffer(), bitmap, width, height, innerBase, outerBase, renderGlyphFast2Bit<0x06>(renderer.getFrameBuffer(), bitmap, width, height, innerBase, outerBase,
pixelState, renderer.getOrientation()); pixelState, renderer.getOrientation(), renderer.getDisplayWidth(),
renderer.getDisplayHeight(), renderer.getDisplayWidthBytes());
break; break;
case 0x04: // raw {2} case 0x04: // raw {2}
renderGlyphFast2Bit<0x04>(renderer.getFrameBuffer(), bitmap, width, height, innerBase, outerBase, renderGlyphFast2Bit<0x04>(renderer.getFrameBuffer(), bitmap, width, height, innerBase, outerBase,
pixelState, renderer.getOrientation()); pixelState, renderer.getOrientation(), renderer.getDisplayWidth(),
renderer.getDisplayHeight(), renderer.getDisplayWidthBytes());
break; break;
case 0x02: // raw {1} case 0x02: // raw {1}
renderGlyphFast2Bit<0x02>(renderer.getFrameBuffer(), bitmap, width, height, innerBase, outerBase, renderGlyphFast2Bit<0x02>(renderer.getFrameBuffer(), bitmap, width, height, innerBase, outerBase,
pixelState, renderer.getOrientation()); pixelState, renderer.getOrientation(), renderer.getDisplayWidth(),
renderer.getDisplayHeight(), renderer.getDisplayWidthBytes());
break; break;
} }
return; return;
@@ -760,7 +766,8 @@ static void renderCharImpl(const GfxRenderer& renderer, GfxRenderer::RenderMode
if constexpr (rotation == TextRotation::None) { if constexpr (rotation == TextRotation::None) {
if (renderMode == GfxRenderer::BW) { if (renderMode == GfxRenderer::BW) {
renderGlyphFastBW(renderer.getFrameBuffer(), bitmap, width, height, innerBase, outerBase, pixelState, renderGlyphFastBW(renderer.getFrameBuffer(), bitmap, width, height, innerBase, outerBase, pixelState,
renderer.getOrientation()); renderer.getOrientation(), renderer.getDisplayWidth(), renderer.getDisplayHeight(),
renderer.getDisplayWidthBytes());
return; return;
} }
} }
@@ -795,18 +802,20 @@ static void renderCharImpl(const GfxRenderer& renderer, GfxRenderer::RenderMode
void GfxRenderer::drawPixel(const int x, const int y, const bool state) const { void GfxRenderer::drawPixel(const int x, const int y, const bool state) const {
int phyX = 0; int phyX = 0;
int phyY = 0; int phyY = 0;
const int displayWidth = getDisplayWidth();
const int displayHeight = getDisplayHeight();
// Note: this call should be inlined for better performance // Note: this call should be inlined for better performance
rotateCoordinates(getOrientation(), x, y, &phyX, &phyY, panelWidth, panelHeight); rotateCoordinates(getOrientation(), x, y, &phyX, &phyY, displayWidth, displayHeight);
// Bounds checking against runtime panel dimensions // Bounds checking against runtime panel dimensions
if (phyX < 0 || phyX >= panelWidth || phyY < 0 || phyY >= panelHeight) { if (phyX < 0 || phyX >= displayWidth || phyY < 0 || phyY >= displayHeight) {
LOG_ERR("GFX", "!! Outside range (%d, %d) -> (%d, %d)", x, y, phyX, phyY); LOG_ERR("GFX", "!! Outside range (%d, %d) -> (%d, %d)", x, y, phyX, phyY);
return; return;
} }
// Calculate byte position and bit position // Calculate byte position and bit position
const uint32_t byteIndex = static_cast<uint32_t>(phyY) * panelWidthBytes + (phyX / 8); const uint32_t byteIndex = static_cast<uint32_t>(phyY) * getDisplayWidthBytes() + (phyX / 8);
const uint8_t bitPosition = 7 - (phyX % 8); // MSB first const uint8_t bitPosition = 7 - (phyX % 8); // MSB first
if (state) { if (state) {
@@ -910,6 +919,9 @@ void GfxRenderer::drawText(const int fontId, const int x, const int y, const cha
void GfxRenderer::drawLine(int x1, int y1, int x2, int y2, const bool state) const { void GfxRenderer::drawLine(int x1, int y1, int x2, int y2, const bool state) const {
if (fontCacheManager_ && fontCacheManager_->isScanning()) return; if (fontCacheManager_ && fontCacheManager_->isScanning()) return;
const int displayWidth = getDisplayWidth();
const int displayHeight = getDisplayHeight();
if (x1 == x2) { if (x1 == x2) {
if (y2 < y1) { if (y2 < y1) {
std::swap(y1, y2); std::swap(y1, y2);
@@ -917,10 +929,10 @@ void GfxRenderer::drawLine(int x1, int y1, int x2, int y2, const bool state) con
// In Portrait/PortraitInverted a logical vertical line maps to a physical horizontal span. // In Portrait/PortraitInverted a logical vertical line maps to a physical horizontal span.
switch (getOrientation()) { switch (getOrientation()) {
case Portrait: case Portrait:
fillPhysicalHSpan(HalDisplay::DISPLAY_HEIGHT - 1 - x1, y1, y2, state); fillPhysicalHSpan(displayHeight - 1 - x1, y1, y2, state);
return; return;
case PortraitInverted: case PortraitInverted:
fillPhysicalHSpan(x1, HalDisplay::DISPLAY_WIDTH - 1 - y2, HalDisplay::DISPLAY_WIDTH - 1 - y1, state); fillPhysicalHSpan(x1, displayWidth - 1 - y2, displayWidth - 1 - y1, state);
return; return;
default: default:
for (int y = y1; y <= y2; y++) drawPixel(x1, y, state); for (int y = y1; y <= y2; y++) drawPixel(x1, y, state);
@@ -936,8 +948,7 @@ void GfxRenderer::drawLine(int x1, int y1, int x2, int y2, const bool state) con
fillPhysicalHSpan(y1, x1, x2, state); fillPhysicalHSpan(y1, x1, x2, state);
return; return;
case LandscapeClockwise: case LandscapeClockwise:
fillPhysicalHSpan(HalDisplay::DISPLAY_HEIGHT - 1 - y1, HalDisplay::DISPLAY_WIDTH - 1 - x2, fillPhysicalHSpan(displayHeight - 1 - y1, displayWidth - 1 - x2, displayWidth - 1 - x1, state);
HalDisplay::DISPLAY_WIDTH - 1 - x1, state);
return; return;
default: default:
for (int x = x1; x <= x2; x++) drawPixel(x, y1, state); for (int x = x1; x <= x2; x++) drawPixel(x, y1, state);
@@ -1099,10 +1110,10 @@ void GfxRenderer::drawRoundedRect(const int x, const int y, const int width, con
void GfxRenderer::fillPhysicalHSpanByte(const int phyY, const int phyX_start, const int phyX_end, void GfxRenderer::fillPhysicalHSpanByte(const int phyY, const int phyX_start, const int phyX_end,
const uint8_t patternByte) const { const uint8_t patternByte) const {
const int cX0 = std::max(phyX_start, 0); const int cX0 = std::max(phyX_start, 0);
const int cX1 = std::min(phyX_end, (int)HalDisplay::DISPLAY_WIDTH - 1); const int cX1 = std::min(phyX_end, (int)getDisplayWidth() - 1);
if (cX0 > cX1 || phyY < 0 || phyY >= (int)HalDisplay::DISPLAY_HEIGHT) return; if (cX0 > cX1 || phyY < 0 || phyY >= (int)getDisplayHeight()) return;
uint8_t* const row = frameBuffer + phyY * HalDisplay::DISPLAY_WIDTH_BYTES; uint8_t* const row = frameBuffer + phyY * getDisplayWidthBytes();
const int startByte = cX0 >> 3; const int startByte = cX0 >> 3;
const int endByte = cX1 >> 3; const int endByte = cX1 >> 3;
const int leftBits = cX0 & 7; // first bit index within startByte const int leftBits = cX0 & 7; // first bit index within startByte
@@ -1143,20 +1154,22 @@ void GfxRenderer::fillPhysicalHSpan(const int phyY, const int phyX_start, const
void GfxRenderer::fillRect(const int x, const int y, const int width, const int height, const bool state) const { void GfxRenderer::fillRect(const int x, const int y, const int width, const int height, const bool state) const {
if (width <= 0 || height <= 0) return; if (width <= 0 || height <= 0) return;
const int displayWidth = getDisplayWidth();
const int displayHeight = getDisplayHeight();
// For each orientation, one logical dimension maps to a constant physical row, allowing the // For each orientation, one logical dimension maps to a constant physical row, allowing the
// perpendicular dimension to be written as a byte-level span — eliminating per-pixel overhead. // perpendicular dimension to be written as a byte-level span — eliminating per-pixel overhead.
switch (getOrientation()) { switch (getOrientation()) {
case Portrait: case Portrait:
// Logical column x → physical row (479-x); logical y range → physical x span // Logical column x → physical row (displayHeight-1-x); logical y range → physical x span
for (int lx = x; lx < x + width; lx++) { for (int lx = x; lx < x + width; lx++) {
fillPhysicalHSpan(HalDisplay::DISPLAY_HEIGHT - 1 - lx, y, y + height - 1, state); fillPhysicalHSpan(displayHeight - 1 - lx, y, y + height - 1, state);
} }
return; return;
case PortraitInverted: case PortraitInverted:
// Logical column x → physical row x; logical y range → physical x span (mirrored) // Logical column x → physical row x; logical y range → physical x span (mirrored)
for (int lx = x; lx < x + width; lx++) { for (int lx = x; lx < x + width; lx++) {
fillPhysicalHSpan(lx, HalDisplay::DISPLAY_WIDTH - 1 - (y + height - 1), HalDisplay::DISPLAY_WIDTH - 1 - y, fillPhysicalHSpan(lx, displayWidth - 1 - (y + height - 1), displayWidth - 1 - y, state);
state);
} }
return; return;
case LandscapeCounterClockwise: case LandscapeCounterClockwise:
@@ -1166,10 +1179,9 @@ void GfxRenderer::fillRect(const int x, const int y, const int width, const int
} }
return; return;
case LandscapeClockwise: case LandscapeClockwise:
// Logical row y → physical row (479-y); logical x range → physical x span (mirrored) // Logical row y → physical row (displayHeight-1-y); logical x range → physical x span (mirrored)
for (int ly = y; ly < y + height; ly++) { for (int ly = y; ly < y + height; ly++) {
fillPhysicalHSpan(HalDisplay::DISPLAY_HEIGHT - 1 - ly, HalDisplay::DISPLAY_WIDTH - 1 - (x + width - 1), fillPhysicalHSpan(displayHeight - 1 - ly, displayWidth - 1 - (x + width - 1), displayWidth - 1 - x, state);
HalDisplay::DISPLAY_WIDTH - 1 - x, state);
} }
return; return;
} }
@@ -1216,7 +1228,7 @@ void GfxRenderer::fillRectDither(const int x, const int y, const int width, cons
switch (getOrientation()) { switch (getOrientation()) {
case Portrait: case Portrait:
for (int lx = x; lx < x + width; lx++) { for (int lx = x; lx < x + width; lx++) {
const int phyY = HalDisplay::DISPLAY_HEIGHT - 1 - lx; const int phyY = getDisplayHeight() - 1 - lx;
const uint8_t pb = (phyY % 2 == 0) ? 0xAA : 0x55; const uint8_t pb = (phyY % 2 == 0) ? 0xAA : 0x55;
fillPhysicalHSpanByte(phyY, y, y + height - 1, pb); fillPhysicalHSpanByte(phyY, y, y + height - 1, pb);
} }
@@ -1225,8 +1237,7 @@ void GfxRenderer::fillRectDither(const int x, const int y, const int width, cons
for (int lx = x; lx < x + width; lx++) { for (int lx = x; lx < x + width; lx++) {
const int phyY = lx; const int phyY = lx;
const uint8_t pb = (phyY % 2 == 0) ? 0xAA : 0x55; const uint8_t pb = (phyY % 2 == 0) ? 0xAA : 0x55;
fillPhysicalHSpanByte(phyY, HalDisplay::DISPLAY_WIDTH - 1 - (y + height - 1), fillPhysicalHSpanByte(phyY, getDisplayWidth() - 1 - (y + height - 1), getDisplayWidth() - 1 - y, pb);
HalDisplay::DISPLAY_WIDTH - 1 - y, pb);
} }
return; return;
case LandscapeCounterClockwise: case LandscapeCounterClockwise:
@@ -1238,10 +1249,9 @@ void GfxRenderer::fillRectDither(const int x, const int y, const int width, cons
return; return;
case LandscapeClockwise: case LandscapeClockwise:
for (int ly = y; ly < y + height; ly++) { for (int ly = y; ly < y + height; ly++) {
const int phyY = HalDisplay::DISPLAY_HEIGHT - 1 - ly; const int phyY = getDisplayHeight() - 1 - ly;
const uint8_t pb = (phyY % 2 == 0) ? 0x55 : 0xAA; const uint8_t pb = (phyY % 2 == 0) ? 0x55 : 0xAA;
fillPhysicalHSpanByte(phyY, HalDisplay::DISPLAY_WIDTH - 1 - (x + width - 1), fillPhysicalHSpanByte(phyY, getDisplayWidth() - 1 - (x + width - 1), getDisplayWidth() - 1 - x, pb);
HalDisplay::DISPLAY_WIDTH - 1 - x, pb);
} }
return; return;
} }
@@ -1255,7 +1265,7 @@ void GfxRenderer::fillRectDither(const int x, const int y, const int width, cons
switch (getOrientation()) { switch (getOrientation()) {
case Portrait: case Portrait:
for (int lx = x; lx < x + width; lx++) { for (int lx = x; lx < x + width; lx++) {
const int phyY = HalDisplay::DISPLAY_HEIGHT - 1 - lx; const int phyY = getDisplayHeight() - 1 - lx;
if (phyY % 2 == 0) continue; // all-white row — no dark pixels to write if (phyY % 2 == 0) continue; // all-white row — no dark pixels to write
fillPhysicalHSpanByte(phyY, y, y + height - 1, 0x55); fillPhysicalHSpanByte(phyY, y, y + height - 1, 0x55);
} }
@@ -1264,8 +1274,7 @@ void GfxRenderer::fillRectDither(const int x, const int y, const int width, cons
for (int lx = x; lx < x + width; lx++) { for (int lx = x; lx < x + width; lx++) {
const int phyY = lx; const int phyY = lx;
if (phyY % 2 != 0) continue; // all-white row if (phyY % 2 != 0) continue; // all-white row
fillPhysicalHSpanByte(phyY, HalDisplay::DISPLAY_WIDTH - 1 - (y + height - 1), fillPhysicalHSpanByte(phyY, getDisplayWidth() - 1 - (y + height - 1), getDisplayWidth() - 1 - y, 0xAA);
HalDisplay::DISPLAY_WIDTH - 1 - y, 0xAA);
} }
return; return;
case LandscapeCounterClockwise: case LandscapeCounterClockwise:
@@ -1277,10 +1286,9 @@ void GfxRenderer::fillRectDither(const int x, const int y, const int width, cons
return; return;
case LandscapeClockwise: case LandscapeClockwise:
for (int ly = y; ly < y + height; ly++) { for (int ly = y; ly < y + height; ly++) {
const int phyY = HalDisplay::DISPLAY_HEIGHT - 1 - ly; const int phyY = getDisplayHeight() - 1 - ly;
if (phyY % 2 == 0) continue; // all-white row if (phyY % 2 == 0) continue; // all-white row
fillPhysicalHSpanByte(phyY, HalDisplay::DISPLAY_WIDTH - 1 - (x + width - 1), fillPhysicalHSpanByte(phyY, getDisplayWidth() - 1 - (x + width - 1), getDisplayWidth() - 1 - x, 0xAA);
HalDisplay::DISPLAY_WIDTH - 1 - x, 0xAA);
} }
return; return;
} }
@@ -1403,7 +1411,7 @@ void GfxRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, co
const auto currentOrientation = getOrientation(); const auto currentOrientation = getOrientation();
int rotatedX = 0; int rotatedX = 0;
int rotatedY = 0; int rotatedY = 0;
rotateCoordinates(currentOrientation, x, y, &rotatedX, &rotatedY, panelWidth, panelHeight); rotateCoordinates(currentOrientation, x, y, &rotatedX, &rotatedY, getDisplayWidth(), getDisplayHeight());
// Rotate origin corner // Rotate origin corner
switch (currentOrientation) { switch (currentOrientation) {
case Portrait: case Portrait:
+4 -4
View File
@@ -50,10 +50,10 @@ class GfxRenderer {
// rendering. See drawMaskFor2BitMode() in GfxRenderer.cpp for the per-level // rendering. See drawMaskFor2BitMode() in GfxRenderer.cpp for the per-level
// pixel breakdown and a worked example glyph. // pixel breakdown and a worked example glyph.
uint8_t* frameBuffer = nullptr; uint8_t* frameBuffer = nullptr;
uint16_t panelWidth = HalDisplay::DISPLAY_WIDTH; uint16_t panelWidth = 0; // set in begin()
uint16_t panelHeight = HalDisplay::DISPLAY_HEIGHT; uint16_t panelHeight = 0; // set in begin()
uint16_t panelWidthBytes = HalDisplay::DISPLAY_WIDTH_BYTES; uint16_t panelWidthBytes = 0; // set in begin()
uint32_t frameBufferSize = HalDisplay::BUFFER_SIZE; uint32_t frameBufferSize = 0; // set in begin()
std::vector<uint8_t*> bwBufferChunks; std::vector<uint8_t*> bwBufferChunks;
std::map<int, EpdFontFamily> fontMap; std::map<int, EpdFontFamily> fontMap;