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 =