diff --git a/ESP32_AP-Flasher/data/www/edit.html.gz b/ESP32_AP-Flasher/data/www/edit.html.gz index b94cbe01..c0333952 100644 Binary files a/ESP32_AP-Flasher/data/www/edit.html.gz and b/ESP32_AP-Flasher/data/www/edit.html.gz differ diff --git a/ESP32_AP-Flasher/data/www/main.js.gz b/ESP32_AP-Flasher/data/www/main.js.gz index c13005c6..d1336d6e 100644 Binary files a/ESP32_AP-Flasher/data/www/main.js.gz and b/ESP32_AP-Flasher/data/www/main.js.gz differ diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp index 99e82c72..f11f84c1 100644 --- a/ESP32_AP-Flasher/src/contentmanager.cpp +++ b/ESP32_AP-Flasher/src/contentmanager.cpp @@ -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) { diff --git a/ESP32_AP-Flasher/src/makeimage.cpp b/ESP32_AP-Flasher/src/makeimage.cpp index b02a1a3e..5371dcaf 100644 --- a/ESP32_AP-Flasher/src/makeimage.cpp +++ b/ESP32_AP-Flasher/src/makeimage.cpp @@ -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); diff --git a/ESP32_AP-Flasher/src/newproto.cpp b/ESP32_AP-Flasher/src/newproto.cpp index a2edb8c9..9fd3cb3d 100644 --- a/ESP32_AP-Flasher/src/newproto.cpp +++ b/ESP32_AP-Flasher/src/newproto.cpp @@ -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) { diff --git a/ESP32_AP-Flasher/wwwroot/edit.html b/ESP32_AP-Flasher/wwwroot/edit.html index bfeec7ea..3e7c7109 100644 --- a/ESP32_AP-Flasher/wwwroot/edit.html +++ b/ESP32_AP-Flasher/wwwroot/edit.html @@ -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; diff --git a/ESP32_AP-Flasher/wwwroot/main.js b/ESP32_AP-Flasher/wwwroot/main.js index b8eab990..b6afee26 100644 --- a/ESP32_AP-Flasher/wwwroot/main.js +++ b/ESP32_AP-Flasher/wwwroot/main.js @@ -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; diff --git a/oepl-definitions.h b/oepl-definitions.h index 7800778c..18ea7457 100755 --- a/oepl-definitions.h +++ b/oepl-definitions.h @@ -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] diff --git a/resources/tagtypes/05.json b/resources/tagtypes/05.json index 5a73e269..0f992338 100644 --- a/resources/tagtypes/05.json +++ b/resources/tagtypes/05.json @@ -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" ]