Recover parts of the regression fiasco

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
jpirnay
2026-05-02 18:19:23 +02:00
co-authored by Copilot
parent a5469c0bb9
commit 06dfc6c50b
21 changed files with 354 additions and 303 deletions
@@ -173,6 +173,84 @@ int32_t jpegSeek(JPEGFILE* pFile, int32_t pos) {
constexpr size_t JPEG_DECODER_APPROX_SIZE = 20 * 1024;
constexpr size_t MIN_FREE_HEAP_FOR_JPEG = JPEG_DECODER_APPROX_SIZE + 16 * 1024;
bool readJpegDimensionsFromHeader(const std::string& imagePath, ImageDimensions& out) {
FsFile f;
if (!Storage.openFileForRead("JPG", imagePath, f)) {
LOG_ERR("JPG", "Failed to open file for dimensions: %s", imagePath.c_str());
return false;
}
auto readByte = [&f](uint8_t& b) -> bool { return f.read(&b, 1) == 1; };
auto readU16BE = [&f](uint16_t& v) -> bool {
uint8_t b[2];
if (f.read(b, 2) != 2) return false;
v = static_cast<uint16_t>((static_cast<uint16_t>(b[0]) << 8) | b[1]);
return true;
};
uint8_t b0 = 0;
uint8_t b1 = 0;
if (!readByte(b0) || !readByte(b1) || b0 != 0xFF || b1 != 0xD8) {
f.close();
LOG_ERR("JPG", "Not a JPEG file: %s", imagePath.c_str());
return false;
}
while (f.available()) {
uint8_t prefix = 0;
if (!readByte(prefix)) break;
if (prefix != 0xFF) continue;
uint8_t marker = 0;
do {
if (!readByte(marker)) {
f.close();
return false;
}
} while (marker == 0xFF);
if (marker == 0x00 || marker == 0xD8 || marker == 0xD9 || (marker >= 0xD0 && marker <= 0xD7)) {
continue;
}
uint16_t segLen = 0;
if (!readU16BE(segLen) || segLen < 2) {
f.close();
return false;
}
const bool isSof = (marker >= 0xC0 && marker <= 0xC3) || (marker >= 0xC5 && marker <= 0xC7) ||
(marker >= 0xC9 && marker <= 0xCB) || (marker >= 0xCD && marker <= 0xCF);
if (isSof) {
uint8_t sof[5];
if (segLen < 7 || f.read(sof, sizeof(sof)) != static_cast<int>(sizeof(sof))) {
f.close();
return false;
}
uint16_t height = static_cast<uint16_t>((static_cast<uint16_t>(sof[1]) << 8) | sof[2]);
uint16_t width = static_cast<uint16_t>((static_cast<uint16_t>(sof[3]) << 8) | sof[4]);
f.close();
if (width == 0 || height == 0) {
LOG_ERR("JPG", "Invalid JPEG dimensions %ux%u: %s", width, height, imagePath.c_str());
return false;
}
out.width = static_cast<int16_t>(width);
out.height = static_cast<int16_t>(height);
return true;
}
const int32_t skip = static_cast<int32_t>(segLen) - 2;
if (!f.seek(f.position() + skip)) {
f.close();
return false;
}
}
f.close();
LOG_ERR("JPG", "No SOF marker found for dimensions: %s", imagePath.c_str());
return false;
}
// Choose JPEGDEC's built-in scale factor for coarse downscaling.
// Returns the scale denominator (1, 2, 4, or 8) and sets jpegScaleOption.
int chooseJpegScale(float targetScale, int& jpegScaleOption) {
@@ -401,29 +479,10 @@ int jpegDrawCallback(JPEGDRAW* pDraw) {
} // namespace
bool JpegToFramebufferConverter::getDimensionsStatic(const std::string& imagePath, ImageDimensions& out) {
size_t freeHeap = ESP.getFreeHeap();
if (freeHeap < MIN_FREE_HEAP_FOR_JPEG) {
LOG_ERR("JPG", "Not enough heap for JPEG decoder (%u free, need %u)", freeHeap, MIN_FREE_HEAP_FOR_JPEG);
if (!readJpegDimensionsFromHeader(imagePath, out)) {
return false;
}
std::unique_ptr<JPEGDEC> jpeg(new (std::nothrow) JPEGDEC());
if (!jpeg) {
LOG_ERR("JPG", "Failed to allocate JPEG decoder for dimensions");
return false;
}
int rc = jpeg->open(imagePath.c_str(), jpegOpen, jpegClose, jpegRead, jpegSeek, nullptr);
if (rc != 1) {
LOG_ERR("JPG", "Failed to open JPEG for dimensions (err=%d): %s", jpeg->getLastError(), imagePath.c_str());
return false;
}
out.width = jpeg->getWidth();
out.height = jpeg->getHeight();
LOG_DBG("JPG", "Image dimensions: %dx%d", out.width, out.height);
jpeg->close();
return true;
}