Reset Sample Transform state in avifDecoderReset() (#3327) Clear the parsed Sample Transform expression and input count before the decode input is rebuilt. Otherwise a second reset, including one performed by avifDecoderSetSource(), encounters stale state and returns AVIF_RESULT_INTERNAL_ERROR, or asserts in debug builds. Add coverage for direct reset and avifDecoderSetSource(). Signed-off-by: David Sarkisyan <david@srkyn.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md index 472f710..31d7841 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -40,6 +40,8 @@ * Parse the children of the 'iref' and 'grpl' boxes within their own declared box size. Files with an EntityToGroupBox carrying grouping type specific data after the entity_id array are no longer rejected. +* Reset Sample Transform decoder state in avifDecoderReset() so repeated resets + and avifDecoderSetSource() calls do not fail. ## [1.4.2] - 2026-05-26
diff --git a/src/read.c b/src/read.c index 9709e1e..29817af 100644 --- a/src/read.c +++ b/src/read.c
@@ -6093,6 +6093,8 @@ memset(&data->tileInfos[c].grid, 0, sizeof(data->tileInfos[c].grid)); } avifDecoderDataClearTiles(data); + data->sampleTransformNumInputImageItems = 0; + avifArrayDestroy(&data->meta->sampleTransformExpression); // Prepare / cleanup decoded image state if (decoder->image) {
diff --git a/tests/gtest/avifaltrtest.cc b/tests/gtest/avifaltrtest.cc index 28a0822..0f0640a 100644 --- a/tests/gtest/avifaltrtest.cc +++ b/tests/gtest/avifaltrtest.cc
@@ -73,6 +73,28 @@ EXPECT_EQ(decoder->image->depth, depth); } +TEST(AltrTest, SampleTransformDecoderReset) { + const testutil::AvifRwData encoded = + testutil::ReadFile(std::string(data_path) + "weld_sato_12B_8B_q0.avif"); + + DecoderPtr decoder(avifDecoderCreate()); + ASSERT_NE(decoder, nullptr); + decoder->imageContentToDecode |= AVIF_IMAGE_CONTENT_SAMPLE_TRANSFORMS; + ASSERT_EQ(avifDecoderSetIOMemory(decoder.get(), encoded.data, encoded.size), + AVIF_RESULT_OK); + + ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_OK); + ASSERT_EQ(avifDecoderReset(decoder.get()), AVIF_RESULT_OK); + ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK); + EXPECT_EQ(decoder->image->depth, 16u); + + // avifDecoderSetSource() automatically resets an already parsed decoder. + ASSERT_EQ(avifDecoderSetSource(decoder.get(), AVIF_DECODER_SOURCE_AUTO), + AVIF_RESULT_OK); + ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK); + EXPECT_EQ(decoder->image->depth, 16u); +} + // Verifies the fix for https://github.com/AOMediaCodec/libavif/issues/2979. TEST(AltrTest, ZeroImageContentToDecode) { const std::string file_path =