WIP: Split Render for multiple screen resolutions (#1888)

* Create README.md

* Move to new folder

* Migrating

* Migrate Remainder

* format fix (all but one) (#1889)

* Update USBPDDebug_FS2711.cpp

* Delete PrintVoltage.cpp

* Copy in 128x32 template

* Mask drawing for 96x16

* Import #1819

* Update Font.h

* Homescreen

* Update draw_homescreen_detailed.cpp

* Fix oled normal draw for variable height

* Update OLED.cpp

* Draw settings icons

* Update draw_homescreen_simplified.cpp

* Update draw_power_source_icon.cpp

* Fixup oled drawing for fill area

* Update the region fill for mixed heights

* Fix newline height

* FIXUP! Draw icons in settings menu at correct size

* Fix scrollbar

* Update settingsGUI.cpp

* S60(P) Disable auto display rotation

* On tall oled, scroll in 2 line increments

* Bugfix transition L<->R

@discip I take it back, there was a bug :)

* Draw every other one on transitions

* .

* cleanup

* Bootup logo: Draw in centre

* Update OLED.hpp

---------

Co-authored-by: discip <53649486+discip@users.noreply.github.com>
This commit is contained in:
Ben V. Brown
2024-07-12 08:27:05 +10:00
committed by GitHub
parent 18e936ae6d
commit 48649908a7
76 changed files with 2052 additions and 1588 deletions

View File

@@ -15,6 +15,7 @@ void delay() {
}
void BootLogo::handleShowingLogo(const uint8_t *ptrLogoArea) {
OLED::clearScreen();
// Read the first few bytes and figure out what format we are looking at
if (OLD_LOGO_HEADER_VALUE == *(reinterpret_cast<const uint32_t *>(ptrLogoArea))) {
showOldFormat(ptrLogoArea);
@@ -23,11 +24,17 @@ void BootLogo::handleShowingLogo(const uint8_t *ptrLogoArea) {
}
OLED::clearScreen();
OLED::refresh();
}
void BootLogo::showOldFormat(const uint8_t *ptrLogoArea) {
#ifdef OLED_128x32
// Draw in middle
OLED::drawAreaSwapped(16, 8, 96, 16, (uint8_t *)(ptrLogoArea + 4));
#else
OLED::drawAreaSwapped(0, 0, 96, 16, (uint8_t *)(ptrLogoArea + 4));
#endif
OLED::refresh();
// Delay here with static logo until a button is pressed or its been the amount of seconds set by the user
delay();
@@ -85,8 +92,12 @@ int BootLogo::showNewFrame(const uint8_t *ptrLogoArea) {
return 1;
break;
case 0xFF:
// Full frame update
// Full frame update
#ifdef OLED_128x32
OLED::drawArea(16, 8, 96, 16, ptrLogoArea + 1);
#else
OLED::drawArea(0, 0, 96, 16, ptrLogoArea + 1);
#endif
length = 96;
break;
default:
@@ -95,7 +106,11 @@ int BootLogo::showNewFrame(const uint8_t *ptrLogoArea) {
for (int p = 0; p < length; p++) {
uint8_t index = ptrLogoArea[1 + (p * 2)];
uint8_t value = ptrLogoArea[2 + (p * 2)];
#ifdef OLED_128x32
OLED::drawArea(16 + (index % 96), index >= 96 ? 16 : 8, 1, 8, &value);
#else
OLED::drawArea(index % 96, index >= 96 ? 8 : 0, 1, 8, &value);
#endif
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -174,12 +174,6 @@ void OLED::drawChar(const uint16_t charCode, const FontStyle fontStyle, const ui
case FontStyle::SMALL:
case FontStyle::LARGE:
default:
if (charCode == '\x01' && cursor_y == 0) { // 0x01 is used as new line char
setCursor(soft_x_limit, 8);
return;
} else if (charCode <= 0x01) {
return;
}
currentFont = nullptr;
index = 0;
switch (fontStyle) {
@@ -193,6 +187,12 @@ void OLED::drawChar(const uint16_t charCode, const FontStyle fontStyle, const ui
fontWidth = 12;
break;
}
if (charCode == '\x01' && cursor_y == 0) { // 0x01 is used as new line char
setCursor(soft_x_limit, fontHeight);
return;
} else if (charCode <= 0x01) {
return;
}
currentFont = fontStyle == FontStyle::SMALL ? FontSectionInfo.font06_start_ptr : FontSectionInfo.font12_start_ptr;
index = charCode - 2;
@@ -209,17 +209,22 @@ void OLED::drawChar(const uint16_t charCode, const FontStyle fontStyle, const ui
*/
void OLED::drawScrollIndicator(uint8_t y, uint8_t height) {
union u_type {
uint16_t whole;
uint8_t strips[2];
uint32_t whole;
uint8_t strips[4];
} column;
column.whole = (1 << height) - 1;
column.whole <<= y;
column.whole = (1 << height) - 1; // preload a set of set bits of height
column.whole <<= y; // Shift down by the y value
// Draw a one pixel wide bar to the left with a single pixel as
// the scroll indicator.
fillArea(OLED_WIDTH - 1, 0, 1, 8, column.strips[0]);
fillArea(OLED_WIDTH - 1, 8, 1, 8, column.strips[1]);
#if OLED_HEIGHT == 32
fillArea(OLED_WIDTH - 1, 16, 1, 8, column.strips[2]);
fillArea(OLED_WIDTH - 1, 24, 1, 8, column.strips[3]);
#endif
}
/**
@@ -269,16 +274,18 @@ void OLED::transitionSecondaryFramebuffer(const bool forwardNavigation, const Ti
stripBackPointers[1] = &secondFrameBuffer[FRAMEBUFFER_START + OLED_WIDTH];
#ifdef OLED_128x32
stripBackPointers[2] = &secondFrameBuffer[OLED_WIDTH * 2];
stripBackPointers[3] = &secondFrameBuffer[OLED_WIDTH * 3];
stripBackPointers[2] = &secondFrameBuffer[FRAMEBUFFER_START + (OLED_WIDTH * 2)];
stripBackPointers[3] = &secondFrameBuffer[FRAMEBUFFER_START + (OLED_WIDTH * 3)];
#endif /* OLED_128x32 */
TickType_t totalDuration = TICKS_100MS * 5; // 500ms
TickType_t duration = 0;
TickType_t start = xTaskGetTickCount();
uint8_t offset = 0;
uint32_t loopCounter = 0;
TickType_t startDraw = xTaskGetTickCount();
while (duration <= totalDuration) {
loopCounter++;
duration = xTaskGetTickCount() - start;
uint16_t progress = ((duration * 100) / totalDuration); // Percentage of the period we are through for animation
progress = easeInOutTiming(progress);
@@ -316,7 +323,14 @@ void OLED::transitionSecondaryFramebuffer(const bool forwardNavigation, const Ti
memmove(&stripPointers[3][newStart], &stripBackPointers[3][newEnd], progress);
#endif /* OLED_128x32 */
#ifdef OLED_128x32
if (loopCounter % 2 == 0) {
refresh();
}
#else
refresh(); // Now refresh to write out the contents to the new page
#endif /* OLED_128x32 */
vTaskDelayUntil(&startDraw, TICKS_100MS / 7);
buttonsReleased |= getButtonState() == BUTTON_NONE;
if (getButtonState() != BUTTON_NONE && buttonsReleased) {
@@ -325,7 +339,7 @@ void OLED::transitionSecondaryFramebuffer(const bool forwardNavigation, const Ti
return;
}
}
refresh(); //
refresh(); // redraw at the end if required
}
void OLED::useSecondaryFramebuffer(bool useSecondary) {
@@ -389,7 +403,14 @@ void OLED::transitionScrollDown(const TickType_t viewEnterTime) {
refresh(); // Now refresh to write out the contents to the new page
return;
}
#ifdef OLED_128x32
// To keep things faster, only redraw every second line
if (heightPos % 2 == 0) {
refresh(); // Now refresh to write out the contents to the new page
}
#else
refresh(); // Now refresh to write out the contents to the new page
#endif
vTaskDelayUntil(&startDraw, TICKS_100MS / 7);
}
}
@@ -443,7 +464,15 @@ void OLED::transitionScrollUp(const TickType_t viewEnterTime) {
refresh(); // Now refresh to write out the contents to the new page
return;
}
#ifdef OLED_128x32
// To keep things faster, only redraw every second line
if (heightPos % 2 == 0) {
refresh(); // Now refresh to write out the contents to the new page
}
#else
refresh(); // Now refresh to write out the contents to the new page
#endif
vTaskDelayUntil(&startDraw, TICKS_100MS / 7);
}
}
@@ -639,76 +668,65 @@ void OLED::drawSymbol(uint8_t symbolID) {
}
// Draw an area, but y must be aligned on 0/8 offset
void OLED::drawArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uint8_t *ptr) {
// Splat this from x->x+wide in two strides
if (x <= -wide) {
void OLED::drawArea(int16_t x, int8_t y, uint8_t width, uint8_t height, const uint8_t *ptr) {
// Splat this from x->x+width in two strides
if (x <= -width) {
return; // cutoffleft
}
if (x > 96) {
if (x > OLED_WIDTH) {
return; // cutoff right
}
uint8_t visibleStart = 0;
uint8_t visibleEnd = wide;
uint8_t visibleEnd = width;
// trimming to draw partials
if (x < 0) {
visibleStart -= x; // subtract negative value == add absolute value
}
if (x + wide > 96) {
visibleEnd = 96 - x;
if (x + width > OLED_WIDTH) {
visibleEnd = OLED_WIDTH - x;
}
if (y == 0) {
// Splat first line of data
uint8_t rowsDrawn = 0;
while (height > 0) {
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
stripPointers[0][xx + x] = ptr[xx];
stripPointers[(y / 8) + rowsDrawn][x + xx] = ptr[xx + (rowsDrawn * width)];
}
height -= 8;
rowsDrawn++;
}
if (y == 8 || height >= 16) {
// Splat the second line
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
stripPointers[1][x + xx] = ptr[xx + (height == 16 ? wide : 0)];
}
}
// TODO NEEDS HEIGHT HANDLERS for 24/32
}
// Draw an area, but y must be aligned on 0/8 offset
// For data which has octets swapped in a 16-bit word.
void OLED::drawAreaSwapped(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uint8_t *ptr) {
// Splat this from x->x+wide in two strides
if (x <= -wide) {
void OLED::drawAreaSwapped(int16_t x, int8_t y, uint8_t width, uint8_t height, const uint8_t *ptr) {
// Splat this from x->x+width in two strides
if (x <= -width) {
return; // cutoffleft
}
if (x > 96) {
if (x > OLED_WIDTH) {
return; // cutoff right
}
uint8_t visibleStart = 0;
uint8_t visibleEnd = wide;
uint8_t visibleEnd = width;
// trimming to draw partials
if (x < 0) {
visibleStart -= x; // subtract negative value == add absolute value
}
if (x + wide > 96) {
visibleEnd = 96 - x;
if (x + width > OLED_WIDTH) {
visibleEnd = OLED_WIDTH - x;
}
if (y == 0) {
// Splat first line of data
uint8_t rowsDrawn = 0;
while (height > 0) {
for (uint8_t xx = visibleStart; xx < visibleEnd; xx += 2) {
stripPointers[0][xx + x] = ptr[xx + 1];
stripPointers[0][xx + x + 1] = ptr[xx];
}
}
if (y == 8 || height == 16) {
// Splat the second line
for (uint8_t xx = visibleStart; xx < visibleEnd; xx += 2) {
stripPointers[1][x + xx] = ptr[xx + 1 + (height == 16 ? wide : 0)];
stripPointers[1][x + xx + 1] = ptr[xx + (height == 16 ? wide : 0)];
stripPointers[(y / 8) + rowsDrawn][x + xx] = ptr[xx + 1 + (rowsDrawn * width)];
stripPointers[(y / 8) + rowsDrawn][x + xx + 1] = ptr[xx + (rowsDrawn * width)];
}
height -= 8;
rowsDrawn++;
}
}
@@ -717,7 +735,7 @@ void OLED::fillArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uin
if (x <= -wide) {
return; // cutoffleft
}
if (x > 96) {
if (x > OLED_WIDTH) {
return; // cutoff right
}
@@ -728,63 +746,43 @@ void OLED::fillArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uin
if (x < 0) {
visibleStart -= x; // subtract negative value == add absolute value
}
if (x + wide > 96) {
visibleEnd = 96 - x;
if (x + wide > OLED_WIDTH) {
visibleEnd = OLED_WIDTH - x;
}
if (y == 0) {
// Splat first line of data
uint8_t rowsDrawn = 0;
while (height > 0) {
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
stripPointers[0][xx + x] = value;
}
}
if (y == 8 || height == 16) {
// Splat the second line
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
stripPointers[1][x + xx] = value;
stripPointers[(y / 8) + rowsDrawn][x + xx] = value;
}
height -= 8;
rowsDrawn++;
}
}
void OLED::drawFilledRect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, bool clear) {
//!! LSB is at the top of the screen !!
// Draw this in 3 sections
// This is basically a N wide version of vertical line
uint8_t remainingHeight = y1 - y0;
for (uint8_t currentRow = y0 / 8; (currentRow < (OLED_HEIGHT / 8)) && remainingHeight; currentRow++) {
uint8_t maskTop = (0xFF) << (y0 % 8); // Shift off the mask
y0 = 0; // Blank out any start offset for future iterations
// If we are terminating the bottom of the rectangle in this row, we mask the bottom side of things too
if (remainingHeight <= 8) {
uint8_t maskBottom = ~((0xFF) << y1 % 8); // Create mask for
maskTop = maskTop & maskBottom; // AND the two masks together for final write mask
}
// Step 1 : Draw in the top few pixels that are not /8 aligned
// LSB is at the top of the screen
uint8_t mask = 0xFF;
if (y0) {
mask = mask << (y0 % 8);
for (uint8_t col = x0; col < x1; col++) {
for (uint8_t xpos = x0; xpos < x1; xpos++) {
if (clear) {
stripPointers[0][(y0 / 8) * 96 + col] &= ~mask;
stripPointers[currentRow][xpos] &= ~maskTop;
} else {
stripPointers[0][(y0 / 8) * 96 + col] |= mask;
stripPointers[currentRow][xpos] |= maskTop;
}
}
}
// Next loop down the line the total number of solids
if (y0 / 8 != y1 / 8) {
for (uint8_t col = x0; col < x1; col++) {
for (uint8_t r = (y0 / 8); r < (y1 / 8); r++) {
// This gives us the row index r
if (clear) {
stripPointers[0][(r * 96) + col] = 0;
} else {
stripPointers[0][(r * 96) + col] = 0xFF;
}
}
}
}
// Finally draw the tail
mask = ~(mask << (y1 % 8));
for (uint8_t col = x0; col < x1; col++) {
if (clear) {
stripPointers[0][(y1 / 8) * 96 + col] &= ~mask;
} else {
stripPointers[0][(y1 / 8) * 96 + col] |= mask;
}
remainingHeight -= 8; // Reduce remaining height but the row stripe height
}
}

View File

@@ -136,7 +136,7 @@ public:
static void drawBattery(uint8_t state) { drawSymbol(3 + (state > 10 ? 10 : state)); }
// Draws a checkbox
static void drawCheckbox(bool state) { drawSymbol((state) ? 16 : 17); }
inline static void drawUnavailableIcon() { drawArea(OLED_WIDTH - 16 - 2, 0, 16, 16, UnavailableIcon); }
inline static void drawUnavailableIcon() { drawArea(OLED_WIDTH - OLED_HEIGHT - 2, 0, OLED_HEIGHT, OLED_HEIGHT, UnavailableIcon); }
static void debugNumber(int32_t val, FontStyle fontStyle);
static void drawHex(uint32_t x, FontStyle fontStyle, uint8_t digits);
static void drawSymbol(uint8_t symbolID); // Used for drawing symbols of a predictable width