adds bezel compensation settings

This commit is contained in:
cottongin
2026-01-27 21:40:52 -05:00
parent c2a966a6ea
commit 80c9e7a1d6
24 changed files with 428 additions and 155 deletions

View File

@@ -621,15 +621,20 @@ void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char
const int pageHeight = getScreenHeight();
constexpr int buttonWidth = 106;
constexpr int buttonHeight = 40;
constexpr int buttonY = 40; // Distance from bottom
constexpr int textYOffset = 7; // Distance from top of button to text baseline
constexpr int buttonPositions[] = {25, 130, 245, 350};
constexpr int baseButtonY = 40; // Base distance from bottom
constexpr int textYOffset = 7; // Distance from top of button to text baseline
constexpr int baseButtonPositions[] = {25, 130, 245, 350};
const char* labels[] = {btn1, btn2, btn3, btn4};
// Apply bezel compensation (in portrait mode, bottom bezel affects Y position)
const int bezelBottom = getBezelOffsetBottom();
const int bezelLeft = getBezelOffsetLeft();
const int buttonY = baseButtonY + bezelBottom;
for (int i = 0; i < 4; i++) {
// Only draw if the label is non-empty
if (labels[i] != nullptr && labels[i][0] != '\0') {
const int x = buttonPositions[i];
const int x = baseButtonPositions[i] + bezelLeft;
fillRect(x, pageHeight - buttonY, buttonWidth, buttonHeight, false);
drawRect(x, pageHeight - buttonY, buttonWidth, buttonHeight);
const int textWidth = getTextWidth(fontId, labels[i]);
@@ -648,9 +653,14 @@ void GfxRenderer::drawSideButtonHints(const int fontId, const char* topBtn, cons
const int screenWidth = getScreenWidth();
constexpr int buttonWidth = 40; // Width on screen (height when rotated)
constexpr int buttonHeight = 80; // Height on screen (width when rotated)
constexpr int buttonX = 5; // Distance from right edge
constexpr int baseButtonX = 5; // Base distance from right edge
// Position for the button group - buttons share a border so they're adjacent
constexpr int topButtonY = 345; // Top button position
constexpr int baseTopButtonY = 345; // Base top button position
// Apply bezel compensation (in portrait mode)
const int bezelRight = getBezelOffsetRight();
const int buttonX = baseButtonX + bezelRight;
const int topButtonY = baseTopButtonY; // Y position doesn't need adjustment for side buttons
const char* labels[] = {topBtn, bottomBtn};
@@ -979,31 +989,107 @@ void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp,
*x += glyph->advanceX;
}
// Helper to map physical bezel edge to logical edge based on orientation
// bezelEdge: 0=physical bottom, 1=physical top, 2=physical left, 3=physical right (in portrait)
// Returns: 0=logical bottom, 1=logical top, 2=logical left, 3=logical right
int mapPhysicalToLogicalEdge(int bezelEdge, GfxRenderer::Orientation orientation) {
switch (orientation) {
case GfxRenderer::Portrait:
return bezelEdge;
case GfxRenderer::LandscapeClockwise:
switch (bezelEdge) {
case 0: return 2; // Physical bottom -> logical left
case 1: return 3; // Physical top -> logical right
case 2: return 1; // Physical left -> logical top
case 3: return 0; // Physical right -> logical bottom
}
break;
case GfxRenderer::PortraitInverted:
switch (bezelEdge) {
case 0: return 1; // Physical bottom -> logical top
case 1: return 0; // Physical top -> logical bottom
case 2: return 3; // Physical left -> logical right
case 3: return 2; // Physical right -> logical left
}
break;
case GfxRenderer::LandscapeCounterClockwise:
switch (bezelEdge) {
case 0: return 3; // Physical bottom -> logical right
case 1: return 2; // Physical top -> logical left
case 2: return 0; // Physical left -> logical bottom
case 3: return 1; // Physical right -> logical top
}
break;
}
return bezelEdge;
}
int GfxRenderer::getViewableMarginTop() const {
int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation);
return BASE_VIEWABLE_MARGIN_TOP + (logicalEdge == 1 ? bezelCompensation : 0);
}
int GfxRenderer::getViewableMarginRight() const {
int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation);
return BASE_VIEWABLE_MARGIN_RIGHT + (logicalEdge == 3 ? bezelCompensation : 0);
}
int GfxRenderer::getViewableMarginBottom() const {
int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation);
return BASE_VIEWABLE_MARGIN_BOTTOM + (logicalEdge == 0 ? bezelCompensation : 0);
}
int GfxRenderer::getViewableMarginLeft() const {
int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation);
return BASE_VIEWABLE_MARGIN_LEFT + (logicalEdge == 2 ? bezelCompensation : 0);
}
int GfxRenderer::getBezelOffsetTop() const {
int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation);
return (logicalEdge == 1) ? bezelCompensation : 0;
}
int GfxRenderer::getBezelOffsetRight() const {
int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation);
return (logicalEdge == 3) ? bezelCompensation : 0;
}
int GfxRenderer::getBezelOffsetBottom() const {
int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation);
return (logicalEdge == 0) ? bezelCompensation : 0;
}
int GfxRenderer::getBezelOffsetLeft() const {
int logicalEdge = mapPhysicalToLogicalEdge(bezelEdge, orientation);
return (logicalEdge == 2) ? bezelCompensation : 0;
}
void GfxRenderer::getOrientedViewableTRBL(int* outTop, int* outRight, int* outBottom, int* outLeft) const {
// Get base margins rotated for current orientation, with bezel compensation applied
switch (orientation) {
case Portrait:
*outTop = VIEWABLE_MARGIN_TOP;
*outRight = VIEWABLE_MARGIN_RIGHT;
*outBottom = VIEWABLE_MARGIN_BOTTOM;
*outLeft = VIEWABLE_MARGIN_LEFT;
*outTop = getViewableMarginTop();
*outRight = getViewableMarginRight();
*outBottom = getViewableMarginBottom();
*outLeft = getViewableMarginLeft();
break;
case LandscapeClockwise:
*outTop = VIEWABLE_MARGIN_LEFT;
*outRight = VIEWABLE_MARGIN_TOP;
*outBottom = VIEWABLE_MARGIN_RIGHT;
*outLeft = VIEWABLE_MARGIN_BOTTOM;
*outTop = BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0);
*outRight = BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0);
*outBottom = BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0);
*outLeft = BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0);
break;
case PortraitInverted:
*outTop = VIEWABLE_MARGIN_BOTTOM;
*outRight = VIEWABLE_MARGIN_LEFT;
*outBottom = VIEWABLE_MARGIN_TOP;
*outLeft = VIEWABLE_MARGIN_RIGHT;
*outTop = BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0);
*outRight = BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0);
*outBottom = BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0);
*outLeft = BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0);
break;
case LandscapeCounterClockwise:
*outTop = VIEWABLE_MARGIN_RIGHT;
*outRight = VIEWABLE_MARGIN_BOTTOM;
*outBottom = VIEWABLE_MARGIN_LEFT;
*outLeft = VIEWABLE_MARGIN_TOP;
*outTop = BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0);
*outRight = BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0);
*outBottom = BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0);
*outLeft = BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0);
break;
}
}

