RLE for greyscale lcd (gives more firmware space!) (#1142)

* use RLE encodes bitmaps on 212x64 pixel radios

* fix model image display
fix icon display

* implement RLE bitmaps in companion for splash image on 212x64 radios
reserve 3kB for RLE bitmaps in firmware

* fix size check

* use defines instead of magic numbers

* fix display of splash screens on 212x64 radios

* add warning message if compressed image is larger than the reserved space
This commit is contained in:
Malte Langermann
2021-12-04 15:06:23 +11:00
committed by GitHub
parent 995e41469e
commit ec01fa8df9
12 changed files with 317 additions and 33 deletions
+169 -13
View File
@@ -25,6 +25,8 @@
#include "storage.h"
#include <QFile>
#include <QMessageBox>
#include <vector>
#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<size)
*buf++ = getNext();
return pos == size;
}
uint8_t getWidth() const { return width; }
uint8_t getRows() const { return rows; }
uint8_t getRawRows() const { return rawRows; }
void goToNextRow()
{
size_t offset = pos%width;
if(offset)
skip(width - pos%width);
}
static size_t encode(uint8_t *buf, size_t bytes)
{
uint8_t* readPtr = buf;
uint8_t* writePtr = buf;
uint8_t state = 0;
uint8_t count = 0;
uint16_t prevByte = 0xFFFF;
while(bytes)
{
bytes--;
uint8_t byte = *readPtr++;
if(state == 0)
{
*writePtr++ = byte;;
if(prevByte == byte)
{
state = 1;
count = 0;
} else {
prevByte = byte;
}
} else {
if(prevByte == byte)
{
count++;
if(count == 255)
{
*writePtr++ = count;
prevByte = 0xFFFF;
state = 0;
}
} else {
*writePtr++ = count;
*writePtr++ = byte;
prevByte = byte;
state = 0;
}
}
}
if(state != 0)
*writePtr++ = count;
return writePtr - buf;
}
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;
size_t pos;
};
FirmwareInterface::FirmwareInterface(const QString & filename, QDialog* parentDialog):
parentDialog(parentDialog),
flash(FSIZE_MAX, 0),
flashSize(0),
eepromVersion(0),
@@ -220,7 +346,7 @@ void FirmwareInterface::seekSplash()
return;
}
if (seekSplash(QByteArray(OTX_SPS_TARANIS, OTX_SPS_SIZE), QByteArray(OTX_SPE, OTX_SPE_SIZE), 6784)) {
if (seekSplash(QByteArray(OTX_SPS_TARANIS, OTX_SPS_SIZE), QByteArray(OTX_SPE, OTX_SPE_SIZE), RLE_SPLASH_MAX_SIZE)) {
splashWidth = SPLASHX9D_WIDTH;
splashHeight = SPLASHX9D_HEIGHT;
splash_format = QImage::Format_Indexed8;
@@ -243,7 +369,7 @@ bool FirmwareInterface::setSplash(const QImage & newsplash)
return false;
}
char b[SPLASH_SIZE_MAX] = {0};
uint8_t b[SPLASH_SIZE_MAX] = {0};
QColor color;
QByteArray splash;
if (splash_format == QImage::Format_Indexed8) {
@@ -256,6 +382,15 @@ bool FirmwareInterface::setSplash(const QImage & newsplash)
b[idx] |= z;
}
}
if(splashWidth == SPLASHX9D_WIDTH && splashHeight == SPLASHX9D_HEIGHT)
{
splashSize = RleBitmap::encode(b, SPLASH_SIZE_MAX);
if(splashSize > 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<splashHeight; y++) {
unsigned int idx = (y/2)*splashWidth;
for (unsigned int x=0; x<splashWidth; x++, idx++) {
uint8_t byte = flash.at(splashOffset+idx);
unsigned int z = (y & 1) ? (byte >> 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<uint8_t> 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<splashHeight; y++) {
unsigned int idx = (y/2)*splashWidth;
for (unsigned int x=0; x<splashWidth; x++, idx++) {
uint8_t byte = data[idx];
unsigned int z = (y & 1) ? (byte >> 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<splashHeight; y++) {
unsigned int idx = (y/2)*splashWidth;
for (unsigned int x=0; x<splashWidth; x++, idx++) {
uint8_t byte = flash.at(splashOffset+idx);
unsigned int z = (y & 1) ? (byte >> 4) : (byte & 0x0F);
z = 255-(z*255)/15;
QRgb rgb = qRgb(z, z, z);
image.setPixel(x, y, rgb);
}
}
}
}