diff --git a/src/main/cms/cms.c b/src/main/cms/cms.c index ee355b162e..af4590b780 100644 --- a/src/main/cms/cms.c +++ b/src/main/cms/cms.c @@ -720,6 +720,7 @@ void cmsMenuOpen(void) currentCtx = (cmsCtx_t){ &menuMain, 0, 0 }; menuStackIdx = 0; setArmingDisabled(ARMING_DISABLED_CMS_MENU); + displayLayerSelect(pCurrentDisplay, DISPLAYPORT_LAYER_FOREGROUND); // make sure the foreground layer is active } else { // Switch display displayPort_t *pNextDisplay = cmsDisplayPortSelectNext(); diff --git a/src/main/drivers/display.c b/src/main/drivers/display.c index 812897cb8a..5e4b839060 100644 --- a/src/main/drivers/display.c +++ b/src/main/drivers/display.c @@ -115,6 +115,33 @@ uint16_t displayTxBytesFree(const displayPort_t *instance) return instance->vTable->txBytesFree(instance); } +bool displayLayerSupported(displayPort_t *instance, displayPortLayer_e layer) +{ + if (layer == DISPLAYPORT_LAYER_FOREGROUND) { + // Every device must support the foreground (default) layer + return true; + } else if (layer < DISPLAYPORT_LAYER_COUNT && instance->vTable->layerSupported) { + return instance->vTable->layerSupported(instance, layer); + } + return false; +} + +bool displayLayerSelect(displayPort_t *instance, displayPortLayer_e layer) +{ + if (instance->vTable->layerSelect) { + return instance->vTable->layerSelect(instance, layer); + } + return false; +} + +bool displayLayerCopy(displayPort_t *instance, displayPortLayer_e destLayer, displayPortLayer_e sourceLayer) +{ + if (instance->vTable->layerCopy && sourceLayer != destLayer) { + return instance->vTable->layerCopy(instance, destLayer, sourceLayer); + } + return false; +} + void displayInit(displayPort_t *instance, const displayPortVTable_t *vTable) { instance->vTable = vTable; diff --git a/src/main/drivers/display.h b/src/main/drivers/display.h index 86812578a5..c756a97b76 100644 --- a/src/main/drivers/display.h +++ b/src/main/drivers/display.h @@ -20,6 +20,12 @@ #pragma once +typedef enum { + DISPLAYPORT_LAYER_FOREGROUND, + DISPLAYPORT_LAYER_BACKGROUND, + DISPLAYPORT_LAYER_COUNT, +} displayPortLayer_e; + struct displayPortVTable_s; typedef struct displayPort_s { @@ -52,6 +58,9 @@ typedef struct displayPortVTable_s { void (*resync)(displayPort_t *displayPort); bool (*isSynced)(const displayPort_t *displayPort); uint32_t (*txBytesFree)(const displayPort_t *displayPort); + bool (*layerSupported)(displayPort_t *displayPort, displayPortLayer_e layer); + bool (*layerSelect)(displayPort_t *displayPort, displayPortLayer_e layer); + bool (*layerCopy)(displayPort_t *displayPort, displayPortLayer_e destLayer, displayPortLayer_e sourceLayer); } displayPortVTable_t; typedef struct displayPortProfile_s { @@ -81,3 +90,7 @@ void displayResync(displayPort_t *instance); bool displayIsSynced(const displayPort_t *instance); uint16_t displayTxBytesFree(const displayPort_t *instance); void displayInit(displayPort_t *instance, const displayPortVTable_t *vTable); +bool displayLayerSupported(displayPort_t *instance, displayPortLayer_e layer); +bool displayLayerSelect(displayPort_t *instance, displayPortLayer_e layer); +bool displayLayerCopy(displayPort_t *instance, displayPortLayer_e destLayer, displayPortLayer_e sourceLayer); + diff --git a/src/main/drivers/max7456.c b/src/main/drivers/max7456.c index 8dcfbb6980..1bd4bb39c1 100644 --- a/src/main/drivers/max7456.c +++ b/src/main/drivers/max7456.c @@ -181,6 +181,15 @@ #define __spiBusTransactionEnd(busdev) {IOHi((busdev)->busdev_u.spi.csnPin);spiSetDivisor((busdev)->busdev_u.spi.instance, MAX7456_RESTORE_CLK);} #endif +#define MAX7456_SUPPORTED_LAYER_COUNT (DISPLAYPORT_LAYER_BACKGROUND + 1) + +typedef struct max7456Layer_s { + uint8_t buffer[VIDEO_BUFFER_CHARS_PAL]; +} max7456Layer_t; + +static max7456Layer_t displayLayers[MAX7456_SUPPORTED_LAYER_COUNT]; +static displayPortLayer_e activeLayer = DISPLAYPORT_LAYER_FOREGROUND; + busDevice_t max7456BusDevice; busDevice_t *busdev = &max7456BusDevice; @@ -188,11 +197,10 @@ static uint16_t max7456SpiClock = MAX7456_SPI_CLK; uint16_t maxScreenSize = VIDEO_BUFFER_CHARS_PAL; -// We write everything in screenBuffer and then compare -// screenBuffer with shadowBuffer to upgrade only changed chars. +// We write everything to the active layer and then compare +// it with shadowBuffer to update only changed chars. // This solution is faster then redrawing entire screen. -static uint8_t screenBuffer[VIDEO_BUFFER_CHARS_PAL+40]; // For faster writes we use memcpy so we need some space to don't overwrite buffer static uint8_t shadowBuffer[VIDEO_BUFFER_CHARS_PAL]; //Max chars to update in one idle @@ -222,6 +230,16 @@ static uint8_t previousInvertRegister = INVALID_PREVIOUS_REGISTER_STATE; static void max7456DrawScreenSlow(void); +static uint8_t *getLayerBuffer(displayPortLayer_e layer) +{ + return displayLayers[layer].buffer; +} + +static uint8_t *getActiveLayerBuffer(void) +{ + return getLayerBuffer(activeLayer); +} + static uint8_t max7456Send(uint8_t add, uint8_t data) { spiTransferByte(busdev->busdev_u.spi.instance, add); @@ -349,11 +367,21 @@ uint8_t max7456GetRowsCount(void) return (videoSignalReg & VIDEO_MODE_PAL) ? VIDEO_LINES_PAL : VIDEO_LINES_NTSC; } +// When clearing the shadow buffer we fill with 0 so that the characters will +// be flagged as changed when compared to the 0x20 used in the layer buffers. static void max7456ClearShadowBuffer(void) { memset(shadowBuffer, 0, maxScreenSize); } +// Buffer is filled with the whitespace character (0x20) +static void max7456ClearLayer(displayPortLayer_e layer) +{ + memset(getLayerBuffer(layer), 0x20, VIDEO_BUFFER_CHARS_PAL); +} + + + void max7456ReInit(void) { uint8_t srdata = 0; @@ -422,6 +450,11 @@ void max7456PreInit(const max7456Config_t *max7456Config) bool max7456Init(const max7456Config_t *max7456Config, const vcdProfile_t *pVcdProfile, bool cpuOverclock) { + // initialize all layers + for (unsigned i = 0; i < MAX7456_SUPPORTED_LAYER_COUNT; i++) { + max7456ClearLayer(i); + } + max7456HardwareReset(); if (!max7456Config->csTag || !max7456Config->spiDevice) { @@ -568,33 +601,58 @@ void max7456Brightness(uint8_t black, uint8_t white) } } -//just fill with spaces with some tricks void max7456ClearScreen(void) { - memset(screenBuffer, 0x20, VIDEO_BUFFER_CHARS_PAL); -} - -uint8_t* max7456GetScreenBuffer(void) -{ - return screenBuffer; + max7456ClearLayer(activeLayer); } void max7456WriteChar(uint8_t x, uint8_t y, uint8_t c) { + uint8_t *buffer = getActiveLayerBuffer(); if (x < CHARS_PER_LINE && y < VIDEO_LINES_PAL) { - screenBuffer[y * CHARS_PER_LINE + x] = c; + buffer[y * CHARS_PER_LINE + x] = c; } } void max7456Write(uint8_t x, uint8_t y, const char *buff) { if (y < VIDEO_LINES_PAL) { + uint8_t *buffer = getActiveLayerBuffer(); for (int i = 0; buff[i] && x + i < CHARS_PER_LINE; i++) { - screenBuffer[y * CHARS_PER_LINE + x + i] = buff[i]; + buffer[y * CHARS_PER_LINE + x + i] = buff[i]; } } } +bool max7456LayerSupported(displayPortLayer_e layer) +{ + if (layer == DISPLAYPORT_LAYER_FOREGROUND || layer == DISPLAYPORT_LAYER_BACKGROUND) { + return true; + } else { + return false; + } +} + +bool max7456LayerSelect(displayPortLayer_e layer) +{ + if (max7456LayerSupported(layer)) { + activeLayer = layer; + return true; + } else { + return false; + } +} + +bool max7456LayerCopy(displayPortLayer_e destLayer, displayPortLayer_e sourceLayer) +{ + if ((sourceLayer != destLayer) && max7456LayerSupported(sourceLayer) && max7456LayerSupported(destLayer)) { + memcpy(getLayerBuffer(destLayer), getLayerBuffer(sourceLayer), VIDEO_BUFFER_CHARS_PAL); + return true; + } else { + return false; + } +} + bool max7456DmaInProgress(void) { #ifdef MAX7456_DMA_CHANNEL_TX @@ -607,7 +665,7 @@ bool max7456DmaInProgress(void) bool max7456BuffersSynced(void) { for (int i = 0; i < maxScreenSize; i++) { - if (screenBuffer[i] != shadowBuffer[i]) { + if (displayLayers[DISPLAYPORT_LAYER_FOREGROUND].buffer[i] != shadowBuffer[i]) { return false; } } @@ -679,16 +737,18 @@ void max7456DrawScreen(void) max7456ReInitIfRequired(false); + uint8_t *buffer = getActiveLayerBuffer(); + int buff_len = 0; for (int k = 0; k < MAX_CHARS2UPDATE; k++) { - if (screenBuffer[pos] != shadowBuffer[pos]) { + if (buffer[pos] != shadowBuffer[pos]) { spiBuff[buff_len++] = MAX7456ADD_DMAH; spiBuff[buff_len++] = pos >> 8; spiBuff[buff_len++] = MAX7456ADD_DMAL; spiBuff[buff_len++] = pos & 0xff; spiBuff[buff_len++] = MAX7456ADD_DMDI; - spiBuff[buff_len++] = screenBuffer[pos]; - shadowBuffer[pos] = screenBuffer[pos]; + spiBuff[buff_len++] = buffer[pos]; + shadowBuffer[pos] = buffer[pos]; } if (++pos >= maxScreenSize) { @@ -712,23 +772,24 @@ void max7456DrawScreen(void) static void max7456DrawScreenSlow(void) { bool escapeCharFound; + uint8_t *buffer = getActiveLayerBuffer(); __spiBusTransactionBegin(busdev); - // Enable auto-increment mode and update every character in the screenBuffer. + // Enable auto-increment mode and update every character in the active buffer. // The "escape" character 0xFF must be skipped as it causes the MAX7456 to exit auto-increment mode. max7456Send(MAX7456ADD_DMAH, 0); max7456Send(MAX7456ADD_DMAL, 0); max7456Send(MAX7456ADD_DMM, displayMemoryModeReg | 1); for (int xx = 0; xx < maxScreenSize; xx++) { - if (screenBuffer[xx] == END_STRING) { + if (buffer[xx] == END_STRING) { escapeCharFound = true; max7456Send(MAX7456ADD_DMDI, ' '); // replace the 0xFF character with a blank in the first pass to avoid terminating auto-increment } else { - max7456Send(MAX7456ADD_DMDI, screenBuffer[xx]); + max7456Send(MAX7456ADD_DMDI, buffer[xx]); } - shadowBuffer[xx] = screenBuffer[xx]; + shadowBuffer[xx] = buffer[xx]; } max7456Send(MAX7456ADD_DMDI, END_STRING); @@ -738,7 +799,7 @@ static void max7456DrawScreenSlow(void) // to update them with direct addressing if (escapeCharFound) { for (int xx = 0; xx < maxScreenSize; xx++) { - if (screenBuffer[xx] == END_STRING) { + if (buffer[xx] == END_STRING) { max7456Send(MAX7456ADD_DMAH, xx >> 8); max7456Send(MAX7456ADD_DMAL, xx & 0xFF); max7456Send(MAX7456ADD_DMDI, END_STRING); diff --git a/src/main/drivers/max7456.h b/src/main/drivers/max7456.h index fb1fe69ec9..d1fc867468 100644 --- a/src/main/drivers/max7456.h +++ b/src/main/drivers/max7456.h @@ -20,6 +20,8 @@ #pragma once +#include "drivers/display.h" + /** PAL or NTSC, value is number of chars total */ #define VIDEO_BUFFER_CHARS_NTSC 390 #define VIDEO_BUFFER_CHARS_PAL 480 @@ -27,7 +29,6 @@ #define VIDEO_LINES_PAL 16 extern uint16_t maxScreenSize; - struct vcdProfile_s; void max7456HardwareReset(void); struct max7456Config_s; @@ -42,6 +43,8 @@ void max7456Write(uint8_t x, uint8_t y, const char *buff); void max7456WriteChar(uint8_t x, uint8_t y, uint8_t c); void max7456ClearScreen(void); void max7456RefreshAll(void); -uint8_t* max7456GetScreenBuffer(void); bool max7456DmaInProgress(void); bool max7456BuffersSynced(void); +bool max7456LayerSupported(displayPortLayer_e layer); +bool max7456LayerSelect(displayPortLayer_e layer); +bool max7456LayerCopy(displayPortLayer_e destLayer, displayPortLayer_e sourceLayer); diff --git a/src/main/io/displayport_crsf.c b/src/main/io/displayport_crsf.c index 8276bfc77e..7f5e3261cf 100644 --- a/src/main/io/displayport_crsf.c +++ b/src/main/io/displayport_crsf.c @@ -138,7 +138,10 @@ static const displayPortVTable_t crsfDisplayPortVTable = { .heartbeat = crsfHeartbeat, .resync = crsfResync, .isSynced = crsfIsSynced, - .txBytesFree = crsfTxBytesFree + .txBytesFree = crsfTxBytesFree, + .layerSupported = NULL, + .layerSelect = NULL, + .layerCopy = NULL, }; crsfDisplayPortScreen_t *crsfDisplayPortScreen(void) diff --git a/src/main/io/displayport_hott.c b/src/main/io/displayport_hott.c index ace3652600..81dd7d14fa 100644 --- a/src/main/io/displayport_hott.c +++ b/src/main/io/displayport_hott.c @@ -119,7 +119,10 @@ static const displayPortVTable_t hottVTable = { .isTransferInProgress = hottIsTransferInProgress, .heartbeat = hottHeartbeat, .resync = hottResync, - .txBytesFree = hottTxBytesFree + .txBytesFree = hottTxBytesFree, + .layerSupported = NULL, + .layerSelect = NULL, + .layerCopy = NULL, }; displayPort_t *displayPortHottInit() diff --git a/src/main/io/displayport_max7456.c b/src/main/io/displayport_max7456.c index 59e26ceaf5..21c8b60dc2 100644 --- a/src/main/io/displayport_max7456.c +++ b/src/main/io/displayport_max7456.c @@ -148,6 +148,24 @@ static uint32_t txBytesFree(const displayPort_t *displayPort) return UINT32_MAX; } +static bool layerSupported(displayPort_t *displayPort, displayPortLayer_e layer) +{ + UNUSED(displayPort); + return max7456LayerSupported(layer); +} + +static bool layerSelect(displayPort_t *displayPort, displayPortLayer_e layer) +{ + UNUSED(displayPort); + return max7456LayerSelect(layer); +} + +static bool layerCopy(displayPort_t *displayPort, displayPortLayer_e destLayer, displayPortLayer_e sourceLayer) +{ + UNUSED(displayPort); + return max7456LayerCopy(destLayer, sourceLayer); +} + static const displayPortVTable_t max7456VTable = { .grab = grab, .release = release, @@ -161,6 +179,9 @@ static const displayPortVTable_t max7456VTable = { .resync = resync, .isSynced = isSynced, .txBytesFree = txBytesFree, + .layerSupported = layerSupported, + .layerSelect = layerSelect, + .layerCopy = layerCopy, }; displayPort_t *max7456DisplayPortInit(const vcdProfile_t *vcdProfile) diff --git a/src/main/io/displayport_msp.c b/src/main/io/displayport_msp.c index 003a30304e..349bf56a2a 100644 --- a/src/main/io/displayport_msp.c +++ b/src/main/io/displayport_msp.c @@ -167,7 +167,10 @@ static const displayPortVTable_t mspDisplayPortVTable = { .heartbeat = heartbeat, .resync = resync, .isSynced = isSynced, - .txBytesFree = txBytesFree + .txBytesFree = txBytesFree, + .layerSupported = NULL, + .layerSelect = NULL, + .layerCopy = NULL, }; displayPort_t *displayPortMspInit(void) diff --git a/src/main/io/displayport_oled.c b/src/main/io/displayport_oled.c index e2b6f85f92..9ca006fa1a 100644 --- a/src/main/io/displayport_oled.c +++ b/src/main/io/displayport_oled.c @@ -114,7 +114,10 @@ static const displayPortVTable_t oledVTable = { .heartbeat = oledHeartbeat, .resync = oledResync, .isSynced = oledIsSynced, - .txBytesFree = oledTxBytesFree + .txBytesFree = oledTxBytesFree, + .layerSupported = NULL, + .layerSelect = NULL, + .layerCopy = NULL, }; displayPort_t *displayPortOledInit(void *device) diff --git a/src/main/io/displayport_srxl.c b/src/main/io/displayport_srxl.c index b7d41686d7..573e991680 100644 --- a/src/main/io/displayport_srxl.c +++ b/src/main/io/displayport_srxl.c @@ -130,7 +130,10 @@ static const displayPortVTable_t srxlVTable = { .heartbeat = srxlHeartbeat, .resync = srxlResync, .isSynced = srxlIsSynced, - .txBytesFree = srxlTxBytesFree + .txBytesFree = srxlTxBytesFree, + .layerSupported = NULL, + .layerSelect = NULL, + .layerCopy = NULL, }; displayPort_t *displayPortSrxlInit() diff --git a/src/main/osd/osd.c b/src/main/osd/osd.c index e3a4f57a3f..c204112109 100644 --- a/src/main/osd/osd.c +++ b/src/main/osd/osd.c @@ -124,6 +124,8 @@ static displayPort_t *osdDisplayPort; static bool suppressStatsDisplay = false; static uint8_t osdStatsRowCount = 0; +static bool backgroundLayerSupported = false; + #ifdef USE_ESC_SENSOR escSensorData_t *osdEscDataCombined; #endif @@ -225,15 +227,30 @@ void changeOsdProfileIndex(uint8_t profileIndex) } #endif +void osdAnalyzeActiveElements(void) +{ + osdAddActiveElements(); + osdDrawActiveElementsBackground(osdDisplayPort); +} + static void osdDrawElements(timeUs_t currentTimeUs) { - displayClearScreen(osdDisplayPort); - // Hide OSD when OSDSW mode is active if (IS_RC_MODE_ACTIVE(BOXOSD)) { + displayClearScreen(osdDisplayPort); return; } + if (backgroundLayerSupported) { + // Background layer is supported, overlay it onto the foreground + // so that we only need to draw the active parts of the elements. + displayLayerCopy(osdDisplayPort, DISPLAYPORT_LAYER_FOREGROUND, DISPLAYPORT_LAYER_BACKGROUND); + } else { + // Background layer not supported, just clear the foreground in preparation + // for drawing the elements including their backgrounds. + displayClearScreen(osdDisplayPort); + } + osdDrawActiveElements(osdDisplayPort, currentTimeUs); } @@ -342,6 +359,9 @@ void osdInit(displayPort_t *osdDisplayPortToUse) cmsDisplayPortRegister(osdDisplayPort); #endif + backgroundLayerSupported = displayLayerSupported(osdDisplayPort, DISPLAYPORT_LAYER_BACKGROUND); + displayLayerSelect(osdDisplayPort, DISPLAYPORT_LAYER_FOREGROUND); + armState = ARMING_FLAG(ARMED); osdResetAlarms(); @@ -372,6 +392,8 @@ void osdInit(displayPort_t *osdDisplayPortToUse) #ifdef USE_OSD_PROFILES setOsdProfile(osdConfig()->osdProfileIndex); #endif + + osdElementsInit(backgroundLayerSupported); osdAnalyzeActiveElements(); } @@ -921,7 +943,6 @@ void osdUpdate(timeUs_t currentTimeUs) #else #define DRAW_FREQ_DENOM 10 // MWOSD @ 115200 baud ( #endif -#define STATS_FREQ_DENOM 50 if (counter % DRAW_FREQ_DENOM == 0) { osdRefresh(currentTimeUs); diff --git a/src/main/osd/osd.h b/src/main/osd/osd.h index 05066aad68..88a2dfc6b4 100644 --- a/src/main/osd/osd.h +++ b/src/main/osd/osd.h @@ -311,6 +311,7 @@ void osdWarnSetState(uint8_t warningIndex, bool enabled); bool osdWarnGetState(uint8_t warningIndex); void osdSuppressStats(bool flag); +void osdAnalyzeActiveElements(void); uint8_t getCurrentOsdProfileIndex(void); void changeOsdProfileIndex(uint8_t profileIndex); bool osdElementVisible(uint16_t value); diff --git a/src/main/osd/osd_elements.c b/src/main/osd/osd_elements.c index 9e43bd6cd4..dadfd03460 100644 --- a/src/main/osd/osd_elements.c +++ b/src/main/osd/osd_elements.c @@ -28,14 +28,31 @@ Next add the element to the osdElementDisplayOrder array defined in this file. If the element needs special runtime conditional processing then it should be added - to the osdAnalyzeActiveElements() function instead. + to the osdAddActiveElements() function instead. - Create the function to "draw" the element. It should be named like "osdElementSomething()" - where the "Something" describes the element. + Create the function to "draw" the element. + ------------------------------------------ + It should be named like "osdElementSomething()" where the "Something" describes + the element. The drawing function should only render the dynamic portions of the + element. If the element has static (unchanging) portions then those should be + rendered in the background function. The exception to this is elements that are + expected to blink (have a warning associated). In this case the entire element + must be handled in the main draw function and you can't use the background capability. Add the mapping from the element ID added in the first step to the function created in the third step to the osdElementDrawFunction array. + Create the function to draw the element's static (background) portion. + --------------------------------------------------------------------- + If an element has static (unchanging) portions then create a function to draw only those + parts. It should be named like "osdBackgroundSomething()" where the "Something" matches + the related element function. + + Add the mapping for the element ID to the background drawing function to the + osdElementBackgroundFunction array. + + Accelerometer reqirement: + ------------------------- If the new element utilizes the accelerometer, add it to the osdElementsNeedAccelerometer() function. Finally add a CLI parameter for the new element in cli/settings.c. @@ -152,6 +169,7 @@ static const char compassBar[] = { static unsigned activeOsdElementCount = 0; static uint8_t activeOsdElementArray[OSD_ITEM_COUNT]; +static bool backgroundLayerSupported = false; // Blink control static bool blinkState = true; @@ -578,7 +596,7 @@ static void osdElementCoreTemperature(osdElementParms_t *element) } #endif // USE_ADC_INTERNAL -static void osdElementCraftName(osdElementParms_t *element) +static void osdBackgroundCraftName(osdElementParms_t *element) { if (strlen(pilotConfig()->name) == 0) { strcpy(element->buff, "CRAFT_NAME"); @@ -639,7 +657,7 @@ static void osdElementCrashFlipArrow(osdElementParms_t *element) } #endif // USE_ACC -static void osdElementCrosshairs(osdElementParms_t *element) +static void osdBackgroundCrosshairs(osdElementParms_t *element) { element->buff[0] = SYM_AH_CENTER_LINE; element->buff[1] = SYM_AH_CENTER; @@ -665,7 +683,7 @@ static void osdElementDisarmed(osdElementParms_t *element) } } -static void osdElementDisplayName(osdElementParms_t *element) +static void osdBackgroundDisplayName(osdElementParms_t *element) { if (strlen(pilotConfig()->displayName) == 0) { strcpy(element->buff, "DISPLAY_NAME"); @@ -862,7 +880,7 @@ static void osdElementGpsSpeed(osdElementParms_t *element) } #endif // USE_GPS -static void osdElementHorizonSidebars(osdElementParms_t *element) +static void osdBackgroundHorizonSidebars(osdElementParms_t *element) { // Draw AH sides const int8_t hudwidth = AH_SIDEBAR_WIDTH_POS; @@ -1078,7 +1096,7 @@ static void osdElementRssiDbm(osdElementParms_t *element) #endif // USE_RX_RSSI_DBM #ifdef USE_OSD_STICK_OVERLAY -static void osdElementStickOverlay(osdElementParms_t *element) +static void osdBackgroundStickOverlay(osdElementParms_t *element) { const uint8_t xpos = element->elemPosX; const uint8_t ypos = element->elemPosY; @@ -1097,6 +1115,14 @@ static void osdElementStickOverlay(osdElementParms_t *element) } } + element->drawElement = false; // element already drawn +} + +static void osdElementStickOverlay(osdElementParms_t *element) +{ + const uint8_t xpos = element->elemPosX; + const uint8_t ypos = element->elemPosY; + // Now draw the cursor rc_alias_e vertical_channel, horizontal_channel; @@ -1410,7 +1436,7 @@ static void osdElementWarnings(osdElementParms_t *element) // Elements positioned later in the list will overlay the earlier // ones if their character positions overlap // Elements that need special runtime conditional processing should be added -// to osdAnalyzeActiveElements() +// to osdAddActiveElements() static const uint8_t osdElementDisplayOrder[] = { OSD_MAIN_BATT_VOLTAGE, @@ -1487,15 +1513,15 @@ static const uint8_t osdElementDisplayOrder[] = { const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = { [OSD_RSSI_VALUE] = osdElementRssi, [OSD_MAIN_BATT_VOLTAGE] = osdElementMainBatteryVoltage, - [OSD_CROSSHAIRS] = osdElementCrosshairs, + [OSD_CROSSHAIRS] = NULL, // only has background #ifdef USE_ACC [OSD_ARTIFICIAL_HORIZON] = osdElementArtificialHorizon, #endif - [OSD_HORIZON_SIDEBARS] = osdElementHorizonSidebars, + [OSD_HORIZON_SIDEBARS] = NULL, // only has background [OSD_ITEM_TIMER_1] = osdElementTimer, [OSD_ITEM_TIMER_2] = osdElementTimer, [OSD_FLYMODE] = osdElementFlymode, - [OSD_CRAFT_NAME] = osdElementCraftName, + [OSD_CRAFT_NAME] = NULL, // only has background [OSD_THROTTLE_POS] = osdElementThrottlePosition, #ifdef USE_VTX_COMMON [OSD_VTX_CHANNEL] = osdElementVtxChannel, @@ -1571,7 +1597,7 @@ const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = { [OSD_STICK_OVERLAY_LEFT] = osdElementStickOverlay, [OSD_STICK_OVERLAY_RIGHT] = osdElementStickOverlay, #endif - [OSD_DISPLAY_NAME] = osdElementDisplayName, + [OSD_DISPLAY_NAME] = NULL, // only has background #if defined(USE_DSHOT_TELEMETRY) || defined(USE_ESC_SENSOR) [OSD_ESC_RPM_FREQ] = osdElementEscRpmFreq, #endif @@ -1588,6 +1614,20 @@ const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = { [OSD_RC_CHANNELS] = osdElementRcChannels, }; +// Define the mapping between the OSD element id and the function to draw its background (static part) +// Only necessary to define the entries that actually have a background function + +const osdElementDrawFn osdElementBackgroundFunction[OSD_ITEM_COUNT] = { + [OSD_CROSSHAIRS] = osdBackgroundCrosshairs, + [OSD_HORIZON_SIDEBARS] = osdBackgroundHorizonSidebars, + [OSD_CRAFT_NAME] = osdBackgroundCraftName, +#ifdef USE_OSD_STICK_OVERLAY + [OSD_STICK_OVERLAY_LEFT] = osdBackgroundStickOverlay, + [OSD_STICK_OVERLAY_RIGHT] = osdBackgroundStickOverlay, +#endif + [OSD_DISPLAY_NAME] = osdBackgroundDisplayName, +}; + static void osdAddActiveElement(osd_items_e element) { if (VISIBLE(osdConfig()->item_pos[element])) { @@ -1598,7 +1638,7 @@ static void osdAddActiveElement(osd_items_e element) // Examine the elements and build a list of only the active (enabled) // ones to speed up rendering. -void osdAnalyzeActiveElements(void) +void osdAddActiveElements(void) { activeOsdElementCount = 0; @@ -1638,10 +1678,14 @@ void osdAnalyzeActiveElements(void) #endif } -static bool osdDrawSingleElement(displayPort_t *osdDisplayPort, uint8_t item) +static void osdDrawSingleElement(displayPort_t *osdDisplayPort, uint8_t item) { + if (!osdElementDrawFunction[item]) { + // Element has no drawing function + return; + } if (BLINK(item)) { - return false; + return; } uint8_t elemPosX = OSD_X(osdConfig()->item_pos[item]); @@ -1661,8 +1705,32 @@ static bool osdDrawSingleElement(displayPort_t *osdDisplayPort, uint8_t item) if (element.drawElement) { displayWrite(osdDisplayPort, elemPosX, elemPosY, buff); } +} - return true; +static void osdDrawSingleElementBackground(displayPort_t *osdDisplayPort, uint8_t item) +{ + if (!osdElementBackgroundFunction[item]) { + // Element has no background drawing function + return; + } + + uint8_t elemPosX = OSD_X(osdConfig()->item_pos[item]); + uint8_t elemPosY = OSD_Y(osdConfig()->item_pos[item]); + char buff[OSD_ELEMENT_BUFFER_LENGTH] = ""; + + osdElementParms_t element; + element.item = item; + element.elemPosX = elemPosX; + element.elemPosY = elemPosY; + element.buff = (char *)&buff; + element.osdDisplayPort = osdDisplayPort; + element.drawElement = true; + + // Call the element background drawing function + osdElementBackgroundFunction[item](&element); + if (element.drawElement) { + displayWrite(osdDisplayPort, elemPosX, elemPosY, buff); + } } void osdDrawActiveElements(displayPort_t *osdDisplayPort, timeUs_t currentTimeUs) @@ -1681,10 +1749,33 @@ void osdDrawActiveElements(displayPort_t *osdDisplayPort, timeUs_t currentTimeUs blinkState = (currentTimeUs / 200000) % 2; for (unsigned i = 0; i < activeOsdElementCount; i++) { + if (!backgroundLayerSupported) { + // If the background layer isn't supported then we + // have to draw the element's static layer as well. + osdDrawSingleElementBackground(osdDisplayPort, activeOsdElementArray[i]); + } osdDrawSingleElement(osdDisplayPort, activeOsdElementArray[i]); } } +void osdDrawActiveElementsBackground(displayPort_t *osdDisplayPort) +{ + if (backgroundLayerSupported) { + displayLayerSelect(osdDisplayPort, DISPLAYPORT_LAYER_BACKGROUND); + displayClearScreen(osdDisplayPort); + for (unsigned i = 0; i < activeOsdElementCount; i++) { + osdDrawSingleElementBackground(osdDisplayPort, activeOsdElementArray[i]); + } + displayLayerSelect(osdDisplayPort, DISPLAYPORT_LAYER_FOREGROUND); + } +} + +void osdElementsInit(bool backgroundLayerFlag) +{ + backgroundLayerSupported = backgroundLayerFlag; + activeOsdElementCount = 0; +} + void osdResetAlarms(void) { memset(blinkBits, 0, sizeof(blinkBits)); diff --git a/src/main/osd/osd_elements.h b/src/main/osd/osd_elements.h index ffcfc374fe..251ed80d96 100644 --- a/src/main/osd/osd_elements.h +++ b/src/main/osd/osd_elements.h @@ -45,8 +45,10 @@ char osdGetMetersToSelectedUnitSymbol(void); int32_t osdGetSpeedToSelectedUnit(int32_t value); char osdGetSpeedToSelectedUnitSymbol(void); char osdGetTemperatureSymbolForSelectedUnit(void); -void osdAnalyzeActiveElements(void); +void osdAddActiveElements(void); void osdDrawActiveElements(displayPort_t *osdDisplayPort, timeUs_t currentTimeUs); +void osdDrawActiveElementsBackground(displayPort_t *osdDisplayPort); +void osdElementsInit(bool backgroundLayerFlag); void osdResetAlarms(void); void osdUpdateAlarms(void); bool osdElementsNeedAccelerometer(void);