Correctly update encoder settings, clear csOptions after avifEncoderAddImage()

Only allow quantizer and tileRows/Cols to change.
diff --git a/include/avif/avif.h b/include/avif/avif.h
index 9b34159..08a1d50 100644
--- a/include/avif/avif.h
+++ b/include/avif/avif.h
@@ -1058,8 +1058,8 @@
 //   image in less bytes. AVIF_SPEED_DEFAULT means "Leave the AV1 codec to its default speed settings"./
 //   If avifEncoder uses rav1e, the speed value is directly passed through (0-10). If libaom is used,
 //   a combination of settings are tweaked to simulate this speed range.
-// * AV1 encoder settings and codec specific options set by avifEncoderSetCodecSpecificOption()
-//   will be applied / updated to AV1 encoder before each call to avifEncoderAddImage().
+// * Some encoder settings can be changed after encoding starts. Changes will take effect in the next
+//   call to avifEncoderAddImage().
 typedef struct avifEncoder
 {
     // Defaults to AVIF_CODEC_CHOICE_AUTO: Preference determined by order in availableCodecs table (avif.c)
@@ -1068,15 +1068,15 @@
     // settings (see Notes above)
     int keyframeInterval; // How many frames between automatic forced keyframes; 0 to disable (default).
     uint64_t timescale;   // timescale of the media (Hz)
-    // AV1 encoder settings.
     int maxThreads;
+    int speed;
+    // changeable encoder settings.
     int minQuantizer;
     int maxQuantizer;
     int minQuantizerAlpha;
     int maxQuantizerAlpha;
     int tileRowsLog2;
     int tileColsLog2;
-    int speed;
 
     // stats from the most recent write
     avifIOStats ioStats;
@@ -1128,8 +1128,10 @@
                                             avifAddImageFlags addImageFlags);
 AVIF_API avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output);
 
-// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs.
-// key must be non-NULL, but passing a NULL value will delete that key, if it exists.
+// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs,
+// to be consumed by the codec at the next avifEncoderAddImage() call.
+// See the codec documentation to know if a setting is persistent or applied only to the next frame.
+// key must be non-NULL, but passing a NULL value will delete the pending key, if it exists.
 // Setting an incorrect or unknown option for the current codec will cause errors of type
 // AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION from avifEncoderWrite() or avifEncoderAddImage().
 AVIF_API void avifEncoderSetCodecSpecificOption(avifEncoder * encoder, const char * key, const char * value);
diff --git a/include/avif/internal.h b/include/avif/internal.h
index 8b1ef22..da650ae 100644
--- a/include/avif/internal.h
+++ b/include/avif/internal.h
@@ -250,6 +250,7 @@
 } avifCodecSpecificOption;
 AVIF_ARRAY_DECLARE(avifCodecSpecificOptions, avifCodecSpecificOption, entries);
 avifCodecSpecificOptions * avifCodecSpecificOptionsCreate(void);
+void avifCodecSpecificOptionsClear(avifCodecSpecificOptions * csOptions);
 void avifCodecSpecificOptionsDestroy(avifCodecSpecificOptions * csOptions);
 void avifCodecSpecificOptionsSet(avifCodecSpecificOptions * csOptions, const char * key, const char * value); // if(value==NULL), key is deleted
 
@@ -259,6 +260,19 @@
 struct avifCodec;
 struct avifCodecInternal;
 
+typedef enum avifEncoderChange
+{
+    AVIF_ENCODER_CHANGE_MIN_QUANTIZER = (1 << 0),
+    AVIF_ENCODER_CHANGE_MAX_QUANTIZER = (1 << 1),
+    AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA = (1 << 2),
+    AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA = (1 << 3),
+    AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2 = (1 << 4),
+    AVIF_ENCODER_CHANGE_TILE_COLS_LOG2 = (1 << 5),
+
+    AVIF_ENCODER_CHANGE_CODEC_SPECIFIC = (1 << 31)
+} avifEncoderChange;
+typedef uint32_t avifEncoderChanges;
+
 typedef avifBool (*avifCodecGetNextImageFunc)(struct avifCodec * codec,
                                               struct avifDecoder * decoder,
                                               const avifDecodeSample * sample,
@@ -273,7 +287,7 @@
                                                avifEncoder * encoder,
                                                const avifImage * image,
                                                avifBool alpha,
-                                               avifBool updateConfig,
+                                               avifEncoderChanges encoderChanges,
                                                avifAddImageFlags addImageFlags,
                                                avifCodecEncodeOutput * output);
 typedef avifBool (*avifCodecEncodeFinishFunc)(struct avifCodec * codec, avifCodecEncodeOutput * output);
