mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 02:04:36 +01:00
add support for 3bpp ACeP buffers (7 color epaper)
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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" ]
|
||||
|
||||
Reference in New Issue
Block a user