diff --git a/companion/src/customizesplashdialog.cpp b/companion/src/customizesplashdialog.cpp index 59b5e1de4..79e18828f 100644 --- a/companion/src/customizesplashdialog.cpp +++ b/companion/src/customizesplashdialog.cpp @@ -28,10 +28,14 @@ //*** Side Class *** -Side::Side(){ +Side::Side(QDialog* parent): + parent(parent) +{ imageLabel = 0; fileNameEdit = 0; saveButton = 0; + invertButton = 0; + libraryButton = 0; loadFwButton=0; loadPictButton = 0; loadProfileButton = 0; @@ -62,7 +66,7 @@ bool Side::displayImage(QString fileName, Source pictSource) // Determine which picture format to use if (pictSource == FW) { - FirmwareInterface firmware(fileName); + FirmwareInterface firmware(fileName, parent); if (!firmware.hasSplash()) return false; else @@ -120,7 +124,7 @@ bool Side::saveImage() { if (*source == FW ) { - FirmwareInterface firmware(*saveToFileName); + FirmwareInterface firmware(*saveToFileName, parent); if (!firmware.hasSplash()) { return false; } @@ -152,6 +156,8 @@ bool Side::saveImage() CustomizeSplashDialog::CustomizeSplashDialog(QWidget *parent) : QDialog(parent), + left(this), + right(this), ui(new Ui::CustomizeSplashDialog) { ui->setupUi(this); diff --git a/companion/src/customizesplashdialog.h b/companion/src/customizesplashdialog.h index 76419d9f8..2955929ec 100644 --- a/companion/src/customizesplashdialog.h +++ b/companion/src/customizesplashdialog.h @@ -40,7 +40,7 @@ enum LCDFormat {LCD9X, LCDTARANIS}; class Side { public: - Side(); + Side(QDialog* parent); void markSourceButton(); void copyImage( Side ); bool displayImage( QString fileName, Source source ); @@ -59,6 +59,7 @@ public: QString *saveToFileName; private: + QDialog* parent; Source *source; LCDFormat *format; }; diff --git a/companion/src/storage/firmwareinterface.cpp b/companion/src/storage/firmwareinterface.cpp index a07ebbd67..4357928d1 100644 --- a/companion/src/storage/firmwareinterface.cpp +++ b/companion/src/storage/firmwareinterface.cpp @@ -25,6 +25,8 @@ #include "storage.h" #include +#include +#include #define FW_MARK "FW" #define VERS_MARK "VERS" @@ -33,7 +35,131 @@ #define EEPR_MARK "EEPR" #define FSIZE_MAX Boards::getFlashSize(Board::BOARD_UNKNOWN) -FirmwareInterface::FirmwareInterface(const QString & filename): +class RleBitmap +{ +public: + RleBitmap(const uint8_t *src, size_t offset) : + state(RLE_FIRST_BYTE), src(src), curPtr(src), byte(0), curCount(0), pos(0) + { + width = *curPtr++; + rawRows = *curPtr++; + rows = (rawRows +1)/2; + skip(offset); + } + + void skip(size_t count) + { + while(count) + { + count--; + getNext(); + } + } + + uint8_t getNext() + { + pos++; + switch(state) + { + case RLE_FIRST_BYTE: + byte = *curPtr++; + if(byte == *curPtr) + state = RLE_SECOND_BYTE; + break; + case RLE_SECOND_BYTE: + byte = *curPtr++; + curCount = (*curPtr++)+1; + state = RLE_CONTINUE; + // fall through + case RLE_CONTINUE: + curCount--; + if(!curCount) + state = RLE_FIRST_BYTE; + break; + } + return byte; + } + + bool to(uint8_t* buf, size_t size) + { + while(pos RLE_SPLASH_MAX_SIZE){ + if(parentDialog) + QMessageBox::critical(parentDialog, CPN_STR_TTL_ERROR, parentDialog->tr("Compressed image size exceeds reserved space.")); + return false; + } + } } else { QColor black = QColor(0,0,0); @@ -268,7 +403,7 @@ bool FirmwareInterface::setSplash(const QImage & newsplash) } } splash.clear(); - splash.append(b, splashSize); + splash.append((char *)b, splashSize); flash.replace(splashOffset, splashSize, splash); return true; } @@ -288,6 +423,7 @@ QImage::Format FirmwareInterface::getSplashFormat() return splash_format; } + QImage FirmwareInterface::getSplash() { if (splashOffset == 0 || splashSize == 0) { @@ -296,15 +432,35 @@ QImage FirmwareInterface::getSplash() if (splash_format == QImage::Format_Indexed8) { QImage image(splashWidth, splashHeight, QImage::Format_RGB888); - if (splashOffset > 0) { - for (unsigned int y=0; y> 4) : (byte & 0x0F); - z = 255-(z*255)/15; - QRgb rgb = qRgb(z, z, z); - image.setPixel(x, y, rgb); + if(splashWidth == SPLASHX9D_WIDTH && splashHeight == SPLASHX9D_HEIGHT) + { + std::vector data; + data.resize(splashWidth * splashHeight); + RleBitmap img((uint8_t*)flash.data() + splashOffset - 2, 0); + img.to(&data[0], data.size()); + if (splashOffset > 0) { + for (unsigned int y=0; y> 4) : (byte & 0x0F); + z = 255-(z*255)/15; + QRgb rgb = qRgb(z, z, z); + image.setPixel(x, y, rgb); + } + } + } + } else { + if (splashOffset > 0) { + for (unsigned int y=0; y> 4) : (byte & 0x0F); + z = 255-(z*255)/15; + QRgb rgb = qRgb(z, z, z); + image.setPixel(x, y, rgb); + } } } } diff --git a/companion/src/storage/firmwareinterface.h b/companion/src/storage/firmwareinterface.h index d6dee286b..f997752a4 100644 --- a/companion/src/storage/firmwareinterface.h +++ b/companion/src/storage/firmwareinterface.h @@ -36,10 +36,13 @@ #define ERSKY9X_SPE "SPE" #define ERSKY9X_OFFSET (7) +// 3kB - width and height bytes +#define RLE_SPLASH_MAX_SIZE (3070) + class FirmwareInterface { public: - FirmwareInterface(const QString & filename); + FirmwareInterface(const QString & filename, QDialog* parentDialog = nullptr); inline QString getDate() { return date; } inline QString getTime() { return time; } int getSize() { return flashSize; } @@ -59,6 +62,7 @@ class FirmwareInterface bool isValid(); private: + QDialog* parentDialog; QByteArray flash; uint flashSize; QString seekString(const QString & string); diff --git a/radio/src/bitmaps/212x64/CMakeLists.txt b/radio/src/bitmaps/212x64/CMakeLists.txt index 11584d0f8..66427fcf9 100644 --- a/radio/src/bitmaps/212x64/CMakeLists.txt +++ b/radio/src/bitmaps/212x64/CMakeLists.txt @@ -6,5 +6,5 @@ set(STICKS_ARGS --rows 4) add_bitmaps_target(taranis_xbm ${RADIO_SRC_DIR}/bitmaps/sticks.xbm 1bit "${STICKS_ARGS}") # 1bit format, 1byte size, split into 4 rows add_fonts_target(taranis_fonts ${RADIO_SRC_DIR}/fonts/${FONT_DIR}/*.png) -add_bitmaps_target(taranis_bitmaps ${RADIO_SRC_DIR}/bitmaps/212x64/*.png 4bits "") +add_bitmaps_target(taranis_bitmaps ${RADIO_SRC_DIR}/bitmaps/212x64/*.png 4bits "--rle") add_dependencies(taranis_bitmaps taranis_fonts taranis_xbm) diff --git a/radio/src/gui/212x64/bmp.cpp b/radio/src/gui/212x64/bmp.cpp index 8cae121ff..4cf78d782 100644 --- a/radio/src/gui/212x64/bmp.cpp +++ b/radio/src/gui/212x64/bmp.cpp @@ -204,6 +204,10 @@ bool loadModelBitmap(char * name, uint8_t * bitmap) } // In all error cases, we set the default logo - memcpy(bitmap, logo_taranis, MODEL_BITMAP_SIZE); + RleBitmap pic(logo_taranis, 0); + *bitmap++ = pic.getWidth(); + *bitmap++ = pic.getRawRows(); + for(int i=0; i < MODEL_BITMAP_SIZE-2; i++) + *bitmap++ = pic.getNext(); return false; } diff --git a/radio/src/gui/212x64/lcd.cpp b/radio/src/gui/212x64/lcd.cpp index 7da7583a8..e49c01050 100644 --- a/radio/src/gui/212x64/lcd.cpp +++ b/radio/src/gui/212x64/lcd.cpp @@ -976,4 +976,39 @@ void lcdDrawBitmap(coord_t x, coord_t y, const uint8_t * img, coord_t offset, co } } } + +void lcdDrawRleBitmap(coord_t x, coord_t y, const uint8_t * img, coord_t offset, coord_t width) +{ + RleBitmap pic(img, offset); + + uint8_t w = pic.getWidth(); + if (!width || width > w) { + width = w; + } + if (x+width > LCD_W) { + if (x >= LCD_W ) return; + width = LCD_W-x; + } + uint8_t rows = pic.getRows(); + + for (uint8_t row=0; row= DISPLAY_END) return; + uint8_t b = pic.getNext(); + if (y & 1) { + *p = (*p & 0x0f) + ((b & 0x0f) << 4); + if ((p+LCD_W) < DISPLAY_END) { + *(p+LCD_W) = (*(p+LCD_W) & 0xf0) + ((b & 0xf0) >> 4); + } + } + else { + *p = b; + } + p++; + } + pic.goToNextRow(); + pic.skip(offset); + } +} #endif diff --git a/radio/src/gui/212x64/lcd.h b/radio/src/gui/212x64/lcd.h index 338b1f339..e50035b2c 100644 --- a/radio/src/gui/212x64/lcd.h +++ b/radio/src/gui/212x64/lcd.h @@ -168,7 +168,9 @@ void drawTelemetryTopBar(); void lcdDraw1bitBitmap(coord_t x, coord_t y, const unsigned char * img, uint8_t idx, LcdFlags att=0); void lcdDrawBitmap(coord_t x, coord_t y, const uint8_t * img, coord_t offset=0, coord_t width=0); -#define LCD_ICON(x, y, icon) lcdDrawBitmap(x, y, icons, icon) +void lcdDrawRleBitmap(coord_t x, coord_t y, const uint8_t * img, coord_t offset=0, coord_t width=0); +#define LCD_ICON(x, y, icon) lcdDrawRleBitmap(x, y, icons, icon) + void lcdClear(); @@ -193,4 +195,75 @@ inline pixel_t getPixel(unsigned int x, unsigned int y) uint8_t getTextWidth(const char * s, uint8_t len=0, LcdFlags flags=0); +class RleBitmap +{ +public: + RleBitmap(const uint8_t *src, coord_t offset) : + state(RLE_FIRST_BYTE), src(src), curPtr(src), byte(0), curCount(0), pos(0) + { + width = *curPtr++; + rawRows = *curPtr++; + rows = (rawRows +1)/2; + skip(offset); + } + + void skip(coord_t count) + { + while(count) + { + count--; + getNext(); + } + } + + uint8_t getNext() + { + pos++; + switch(state) + { + case RLE_FIRST_BYTE: + byte = *curPtr++; + if(byte == *curPtr) + state = RLE_SECOND_BYTE; + break; + case RLE_SECOND_BYTE: + byte = *curPtr++; + curCount = (*curPtr++)+1; + state = RLE_CONTINUE; + // fall through + case RLE_CONTINUE: + curCount--; + if(!curCount) + state = RLE_FIRST_BYTE; + break; + } + return byte; + } + + uint8_t getWidth() const { return width; } + uint8_t getRows() const { return rows; } + uint8_t getRawRows() const { return rawRows; } + void goToNextRow() + { + coord_t offset = pos%width; + if(offset) + skip(width - pos%width); + } + +private: + enum State {RLE_FIRST_BYTE, RLE_SECOND_BYTE, RLE_CONTINUE} state; + const uint8_t* src; + const uint8_t* curPtr; + + uint8_t width; + uint8_t rows; + uint8_t rawRows; + + uint8_t byte; + uint16_t curCount; + + coord_t pos; +}; + + #endif // _LCD_H_ diff --git a/radio/src/gui/212x64/popups.cpp b/radio/src/gui/212x64/popups.cpp index 0a1420cc6..12063cfca 100644 --- a/radio/src/gui/212x64/popups.cpp +++ b/radio/src/gui/212x64/popups.cpp @@ -28,7 +28,7 @@ const unsigned char ASTERISK_BITMAP[] = { void drawAlertBox(const char * title, const char * text, const char * action) { lcdClear(); - lcdDrawBitmap(0, 0, ASTERISK_BITMAP); + lcdDrawRleBitmap(0, 0, ASTERISK_BITMAP); #define MESSAGE_LCD_OFFSET 60 diff --git a/radio/src/gui/212x64/splash.cpp b/radio/src/gui/212x64/splash.cpp index 8b4a038a1..c5b2467a2 100644 --- a/radio/src/gui/212x64/splash.cpp +++ b/radio/src/gui/212x64/splash.cpp @@ -22,16 +22,21 @@ #include "opentx.h" #if defined(SPLASH) -const unsigned char splashdata[] = { - 'S','P','S',0, - #include "bitmaps/212x64/splash.lbm" - 'S','P','E',0 }; -const unsigned char * const splash_lbm = splashdata+4; +#define MAXIMUM_SPLASH_IMAGE_SIZE 3072 +struct { + const uint8_t __magic_prefix[4] = { 'S','P','S',0 }; + const uint8_t splashdata[MAXIMUM_SPLASH_IMAGE_SIZE] = { + #include "bitmaps/212x64/splash.lbm" + }; + const uint8_t __magic_suffix[4] = { 'S','P','E',0 }; +} splash_struct; +static_assert(sizeof(splash_struct.splashdata) <= MAXIMUM_SPLASH_IMAGE_SIZE, ""); +const unsigned char * const splash_lbm = splash_struct.splashdata; void drawSplash() { lcdClear(); - lcdDrawBitmap(0, 0, splash_lbm); + lcdDrawRleBitmap(0, 0, splash_lbm); #if MENUS_LOCK == 1 if (readonly == false) { diff --git a/radio/src/gui/212x64/startup_shutdown.cpp b/radio/src/gui/212x64/startup_shutdown.cpp index c071961b0..59ebef294 100644 --- a/radio/src/gui/212x64/startup_shutdown.cpp +++ b/radio/src/gui/212x64/startup_shutdown.cpp @@ -52,9 +52,9 @@ void drawStartupAnimation(uint32_t duration, uint32_t totalDuration) lcdClear(); if (index == 0) - lcdDrawBitmap((LCD_W - ANIMATIONS_BITMAP_WIDTH) / 2, (LCD_H - ANIMATIONS_BITMAP_HEIGHT) / 2, bmp_lock, 0, ANIMATIONS_BITMAP_WIDTH); + lcdDrawRleBitmap((LCD_W - ANIMATIONS_BITMAP_WIDTH) / 2, (LCD_H - ANIMATIONS_BITMAP_HEIGHT) / 2, bmp_lock, 0, ANIMATIONS_BITMAP_WIDTH); else - lcdDrawBitmap((LCD_W - ANIMATIONS_BITMAP_WIDTH) / 2, (LCD_H - ANIMATIONS_BITMAP_HEIGHT) / 2, bmp_startup, (index - 1) * ANIMATIONS_BITMAP_WIDTH, ANIMATIONS_BITMAP_WIDTH); + lcdDrawRleBitmap((LCD_W - ANIMATIONS_BITMAP_WIDTH) / 2, (LCD_H - ANIMATIONS_BITMAP_HEIGHT) / 2, bmp_startup, (index - 1) * ANIMATIONS_BITMAP_WIDTH, ANIMATIONS_BITMAP_WIDTH); lcdRefresh(); } @@ -69,7 +69,7 @@ void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, const char lcdRefreshWait(); lcdClear(); - lcdDrawBitmap((LCD_W - ANIMATIONS_BITMAP_WIDTH) / 2, (LCD_H - ANIMATIONS_BITMAP_HEIGHT) / 2, bmp_shutdown, (3 - index) * ANIMATIONS_BITMAP_WIDTH, ANIMATIONS_BITMAP_WIDTH); + lcdDrawRleBitmap((LCD_W - ANIMATIONS_BITMAP_WIDTH) / 2, (LCD_H - ANIMATIONS_BITMAP_HEIGHT) / 2, bmp_shutdown, (3 - index) * ANIMATIONS_BITMAP_WIDTH, ANIMATIONS_BITMAP_WIDTH); if (message) { lcdDrawText((LCD_W - getTextWidth(message)) / 2, LCD_H - 2*FH, message); } @@ -81,7 +81,7 @@ void drawSleepBitmap() { lcdRefreshWait(); lcdClear(); - lcdDrawBitmap((LCD_W - ANIMATIONS_BITMAP_WIDTH) / 2, (LCD_H - ANIMATIONS_BITMAP_HEIGHT) / 2, bmp_sleep, 0); + lcdDrawRleBitmap((LCD_W - ANIMATIONS_BITMAP_WIDTH) / 2, (LCD_H - ANIMATIONS_BITMAP_HEIGHT) / 2, bmp_sleep, 0); lcdRefresh(); lcdRefreshWait(); } diff --git a/radio/src/gui/212x64/view_main.cpp b/radio/src/gui/212x64/view_main.cpp index bb9e52125..772195976 100644 --- a/radio/src/gui/212x64/view_main.cpp +++ b/radio/src/gui/212x64/view_main.cpp @@ -203,7 +203,7 @@ void displayTopBarGauge(coord_t x, int count, bool blinking=false) } #define LCD_NOTIF_ICON(x, icon) \ - lcdDrawBitmap(x, BAR_Y, icons, icon); \ + lcdDrawRleBitmap(x, BAR_Y, icons, icon); \ lcdDrawSolidHorizontalLine(x, BAR_Y+8, 11) void displayTopBar()