diff --git a/src/avif.c b/src/avif.c
index 81f2889..fd4a9ed 100644
--- a/src/avif.c
+++ b/src/avif.c
@@ -878,17 +878,24 @@
     return NULL;
 }
 
+void avifCodecSpecificOptionsClear(avifCodecSpecificOptions * csOptions)
+{
+    for (uint32_t i = 0; i < csOptions->count; ++i) {
+        avifCodecSpecificOption * entry = &csOptions->entries[i];
+        avifFree(entry->key);
+        avifFree(entry->value);
+    }
+
+    csOptions->count = 0;
+}
+
 void avifCodecSpecificOptionsDestroy(avifCodecSpecificOptions * csOptions)
 {
     if (!csOptions) {
         return;
     }
 
-    for (uint32_t i = 0; i < csOptions->count; ++i) {
-        avifCodecSpecificOption * entry = &csOptions->entries[i];
-        avifFree(entry->key);
-        avifFree(entry->value);
-    }
+    avifCodecSpecificOptionsClear(csOptions);
     avifArrayDestroy(csOptions);
     avifFree(csOptions);
 }
@@ -916,10 +923,12 @@
         }
     }
 
-    // Add a new key
-    avifCodecSpecificOption * entry = (avifCodecSpecificOption *)avifArrayPushPtr(csOptions);
-    entry->key = avifStrdup(key);
-    entry->value = avifStrdup(value);
+    if (value) {
+        // Add a new key
+        avifCodecSpecificOption * entry = (avifCodecSpecificOption *)avifArrayPushPtr(csOptions);
+        entry->key = avifStrdup(key);
+        entry->value = avifStrdup(value);
+    }
 }
 
 // ---------------------------------------------------------------------------
diff --git a/src/codec_aom.c b/src/codec_aom.c
index 6f64e59..4d9c801 100644
--- a/src/codec_aom.c
+++ b/src/codec_aom.c
@@ -527,11 +527,17 @@
                                       avifEncoder * encoder,
                                       const avifImage * image,
                                       avifBool alpha,
