add support for 3bpp ACeP buffers (7 color epaper)

This commit is contained in:
Nic Limper
2024-11-13 15:12:26 +01:00
parent 7a31db91ba
commit 95d5aac01a
9 changed files with 144 additions and 40 deletions

Binary file not shown.

View File

@@ -277,7 +277,10 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) {
imageParams.lut = EPD_LUT_DEFAULT;
}
if (imageParams.zlib) {
if (imageParams.bpp == 3) {
imageParams.dataType = DATATYPE_IMG_RAW_3BPP;
Serial.println("datatype: DATATYPE_IMG_RAW_3BPP");
} else if (imageParams.zlib) {
imageParams.dataType = DATATYPE_IMG_ZLIB;
Serial.println("datatype: DATATYPE_IMG_ZLIB");
} else if (imageParams.hasRed) {
@@ -557,7 +560,10 @@ bool updateTagImage(String &filename, const uint8_t *dst, uint16_t nextCheckin,
imageParams.lut = EPD_LUT_DEFAULT;
}
if (imageParams.zlib) {
if (imageParams.bpp == 3) {
imageParams.dataType = DATATYPE_IMG_RAW_3BPP;
Serial.println("datatype: DATATYPE_IMG_RAW_3BPP");
} else if (imageParams.zlib) {
imageParams.dataType = DATATYPE_IMG_ZLIB;
Serial.println("datatype: DATATYPE_IMG_ZLIB");
} else if (imageParams.hasRed) {
@@ -1439,18 +1445,32 @@ bool getCalFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
#ifdef CONTENT_DAYAHEAD
uint16_t getPercentileColor(const double *prices, int numPrices, double price, HwType hwdata) {
double percentile = 100.0;
int colorIndex = 3;
const char *colors[] = {"black", "darkgray", "pink", "red"};
if (hwdata.highlightColor == 3) {
// yellow
colors[2] = "brown";
colors[3] = "yellow";
}
const int numColors = sizeof(colors) / sizeof(colors[0]);
const char *colorsDefault[] = {"black", "darkgray", "pink", "red"};
const double boundariesDefault[] = {40.0, 80.0, 90.0};
const double boundaries[] = {40.0, 80.0, 90.0};
const int numBoundaries = sizeof(boundaries) / sizeof(boundaries[0]);
const char *colors3bpp[] = {"blue", "green", "yellow", "orange", "red"};
const double boundaries3bpp[] = {20.0, 50.0, 70.0, 90.0};
const char **colors;
const double *boundaries;
int numColors, numBoundaries;
if (hwdata.bpp == 3) {
colors = colors3bpp;
boundaries = boundaries3bpp;
numColors = sizeof(colors3bpp) / sizeof(colors3bpp[0]);
numBoundaries = sizeof(boundaries3bpp) / sizeof(boundaries3bpp[0]);
} else {
colors = colorsDefault;
boundaries = boundariesDefault;
numColors = sizeof(colorsDefault) / sizeof(colorsDefault[0]);
numBoundaries = sizeof(boundariesDefault) / sizeof(boundariesDefault[0]);
if (hwdata.highlightColor == 3) {
colors[2] = "brown";
colors[3] = "yellow";
}
}
int colorIndex = numColors - 1;
for (int i = 0; i < numBoundaries; i++) {
if (price < prices[int(numPrices * boundaries[i] / 100.0)]) {
@@ -1622,7 +1642,7 @@ bool getDayAheadFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo,
spr.fillTriangle(barX + i * barwidth, 15 + arrowY,
barX + i * barwidth + barwidth - 1, 15 + arrowY,
barX + i * barwidth + (barwidth - 1) / 2, 15 + barwidth + arrowY, imageParams.highlightColor);
spr.drawLine(barX + i * barwidth + (barwidth - 1) / 2, 20 + barwidth + arrowY, barX + i * barwidth + (barwidth - 1) / 2, spr.height(), getColor("pink"));
spr.drawLine(barX + i * barwidth + (barwidth - 1) / 2, 20 + barwidth + arrowY, barX + i * barwidth + (barwidth - 1) / 2, spr.height(), TFT_BLACK);
pricenow = price;
}
}
@@ -2217,6 +2237,9 @@ uint16_t getColor(const String &color) {
if (color == "5" || color == "darkgray") return TFT_DARKGREY;
if (color == "6" || color == "pink") return 0xFBCF;
if (color == "7" || color == "brown") return 0x8400;
if (color == "8" || color == "green") return TFT_GREEN;
if (color == "9" || color == "blue") return TFT_BLUE;
if (color == "10" || color == "orange") return 0xFBE0;
uint16_t r, g, b;
if (color.length() == 7 && color[0] == '#' &&
sscanf(color.c_str(), "#%2hx%2hx%2hx", &r, &g, &b) == 3) {

View File

@@ -112,6 +112,7 @@ void spr2color(TFT_eSprite &spr, imgParam &imageParams, uint8_t *buffer, size_t
{12, 5, 14, 6},
{3, 11, 1, 8},
{15, 7, 13, 4}};
size_t bitOffset = 0;
memset(error_bufferold, 0, bufw * sizeof(Error));
for (uint16_t y = 0; y < bufh; y++) {
@@ -134,10 +135,10 @@ void spr2color(TFT_eSprite &spr, imgParam &imageParams, uint8_t *buffer, size_t
if (imageParams.dither == 2) {
// Ordered dithering
uint8_t ditherValue = ditherMatrix[y % 4][x % 4];
error_bufferold[x].r = (ditherValue << 4) - 120; // * 256 / 16 - 128 + 8
error_bufferold[x].g = (ditherValue << 4) - 120;
error_bufferold[x].b = (ditherValue << 4) - 120;
uint8_t ditherValue = ditherMatrix[y % 4][x % 4] << (imageParams.bpp == 3 ? 2 : 4);
error_bufferold[x].r = ditherValue - (imageParams.bpp == 3 ? 30 : 120); // * 256 / 16 - 128 + 8
error_bufferold[x].g = ditherValue - (imageParams.bpp == 3 ? 30 : 120);
error_bufferold[x].b = ditherValue - (imageParams.bpp == 3 ? 30 : 120);
}
int best_color_index = 0;
@@ -151,24 +152,40 @@ void spr2color(TFT_eSprite &spr, imgParam &imageParams, uint8_t *buffer, size_t
best_color_index = i;
}
}
uint8_t bitIndex = 7 - (x % 8);
uint32_t byteIndex = (y * bufw + x) / 8;
// this looks a bit ugly, but it's performing better than shorter notations
switch (best_color_index) {
case 1:
if (!is_red)
if (imageParams.bpp == 3) {
size_t byteIndex = bitOffset / 8;
uint8_t bitIndex = bitOffset % 8;
if (bitIndex + imageParams.bpp <= 8) {
buffer[byteIndex] |= best_color_index << (8 - bitIndex - imageParams.bpp);
} else {
uint8_t highPart = best_color_index >> (bitIndex + imageParams.bpp - 8);
uint8_t lowPart = best_color_index & ((1 << (bitIndex + imageParams.bpp - 8)) - 1);
buffer[byteIndex] |= highPart;
buffer[byteIndex + 1] |= lowPart << (8 - (bitIndex + imageParams.bpp - 8));
}
bitOffset += imageParams.bpp;
} else {
uint8_t bitIndex = 7 - (x % 8);
uint32_t byteIndex = (y * bufw + x) / 8;
// this looks a bit ugly, but it's performing better than shorter notations
switch (best_color_index) {
case 1:
if (!is_red)
buffer[byteIndex] |= (1 << bitIndex);
break;
case 2:
imageParams.hasRed = true;
if (is_red)
buffer[byteIndex] |= (1 << bitIndex);
break;
case 3:
imageParams.hasRed = true;
buffer[byteIndex] |= (1 << bitIndex);
break;
case 2:
imageParams.hasRed = true;
if (is_red)
buffer[byteIndex] |= (1 << bitIndex);
break;
case 3:
imageParams.hasRed = true;
buffer[byteIndex] |= (1 << bitIndex);
break;
break;
}
}
if (imageParams.dither == 1) {
@@ -226,7 +243,10 @@ size_t prepareHeader(uint8_t headerbuf[], uint16_t bufw, uint16_t bufh, imgParam
memcpy(headerbuf + (imageParams.rotatebuffer % 2 == 1 ? 3 : 1), &bufw, sizeof(uint16_t));
memcpy(headerbuf + (imageParams.rotatebuffer % 2 == 1 ? 1 : 3), &bufh, sizeof(uint16_t));
if (imageParams.hasRed && imageParams.bpp > 1) {
if (imageParams.bpp == 3) {
totalbytes = buffer_size * 3 + headersize;
headerbuf[5] = 3;
} else if (imageParams.hasRed && imageParams.bpp > 1) {
totalbytes = buffer_size * 2 + headersize;
headerbuf[5] = 2;
} else {
@@ -370,6 +390,23 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
free(buffer);
} break;
case 3: {
long bufw = spr.width(), bufh = spr.height();
size_t buffer_size = (bufw * bufh) / 8 * 3;
uint8_t *buffer = (uint8_t *)ps_malloc(buffer_size);
if (!buffer) {
Serial.println("Failed to allocate buffer");
util::printLargestFreeBlock();
f_out.close();
xSemaphoreGive(fsMutex);
return;
}
spr2color(spr, imageParams, buffer, buffer_size, false);
f_out.write(buffer, buffer_size);
free(buffer);
} break;
case 16: {
size_t spriteDataSize = (spr.getColorDepth() == 1) ? (spr.width() * spr.height() / 8) : ((spr.getColorDepth() == 8) ? (spr.width() * spr.height()) : ((spr.getColorDepth() == 16) ? (spr.width() * spr.height() * 2) : 0));
f_out.write((const uint8_t *)spr.getPointer(), spriteDataSize);

View File

@@ -273,7 +273,8 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
case DATATYPE_IMG_DIFF:
case DATATYPE_IMG_ZLIB:
case DATATYPE_IMG_RAW_1BPP:
case DATATYPE_IMG_RAW_2BPP: {
case DATATYPE_IMG_RAW_2BPP:
case DATATYPE_IMG_RAW_3BPP: {
char hexmac[17];
mac2hex(pending->targetMac, hexmac);
String filename = "/current/" + String(hexmac) + "_" + String(millis() % 1000000) + ".pending";
@@ -445,7 +446,7 @@ void processXferComplete(struct espXferComplete* xfc, bool local) {
contentFS->remove(dst_path);
}
if (contentFS->exists(queueItem->filename)) {
if (config.preview && (queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_RAW_2BPP || queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_RAW_1BPP || queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_ZLIB)) {
if (config.preview && (queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_RAW_3BPP || queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_RAW_2BPP || queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_RAW_1BPP || queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_ZLIB)) {
contentFS->rename(queueItem->filename, String(dst_path));
} else {
if (queueItem->pendingdata.availdatainfo.dataType != DATATYPE_FW_UPDATE) contentFS->remove(queueItem->filename);
@@ -966,7 +967,7 @@ bool queueDataAvail(struct pendingData* pending, bool local) {
}
newPending.len = taginfo->len;
if ((pending->availdatainfo.dataType == DATATYPE_IMG_RAW_1BPP || pending->availdatainfo.dataType == DATATYPE_IMG_RAW_2BPP || pending->availdatainfo.dataType == DATATYPE_IMG_ZLIB) && (pending->availdatainfo.dataTypeArgument & 0xF8) == 0x00) {
if ((pending->availdatainfo.dataType == DATATYPE_IMG_RAW_1BPP || pending->availdatainfo.dataType == DATATYPE_IMG_RAW_2BPP || pending->availdatainfo.dataType == DATATYPE_IMG_RAW_3BPP || pending->availdatainfo.dataType == DATATYPE_IMG_ZLIB) && (pending->availdatainfo.dataTypeArgument & 0xF8) == 0x00) {
// in case of an image (no preload), remove already queued images
pendingQueue.erase(std::remove_if(pendingQueue.begin(), pendingQueue.end(),
[pending](const PendingItem& item) {

View File

@@ -452,7 +452,27 @@
imageData.data[i * 4 + 2] = is16Bit ? (rgb & 0x1F) << 3 : ((rgb & 0x03) << 6) * 1.3;
imageData.data[i * 4 + 3] = 255;
}
} else if (tagTypes[hwtype].bpp == 3) {
const colorTable = tagTypes[hwtype].colortable;
let pixelIndex = 0;
for (let i = 0; i < data.length; i += 3) {
for (let j = 0; j < 8; j++) {
let bitPos = j * 3;
let bytePos = Math.floor(bitPos / 8);
let bitOffset = bitPos % 8;
let pixelValue = (data[i + bytePos] >> (5 - bitOffset)) & 0x07;
if (bitOffset > 5) {
pixelValue = ((data[i + bytePos] & (0xFF >> bitOffset)) << (bitOffset - 5)) |
(data[i + bytePos + 1] >> (13 - bitOffset));
}
imageData.data[pixelIndex * 4] = colorTable[pixelValue][0];
imageData.data[pixelIndex * 4 + 1] = colorTable[pixelValue][1];
imageData.data[pixelIndex * 4 + 2] = colorTable[pixelValue][2];
imageData.data[pixelIndex * 4 + 3] = 255;
pixelIndex++;
}
}
} else {
const offsetRed = (data.length >= (canvas.width * canvas.height / 8) * 2) ? canvas.width * canvas.height / 8 : 0;

View File

@@ -1243,6 +1243,28 @@ function drawCanvas(buffer, canvas, hwtype, tagmac, doRotate) {
imageData.data[i * 4 + 3] = 255;
}
} else if (tagTypes[hwtype].bpp == 3) {
const colorTable = tagTypes[hwtype].colortable;
let pixelIndex = 0;
for (let i = 0; i < data.length; i += 3) {
for (let j = 0; j < 8; j++) {
let bitPos = j * 3;
let bytePos = Math.floor(bitPos / 8);
let bitOffset = bitPos % 8;
let pixelValue = (data[i + bytePos] >> (5 - bitOffset)) & 0x07;
if (bitOffset > 5) {
pixelValue = ((data[i + bytePos] & (0xFF >> bitOffset)) << (bitOffset - 5)) |
(data[i + bytePos + 1] >> (13 - bitOffset));
}
imageData.data[pixelIndex * 4] = colorTable[pixelValue][0];
imageData.data[pixelIndex * 4 + 1] = colorTable[pixelValue][1];
imageData.data[pixelIndex * 4 + 2] = colorTable[pixelValue][2];
imageData.data[pixelIndex * 4 + 3] = 255;
pixelIndex++;
}
}
} else {
const offsetRed = (data.length >= (canvas.width * canvas.height / 8) * 2) ? canvas.width * canvas.height / 8 : 0;

View File

@@ -117,6 +117,7 @@
#define DATATYPE_IMG_DIFF 0x10 // always 1BPP ** deprecated
#define DATATYPE_IMG_RAW_1BPP 0x20 // 2888 bytes for 1.54" / 4736 2.9" / 15000 4.2"
#define DATATYPE_IMG_RAW_2BPP 0x21 // 5776 bytes for 1.54" / 9472 2.9" / 30000 4.2"
#define DATATYPE_IMG_RAW_3BPP 0x22 // ACEP
#define DATATYPE_IMG_ZLIB 0x30 // compressed format.
// [uint32_t uncompressed size][2 byte zlib header][zlib compressed image]
// image format: [uint8_t header length][uint16_t width][uint16_t height][uint8_t bpp (lower 4)][img data]

View File

@@ -1,5 +1,5 @@
{
"version": 3,
"version": 4,
"name": "M2 7.4\"",
"width": 640,
"height": 384,
@@ -54,7 +54,7 @@
"gridparam": [ 3, 17, 30, "calibrib16.vlw", "tahoma9.vlw", 14 ]
},
"27": {
"bars": [ 18, 612, 350, 20 ],
"bars": [ 18, 612, 330, 20, 22 ],
"time": [ "calibrib16.vlw" ],
"yaxis": [ "calibrib16.vlw", 1, 12 ],
"head": [ "calibrib30.vlw" ]