View File

@@ -28,9 +28,17 @@ class GfxRenderer {
static_assert(BW_BUFFER_CHUNK_SIZE * BW_BUFFER_NUM_CHUNKS == EInkDisplay::BUFFER_SIZE,
"BW buffer chunking does not line up with display buffer size");
// Base viewable margins (hardware-specific, before bezel compensation)
static constexpr int BASE_VIEWABLE_MARGIN_TOP = 9;
static constexpr int BASE_VIEWABLE_MARGIN_RIGHT = 3;
static constexpr int BASE_VIEWABLE_MARGIN_BOTTOM = 3;
static constexpr int BASE_VIEWABLE_MARGIN_LEFT = 3;
EInkDisplay& einkDisplay;
RenderMode renderMode;
Orientation orientation;
int bezelCompensation = 0; // Pixels to add for bezel defect compensation
int bezelEdge = 0; // Which physical edge (0=bottom, 1=top, 2=left, 3=right in portrait)
uint8_t* bwBufferChunks[BW_BUFFER_NUM_CHUNKS] = {nullptr};
std::map<int, EpdFontFamily> fontMap;
void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState,
@@ -42,10 +50,24 @@ class GfxRenderer {
explicit GfxRenderer(EInkDisplay& einkDisplay) : einkDisplay(einkDisplay), renderMode(BW), orientation(Portrait) {}
~GfxRenderer() { freeBwBufferChunks(); }
static constexpr int VIEWABLE_MARGIN_TOP = 9;
static constexpr int VIEWABLE_MARGIN_RIGHT = 3;
static constexpr int VIEWABLE_MARGIN_BOTTOM = 3;
static constexpr int VIEWABLE_MARGIN_LEFT = 3;
// Viewable margins (includes bezel compensation applied to the configured edge)
int getViewableMarginTop() const;
int getViewableMarginRight() const;
int getViewableMarginBottom() const;
int getViewableMarginLeft() const;
// Bezel compensation configuration
void setBezelCompensation(int amount, int edge) {
bezelCompensation = amount;
bezelEdge = edge;
}
// Get bezel offset for each logical edge (0 if not the compensated edge)
// Use these to add bezel compensation to hardcoded margins in UI screens
int getBezelOffsetTop() const;
int getBezelOffsetRight() const;
int getBezelOffsetBottom() const;
int getBezelOffsetLeft() const;
// Setup
void insertFont(int fontId, EpdFontFamily font);