-                                      avifBool updateConfig,
+                                      avifEncoderChanges encoderChanges,
                                       avifAddImageFlags addImageFlags,
                                       avifCodecEncodeOutput * output)
 {
-    if (!codec->internal->encoderInitialized || updateConfig) {
+    struct aom_codec_enc_cfg * cfg = &codec->internal->cfg;
+    aom_codec_iface_t * encoderInterface = NULL;
+    unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY;
+    int aomCpuUsed = -1;
+    avifBool lossless = AVIF_FALSE;
+
+    if (!codec->internal->encoderInitialized) {
         // Map encoder speed to AOM usage + CpuUsed:
         // Speed  0: GoodQuality CpuUsed 0
         // Speed  1: GoodQuality CpuUsed 1
@@ -544,7 +550,6 @@
         // Speed  8: RealTime    CpuUsed 8
         // Speed  9: RealTime    CpuUsed 9
         // Speed 10: RealTime    CpuUsed 9
-        unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY;
         // Use the new AOM_USAGE_ALL_INTRA (added in https://crbug.com/aomedia/2959) for still
         // image encoding if it is available.
 #if defined(AOM_USAGE_ALL_INTRA)
@@ -552,7 +557,6 @@
             aomUsage = AOM_USAGE_ALL_INTRA;
         }
 #endif
-        int aomCpuUsed = -1;
         if (encoder->speed != AVIF_SPEED_DEFAULT) {
             aomCpuUsed = AVIF_CLAMP(encoder->speed, 0, 9);
             if (aomCpuUsed >= 7) {
@@ -589,23 +593,18 @@
             }
         }
 
-        struct aom_codec_enc_cfg * cfg = &codec->internal->cfg;
+        codec->internal->aomFormat = avifImageCalcAOMFmt(image, alpha);
+        if (codec->internal->aomFormat == AOM_IMG_FMT_NONE) {
+            return AVIF_RESULT_UNKNOWN_ERROR;
+        }
 
-        aom_codec_iface_t * encoderInterface = NULL;
-        if (!codec->internal->encoderInitialized) {
-            codec->internal->aomFormat = avifImageCalcAOMFmt(image, alpha);
-            if (codec->internal->aomFormat == AOM_IMG_FMT_NONE) {
-                return AVIF_RESULT_UNKNOWN_ERROR;
-            }
+        avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo);
 
-            avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo);
-
-            encoderInterface = aom_codec_av1_cx();
-            aom_codec_err_t err = aom_codec_enc_config_default(encoderInterface, cfg, aomUsage);
-            if (err != AOM_CODEC_OK) {
-                avifDiagnosticsPrintf(codec->diag, "aom_codec_enc_config_default() failed: %s", aom_codec_err_to_string(err));
-                return AVIF_RESULT_UNKNOWN_ERROR;
-            }
+        encoderInterface = aom_codec_av1_cx();
+        aom_codec_err_t err = aom_codec_enc_config_default(encoderInterface, cfg, aomUsage);
+        if (err != AOM_CODEC_OK) {
+            avifDiagnosticsPrintf(codec->diag, "aom_codec_enc_config_default() failed: %s", aom_codec_err_to_string(err));
+            return AVIF_RESULT_UNKNOWN_ERROR;
         }
 
         // Set our own default cfg->rc_end_usage value, which may differ from libaom's default.
@@ -666,8 +665,7 @@
         cfg->g_profile = seqProfile;
         cfg->g_bit_depth = image->depth;
         cfg->g_input_bit_depth = image->depth;
-        cfg->g_w = image->width;
-        cfg->g_h = image->height;
+
         if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) {
             // Set the maximum number of frames to encode to 1. This instructs
             // libaom to set still_picture and reduced_still_picture_header to
@@ -690,16 +688,6 @@
             cfg->g_threads = encoder->maxThreads;
         }
 
-        int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63);
-        int maxQuantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63);
-        if (alpha) {
-            minQuantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63);
-            maxQuantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63);
-        }
-        avifBool lossless = ((minQuantizer == AVIF_QUANTIZER_LOSSLESS) && (maxQuantizer == AVIF_QUANTIZER_LOSSLESS));
-        cfg->rc_min_quantizer = minQuantizer;
-        cfg->rc_max_quantizer = maxQuantizer;
-
         codec->internal->monochromeEnabled = AVIF_FALSE;
         if (aomVersion > aomVersion_2_0_0) {
             // There exists a bug in libaom's chroma_check() function where it will attempt to
@@ -715,76 +703,126 @@
                 cfg->monochrome = 1;
             }
         }
