[runtime_image] Report a failed seek to the start as a read error

Also limit the JPEGDEC pin comment to the fixes ESPHome uses: it never
crops, and refuses progressive images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
clyde
2026-10-22 13:14:51 +11:00
co-authored by Claude Opus 5.5
parent b2eebfb010
commit f752e3b150
3 changed files with 20 additions and 5 deletions
+2 -3
View File
@@ -94,9 +94,8 @@ class JPEGFormat(Format):
cg.add_define("USE_RUNTIME_IMAGE_JPEG")
# 1.8.4 plus decodeBegin()/decodeRows(), so an image can be decoded a few rows at a time and
# can wait for input that has not arrived yet, and fixes for: input that arrives in short
# reads, garbled RGB8888 output of grayscale images, progressive images (skipped blocks, unused
# AC tables that fail to build), cropping, and two ARM NEON faults (swapped red and blue in the
# 4:2:0 RGB8888 path, writes past the row on clipped MCUs)
# reads, garbled RGB8888 output of grayscale images, and two ARM NEON faults seen on arm64 hosts
# (swapped red and blue in the 4:2:0 RGB8888 path, writes past the row on clipped MCUs)
cg.add_library(
"JPEGDEC",
"1.8.4",
@@ -229,10 +229,13 @@ int JpegDecoder::begin_source(DecodeSource *source) {
ESP_LOGE(TAG, "Image size %zu cannot be read from a source", this->expected_size_);
return DECODE_ERROR_INVALID_TYPE;
}
if (!source->seek(0)) {
ESP_LOGE(TAG, "Could not read the image");
return DECODE_ERROR_READ;
}
this->source_ = source;
this->source_failed_ = false;
if (!source->seek(0) ||
!this->jpeg_.open(this, this->expected_size_, source_close_, source_read_, source_seek_, draw_callback)) {
if (!this->jpeg_.open(this, this->expected_size_, source_close_, source_read_, source_seek_, draw_callback)) {
ESP_LOGE(TAG, "Could not open image for decoding: %d", this->jpeg_.getLastError());
this->source_ = nullptr;
return this->source_failed_ ? DECODE_ERROR_READ : DECODE_ERROR_INVALID_TYPE;
@@ -326,6 +326,19 @@ TEST(JpegSourceDecode, FailedFirstReadIsAReadError) {
EXPECT_EQ(img.begin_source(&failing), DECODE_ERROR_READ);
}
TEST(JpegSourceDecode, FailedSeekToTheStartIsAReadError) {
class NoSeekSource : public MemorySource {
public:
using MemorySource::MemorySource;
bool seek(uint32_t position) override { return false; }
};
SourceImage img;
NoSeekSource source(JPEG_ROWS, sizeof(JPEG_ROWS));
ASSERT_TRUE(img.begin_decode(sizeof(JPEG_ROWS)));
EXPECT_EQ(img.begin_source(&source), DECODE_ERROR_READ);
EXPECT_EQ(img.decode_step(), DECODE_ERROR_INVALID_TYPE);
}
TEST(JpegSourceDecode, SourceIsReadAsTheDecodeNeedsIt) {
SourceImage img;
MemorySource source(JPEG_ROWS, sizeof(JPEG_ROWS));