+    }
+
+    avifBool dimensionsChanged = AVIF_FALSE;
+    if (!codec->internal->encoderInitialized) {
+        cfg->g_w = image->width;
+        cfg->g_h = image->height;
+    } else if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) {
+        // We are not ready for dimension change for now.
+        return AVIF_RESULT_NOT_IMPLEMENTED;
+    }
+
+    if (!codec->internal->encoderInitialized || encoderChanges) {
+        int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63);
+        int maxQuantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63);
+        if (alpha) {
+            minQuantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63);
+            maxQuantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63);
+        }
+        lossless = ((minQuantizer == AVIF_QUANTIZER_LOSSLESS) && (maxQuantizer == AVIF_QUANTIZER_LOSSLESS));
+        cfg->rc_min_quantizer = minQuantizer;
+        cfg->rc_max_quantizer = maxQuantizer;
+    }
+
+    if (!codec->internal->encoderInitialized) {
+        aom_codec_flags_t encoderFlags = 0;
+        if (image->depth > 8) {
+            encoderFlags |= AOM_CODEC_USE_HIGHBITDEPTH;
+        }
 
         if (!avifProcessAOMOptionsPreInit(codec, alpha, cfg)) {
             return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION;
         }
 
-        if (!codec->internal->encoderInitialized) {
-            aom_codec_flags_t encoderFlags = 0;
-            if (image->depth > 8) {
-                encoderFlags |= AOM_CODEC_USE_HIGHBITDEPTH;
-            }
-
-            if (aom_codec_enc_init(&codec->internal->encoder, encoderInterface, cfg, encoderFlags) != AOM_CODEC_OK) {
-                avifDiagnosticsPrintf(codec->diag,
-                                      "aom_codec_enc_init() failed: %s: %s",
-                                      aom_codec_error(&codec->internal->encoder),
-                                      aom_codec_error_detail(&codec->internal->encoder));
-                return AVIF_RESULT_UNKNOWN_ERROR;
-            }
-            codec->internal->encoderInitialized = AVIF_TRUE;
-        } else {
-            if (aom_codec_enc_config_set(&codec->internal->encoder, cfg) != AOM_CODEC_OK) {
-                avifDiagnosticsPrintf(codec->diag,
-                                      "aom_codec_enc_config_set() failed: %s: %s",
-                                      aom_codec_error(&codec->internal->encoder),
-                                      aom_codec_error_detail(&codec->internal->encoder));
-                return AVIF_RESULT_UNKNOWN_ERROR;
-            }
+        if (aom_codec_enc_init(&codec->internal->encoder, encoderInterface, cfg, encoderFlags) != AOM_CODEC_OK) {
+            avifDiagnosticsPrintf(codec->diag,
+                                  "aom_codec_enc_init() failed: %s: %s",
+                                  aom_codec_error(&codec->internal->encoder),
+                                  aom_codec_error_detail(&codec->internal->encoder));
+            return AVIF_RESULT_UNKNOWN_ERROR;
         }
 
-        if (lossless) {
-            aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, 1);
-        }
         if (encoder->maxThreads > 1) {
             aom_codec_control(&codec->internal->encoder, AV1E_SET_ROW_MT, 1);
         }
-        if (encoder->tileRowsLog2 != 0) {
-            int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6);
-            aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2);
-        }
-        if (encoder->tileColsLog2 != 0) {
-            int tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6);
-            aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2);
-        }
         if (aomCpuUsed != -1) {
             if (aom_codec_control(&codec->internal->encoder, AOME_SET_CPUUSED, aomCpuUsed) != AOM_CODEC_OK) {
                 return AVIF_RESULT_UNKNOWN_ERROR;
             }
         }
+    } else if ((encoderChanges & ~AVIF_ENCODER_CHANGE_CODEC_SPECIFIC) || dimensionsChanged) {
+        // Codec specific options does not change cfg, so no need to update it.
+        aom_codec_err_t err = aom_codec_enc_config_set(&codec->internal->encoder, cfg);
+        if (err != AOM_CODEC_OK) {
+            avifDiagnosticsPrintf(codec->diag,
+                                  "aom_codec_enc_config_set() failed: %s: %s",
+                                  aom_codec_error(&codec->internal->encoder),
+                                  aom_codec_error_detail(&codec->internal->encoder));
+            return AVIF_RESULT_UNKNOWN_ERROR;
+        }
+    }
+
+    if (!codec->internal->encoderInitialized || (encoderChanges & AVIF_ENCODER_CHANGE_CODEC_SPECIFIC)) {
         if (!avifProcessAOMOptionsPostInit(codec, alpha)) {
             return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION;
         }
-#if defined(AOM_USAGE_ALL_INTRA)
-        if (aomUsage == AOM_USAGE_ALL_INTRA && !codec->internal->endUsageSet && !codec->internal->cqLevelSet) {
-            // The default rc_end_usage in all intra mode is AOM_Q, which requires cq-level to
-            // function. A libavif user may not know this internal detail and therefore may only
-            // set the min and max quantizers in the avifEncoder struct. If this is the case, set
-            // cq-level to a reasonable value for the user, otherwise the default cq-level
-            // (currently 10) will be unknowingly used.
-            assert(cfg->rc_end_usage == AOM_Q);
-            unsigned int cqLevel = (cfg->rc_min_quantizer + cfg->rc_max_quantizer) / 2;
-            aom_codec_control(&codec->internal->encoder, AOME_SET_CQ_LEVEL, cqLevel);
+    }
+
+    avifBool quantizerUpdated = AVIF_FALSE;
+    if (!codec->internal->encoderInitialized) {
+        quantizerUpdated = AVIF_TRUE;
+        if (lossless) {
+            aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless);
         }
-#endif
+        int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6);
+        if (tileRowsLog2 > 0) {
+            aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2);
+        }
+        int tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6);
+        if (tileColsLog2 > 0) {
+            aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2);
+        }
         if (!codec->internal->tuningSet) {
             if (aom_codec_control(&codec->internal->encoder, AOME_SET_TUNING, AOM_TUNE_SSIM) != AOM_CODEC_OK) {
                 return AVIF_RESULT_UNKNOWN_ERROR;
             }
         }
+        codec->internal->encoderInitialized = AVIF_TRUE;
+    } else if (encoderChanges) {
+        if (alpha) {
+            if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA | AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA)) {
+                quantizerUpdated = AVIF_TRUE;
+                aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless);
+            }
+        } else {
+            if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER | AVIF_ENCODER_CHANGE_MAX_QUANTIZER)) {
+                quantizerUpdated = AVIF_TRUE;
+                aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless);
+            }
+        }
+        if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2) {
+            aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, AVIF_CLAMP(encoder->tileRowsLog2, 0, 6));
+        }
+        if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_COLS_LOG2) {
+            aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, AVIF_CLAMP(encoder->tileColsLog2, 0, 6));
+        }
     }
 
+#if defined(AOM_USAGE_ALL_INTRA)
+    if (aomUsage == AOM_USAGE_ALL_INTRA && !codec->internal->endUsageSet && !codec->internal->cqLevelSet && quantizerUpdated) {
+        // The default rc_end_usage in all intra mode is AOM_Q, which requires cq-level to
+        // function. A libavif user may not know this internal detail and therefore may only
+        // set the min and max quantizers in the avifEncoder struct. If this is the case, set
+        // cq-level to a reasonable value for the user, otherwise the default cq-level
+        // (currently 10) will be unknowingly used.
+        assert(cfg->rc_end_usage == AOM_Q);
+        unsigned int cqLevel = (cfg->rc_min_quantizer + cfg->rc_max_quantizer) / 2;
+        aom_codec_control(&codec->internal->encoder, AOME_SET_CQ_LEVEL, cqLevel);
+    }
+#endif
+
     aom_image_t aomImage;
     // We prefer to simply set the aomImage.planes[] pointers to the plane buffers in 'image'. When
     // doing this, we set aomImage.w equal to aomImage.d_w and aomImage.h equal to aomImage.d_h and
diff --git a/src/codec_rav1e.c b/src/codec_rav1e.c
index f8b301a..ee68268 100644
--- a/src/codec_rav1e.c
+++ b/src/codec_rav1e.c
@@ -12,6 +12,8 @@
     RaContext * rav1eContext;
     RaChromaSampling chromaSampling;
     int yShift;
+    uint32_t encodeWidth;
+    uint32_t encodeHeight;
 };
 
 static void rav1eCodecDestroyInternal(avifCodec * codec)
@@ -51,11 +53,20 @@
                                         avifEncoder * encoder,
                                         const avifImage * image,
                                         avifBool alpha,
-                                        avifBool updateConfig,
+                                        avifEncoderChanges updatedConfig,
                                         uint32_t addImageFlags,
                                         avifCodecEncodeOutput * output)
 {
-    if (updateConfig) {
+    // rav1e does not support changing config.
+    if (updatedConfig) {
+        return AVIF_RESULT_NOT_IMPLEMENTED;
+    }
+
+    // rav1e does not support changing encoding dimension.
+    if (!codec->internal->rav1eContext) {
+        codec->internal->encodeWidth = image->width;
+        codec->internal->encodeHeight = image->height;
+    } else if ((codec->internal->encodeWidth != image->width) || (codec->internal->encodeHeight != image->height)) {
         return AVIF_RESULT_NOT_IMPLEMENTED;
     }
 
diff --git a/src/codec_svt.c b/src/codec_svt.c
index f45ccf1..f6a4c18 100644
--- a/src/codec_svt.c
+++ b/src/codec_svt.c
@@ -46,14 +46,22 @@
                                       avifEncoder * encoder,
                                       const avifImage * image,
                                       avifBool alpha,
-                                      avifBool updateConfig,
+                                      avifEncoderChanges updatedConfig,
                                       uint32_t addImageFlags,
                                       avifCodecEncodeOutput * output)
 {
-    if (updateConfig) {
+    // svt does not support changing config.
+    if (updatedConfig) {
         return AVIF_RESULT_NOT_IMPLEMENTED;
     }
 
+    // svt does not support changing encoding dimension.
+    if (codec->internal->svt_encoder != NULL) {
+        if ((codec->internal->svt_config.source_width != image->width) || (codec->internal->svt_config.source_height != image->height)) {
+            return AVIF_RESULT_NOT_IMPLEMENTED;
+        }
+    }
+
     avifResult result = AVIF_RESULT_UNKNOWN_ERROR;
     EbColorFormat color_format = EB_YUV420;
     EbBufferHeaderType * input_buffer = NULL;
diff --git a/src/write.c b/src/write.c
index 0083dcf..5cfcff9 100644
--- a/src/write.c
+++ b/src/write.c
@@ -128,7 +128,6 @@
     uint16_t primaryItemID;
     avifBool singleImage; // if true, the AVIF_ADD_IMAGE_FLAG_SINGLE flag was set on the first call to avifEncoderAddImage()
     avifBool alphaPresent;
-    avifBool csOptionsUpdated;
 } avifEncoderData;
 
 static void avifEncoderDataDestroy(avifEncoderData * data);
@@ -319,7 +318,6 @@
 void avifEncoderSetCodecSpecificOption(avifEncoder * encoder, const char * key, const char * value)
 {
     avifCodecSpecificOptionsSet(encoder->csOptions, key, value);
-    encoder->data->csOptionsUpdated = AVIF_TRUE; // False positive is possible but not important.
 }
 
 static void avifBackupSettings(avifEncoder * encoder)
@@ -339,13 +337,12 @@
     lastEncoder->tileRowsLog2 = encoder->tileRowsLog2;
     lastEncoder->tileColsLog2 = encoder->tileColsLog2;
     lastEncoder->speed = encoder->speed;
-    encoder->data->csOptionsUpdated = AVIF_FALSE;
 }
 
 // This function detect changes made on avifEncoder.
 // It reports if the change is valid, i.e. if any setting that can't change was changed.
-// It also sets needUpdate to true if valid changes are detected.
-static avifBool avifEncoderSettingsChanged(const avifEncoder * encoder, avifBool * needUpdate)
+// It also reports detected changes in updatedConfig.
+static avifBool avifEncoderSettingsChanged(const avifEncoder * encoder, avifEncoderChanges * encoderChanges)
 {
     const avifEncoder * lastEncoder = &encoder->data->lastEncoder;
 
@@ -354,16 +351,32 @@
     }
 
     if ((lastEncoder->codecChoice != encoder->codecChoice) || (lastEncoder->keyframeInterval != encoder->keyframeInterval) ||
-        (lastEncoder->timescale != encoder->timescale)) {
+        (lastEncoder->timescale != encoder->timescale) || (lastEncoder->maxThreads != encoder->maxThreads) ||
+        (lastEncoder->speed != encoder->speed)) {
         return AVIF_FALSE;
     }
 
-    if ((lastEncoder->maxThreads != encoder->maxThreads) || (lastEncoder->minQuantizer != encoder->minQuantizer) ||
-        (lastEncoder->maxQuantizer != encoder->maxQuantizer) || (lastEncoder->minQuantizerAlpha != encoder->minQuantizerAlpha) ||
-        (lastEncoder->maxQuantizerAlpha != encoder->maxQuantizerAlpha) || (lastEncoder->tileRowsLog2 != encoder->tileRowsLog2) ||
-        (lastEncoder->tileColsLog2 != encoder->tileColsLog2) || (lastEncoder->speed != encoder->speed) ||
-        (encoder->data->csOptionsUpdated)) {
-        *needUpdate = AVIF_TRUE;
+    *encoderChanges = 0;
+    if ((lastEncoder->minQuantizer != encoder->minQuantizer)) {
+        *encoderChanges |= AVIF_ENCODER_CHANGE_MIN_QUANTIZER;
+    }
+    if ((lastEncoder->maxQuantizer != encoder->maxQuantizer)) {
+        *encoderChanges |= AVIF_ENCODER_CHANGE_MAX_QUANTIZER;
+    }
+    if ((lastEncoder->minQuantizerAlpha != encoder->minQuantizerAlpha)) {
+        *encoderChanges |= AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA;
+    }
+    if ((lastEncoder->maxQuantizerAlpha != encoder->maxQuantizerAlpha)) {
+        *encoderChanges |= AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA;
+    }
+    if ((lastEncoder->tileRowsLog2 != encoder->tileRowsLog2)) {
+        *encoderChanges |= AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2;
+    }
+    if ((lastEncoder->tileColsLog2 != encoder->tileColsLog2)) {
+        *encoderChanges |= AVIF_ENCODER_CHANGE_TILE_COLS_LOG2;
+    }
+    if (encoder->csOptions->count > 0) {
+        *encoderChanges |= AVIF_ENCODER_CHANGE_CODEC_SPECIFIC;
     }
 
     return AVIF_TRUE;
@@ -656,8 +669,8 @@
         return AVIF_RESULT_NO_CODEC_AVAILABLE;
     }
 
-    avifBool updateConfig = AVIF_FALSE;
-    if (!avifEncoderSettingsChanged(encoder, &updateConfig)) {
+    avifEncoderChanges encoderChanges = 0;
+    if (!avifEncoderSettingsChanged(encoder, &encoderChanges)) {
         return AVIF_RESULT_CANNOT_CHANGE_SETTING;
     }
     avifBackupSettings(encoder);
@@ -863,19 +876,11 @@
         // Another frame in an image sequence
 
         const avifImage * imageMetadata = encoder->data->imageMetadata;
-        // HEIF (ISO 23008-12:2017), Section 6.6.2.3.1:
-        //   All input images shall have exactly the same width and height; call those tile_width and tile_height.
-        // MIAF (ISO 23000-22:2019), Section 7.3.11.4.1:
-        //   All input images of a grid image item shall use the same coding format, chroma sampling format, and the
-        //   same decoder configuration (see 7.3.6.2).
-        // If the first image in the sequence had an alpha plane (even if fully opaque), all
-        // subsequence images must have alpha as well.
-        if ((imageMetadata->width != firstCell->width) || (imageMetadata->height != firstCell->height) ||
-            (imageMetadata->depth != firstCell->depth) || (imageMetadata->yuvFormat != firstCell->yuvFormat) ||
+        // If the first image had an alpha plane (even if fully opaque), all subsequent images must have alpha as well.
+        if ((imageMetadata->depth != firstCell->depth) || (imageMetadata->yuvFormat != firstCell->yuvFormat) ||
             (imageMetadata->yuvRange != firstCell->yuvRange) || (imageMetadata->colorPrimaries != firstCell->colorPrimaries) ||
             (imageMetadata->transferCharacteristics != firstCell->transferCharacteristics) ||
             (imageMetadata->matrixCoefficients != firstCell->matrixCoefficients) ||
-            (!!imageMetadata->alphaPlane != !!firstCell->alphaPlane) ||
             (imageMetadata->alphaPremultiplied != firstCell->alphaPremultiplied) ||
             (encoder->data->alphaPresent && !firstCell->alphaPlane)) {
             return AVIF_RESULT_INCOMPATIBLE_IMAGE;
@@ -894,7 +899,7 @@
         if (item->codec) {
             const avifImage * cellImage = cellImages[item->cellIndex];
             avifResult encodeResult =
-                item->codec->encodeImage(item->codec, encoder, cellImage, item->alpha, updateConfig, addImageFlags, item->encodeOutput);
+                item->codec->encodeImage(item->codec, encoder, cellImage, item->alpha, encoderChanges, addImageFlags, item->encodeOutput);
             if (encodeResult == AVIF_RESULT_UNKNOWN_ERROR) {
                 encodeResult = item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED;
             }
@@ -904,6 +909,7 @@
         }
     }
 
+    avifCodecSpecificOptionsClear(encoder->csOptions);
     avifEncoderFrame * frame = (avifEncoderFrame *)avifArrayPushPtr(&encoder->data->frames);
     frame->durationInTimescales = durationInTimescales;
     return AVIF_RESULT_OK;