Make avifEncoder.headerFormat a flag combination (#2612)

Rename AVIF_HEADER_FULL to AVIF_HEADER_DEFAULT.
Deprecate AVIF_HEADER_FULL.
Rename AVIF_HEADER_REDUCED to AVIF_HEADER_MINI.
Rename AVIF_HEADER_FULL_WITH_EXTENDED_PIXI to
AVIF_HEADER_EXTENDED_PIXI.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8893cd4..1454271 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,7 +32,7 @@
   is specified. https://github.com/AOMediaCodec/libavif/issues/2365.
 * Rename AVIF_ENABLE_EXPERIMENTAL_METAV1 to AVIF_ENABLE_EXPERIMENTAL_MINI and
   update the experimental reduced header feature to the latest specification
-  draft.
+  draft. Rename AVIF_HEADER_REDUCED to AVIF_HEADER_MINI.
 * Update the experimental Sample Transform feature behind the
   AVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM CMake flag to the latest
   specification draft.
@@ -76,6 +76,8 @@
 * android_jni: Set threads to 2 instead of CPU count
 * Fix overflows when dealing with alpha during YUV/RGB conversions and in
   avifRGBImageAllocatePixels().
+* Make avifEncoder.headerFormat a flag combination for future features.
+* Rename AVIF_HEADER_FULL to AVIF_HEADER_DEFAULT. Deprecate AVIF_HEADER_FULL.
 
 ## [1.1.1] - 2024-07-30
 
diff --git a/apps/avifenc.c b/apps/avifenc.c
index e1fba28..2511826 100644
--- a/apps/avifenc.c
+++ b/apps/avifenc.c
@@ -40,7 +40,7 @@
     avifBool layered;     // manual layered encoding by specifying each layer
     int layers;
     int speed;
-    avifHeaderFormat headerFormat;
+    avifHeaderFormatFlags headerFormat;
 
     avifBool paspPresent;
     uint32_t paspValues[2];
@@ -1452,7 +1452,7 @@
     settings.layered = AVIF_FALSE;
     settings.layers = 0;
     settings.speed = 6;
-    settings.headerFormat = AVIF_HEADER_FULL;
+    settings.headerFormat = AVIF_HEADER_DEFAULT;
     settings.repetitionCount = AVIF_REPETITION_COUNT_INFINITE;
     settings.keyframeInterval = 0;
     settings.ignoreExif = AVIF_FALSE;
@@ -1558,7 +1558,7 @@
             outputFilename = arg;
 #if defined(AVIF_ENABLE_EXPERIMENTAL_MINI)
         } else if (!strcmp(arg, "--mini")) {
-            settings.headerFormat = AVIF_HEADER_REDUCED;
+            settings.headerFormat |= AVIF_HEADER_MINI;
 #endif // AVIF_ENABLE_EXPERIMENTAL_MINI
         } else if (!strcmp(arg, "-d") || !strcmp(arg, "--depth")) {
             NEXTARG();
diff --git a/include/avif/avif.h b/include/avif/avif.h
index c0cc018..7f0f101 100644
--- a/include/avif/avif.h
+++ b/include/avif/avif.h
@@ -210,22 +210,29 @@
 // ---------------------------------------------------------------------------
 // avifHeaderFormat
 
+// Bit flag for selecting container strategies when encoding an image.
 typedef enum avifHeaderFormat
 {
-    // AVIF file with an "avif" brand, a MetaBox and all its required boxes for maximum compatibility.
-    AVIF_HEADER_FULL,
+    AVIF_HEADER_DEFAULT = 0x0,
 #if defined(AVIF_ENABLE_EXPERIMENTAL_MINI)
     // AVIF file with a "mif3" brand and a MinimizedImageBox to reduce the encoded file size.
     // This is based on the w24144 "Low-overhead image file format" MPEG proposal for HEIF.
     // WARNING: Experimental feature. Produces files that are incompatible with older decoders.
-    AVIF_HEADER_REDUCED,
+    // If this flag is omitted or if MinimizedImageBox cannot be used at encoding, falls back to an
+    // AVIF file with an "avif" brand, a MetaBox and all its required boxes for maximum compatibility.
+    AVIF_HEADER_MINI = 0x1,
 #endif
 #if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI)
     // Use the full syntax of the PixelInformationProperty from HEIF 3rd edition Amendment 2.
     // WARNING: Experimental feature. Produces files that may be incompatible with older decoders.
-    AVIF_HEADER_FULL_WITH_EXTENDED_PIXI,
+    // Only relevant if a MetaBox is used. No effect if a MinimizedImageBox is used.
+    AVIF_HEADER_EXTENDED_PIXI = 0x2,
 #endif
+
+    // Deprecated.
+    AVIF_HEADER_FULL = AVIF_HEADER_DEFAULT,
 } avifHeaderFormat;
+typedef int avifHeaderFormatFlags;
 
 // ---------------------------------------------------------------------------
 // avifROData/avifRWData: Generic raw memory storage
@@ -1571,8 +1578,8 @@
     // Version 1.0.0 ends here.
     // --------------------------------------------------------------------------------------------
 
-    // Defaults to AVIF_HEADER_FULL
-    avifHeaderFormat headerFormat; // Changeable encoder setting.
+    // Defaults to AVIF_HEADER_DEFAULT
+    avifHeaderFormatFlags headerFormat; // Changeable encoder setting.
 
     // Version 1.1.0 ends here.
     // --------------------------------------------------------------------------------------------
diff --git a/src/write.c b/src/write.c
index ed85927..346634e 100644
--- a/src/write.c
+++ b/src/write.c
@@ -493,7 +493,7 @@
         avifEncoderDestroy(encoder);
         return NULL;
     }
-    encoder->headerFormat = AVIF_HEADER_FULL;
+    encoder->headerFormat = AVIF_HEADER_DEFAULT;
 #if defined(AVIF_ENABLE_EXPERIMENTAL_SAMPLE_TRANSFORM)
     encoder->sampleTransformRecipe = AVIF_SAMPLE_TRANSFORM_NONE;
 #endif
@@ -2986,7 +2986,7 @@
             avifBoxMarker pixi;
             uint32_t flags = 0;
 #if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI)
-            if (encoder->headerFormat == AVIF_HEADER_FULL_WITH_EXTENDED_PIXI) {
+            if (encoder->headerFormat & AVIF_HEADER_EXTENDED_PIXI) {
                 flags |= 1;
             }
 #endif // AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI
@@ -3209,7 +3209,7 @@
 
 #if defined(AVIF_ENABLE_EXPERIMENTAL_MINI)
     // Decide whether to go for a reduced MinimizedImageBox or a full regular MetaBox.
-    if ((encoder->headerFormat == AVIF_HEADER_REDUCED) && avifEncoderIsMiniCompatible(encoder)) {
+    if ((encoder->headerFormat & AVIF_HEADER_MINI) && avifEncoderIsMiniCompatible(encoder)) {
         AVIF_CHECKRES(avifEncoderWriteFileTypeBoxAndMiniBox(encoder, output));
         return AVIF_RESULT_OK;
     }
diff --git a/tests/gtest/avifavmminitest.cc b/tests/gtest/avifavmminitest.cc
index 5972c53..e2a11ce 100644
--- a/tests/gtest/avifavmminitest.cc
+++ b/tests/gtest/avifavmminitest.cc
@@ -30,7 +30,7 @@
   EncoderPtr encoder(avifEncoderCreate());
   ASSERT_NE(encoder, nullptr);
   encoder->codecChoice = AVIF_CODEC_CHOICE_AVM;
-  encoder->headerFormat = AVIF_HEADER_REDUCED;
+  encoder->headerFormat = AVIF_HEADER_MINI;
   testutil::AvifRwData encoded;
   ASSERT_EQ(avifEncoderWrite(encoder.get(), image.get(), &encoded),
             AVIF_RESULT_OK);
@@ -99,7 +99,7 @@
 
   EncoderPtr encoder(avifEncoderCreate());
   ASSERT_NE(encoder, nullptr);
-  encoder->headerFormat = AVIF_HEADER_REDUCED;
+  encoder->headerFormat = AVIF_HEADER_MINI;
   testutil::AvifRwData encoded;
   ASSERT_EQ(avifEncoderWrite(encoder.get(), image.get(), &encoded),
             AVIF_RESULT_OK);
diff --git a/tests/gtest/avifminitest.cc b/tests/gtest/avifminitest.cc
index 777a1f0..32e4620 100644
--- a/tests/gtest/avifminitest.cc
+++ b/tests/gtest/avifminitest.cc
@@ -82,7 +82,7 @@
   EncoderPtr encoder(avifEncoderCreate());
   ASSERT_NE(encoder, nullptr);
   encoder->speed = AVIF_SPEED_FASTEST;
-  encoder->headerFormat = AVIF_HEADER_REDUCED;
+  encoder->headerFormat = AVIF_HEADER_MINI;
   ASSERT_EQ(avifEncoderWrite(encoder.get(), image.get(), &encoded_mini),
             AVIF_RESULT_OK);
 
diff --git a/tests/gtest/avifpixitest.cc b/tests/gtest/avifpixitest.cc
index 3c1169d..4e94caf 100644
--- a/tests/gtest/avifpixitest.cc
+++ b/tests/gtest/avifpixitest.cc
@@ -31,7 +31,7 @@
   EncoderPtr encoder_regular_pixi(avifEncoderCreate());
   ASSERT_NE(encoder_regular_pixi, nullptr);
   encoder_regular_pixi->speed = AVIF_SPEED_FASTEST;
-  encoder_regular_pixi->headerFormat = AVIF_HEADER_FULL;
+  encoder_regular_pixi->headerFormat = AVIF_HEADER_DEFAULT;
   ASSERT_EQ(avifEncoderWrite(encoder_regular_pixi.get(), image.get(),
                              &encoded_regular_pixi),
             AVIF_RESULT_OK);
@@ -40,7 +40,7 @@
   EncoderPtr encoder_extended_pixi(avifEncoderCreate());
   ASSERT_NE(encoder_extended_pixi, nullptr);
   encoder_extended_pixi->speed = AVIF_SPEED_FASTEST;
-  encoder_extended_pixi->headerFormat = AVIF_HEADER_FULL_WITH_EXTENDED_PIXI;
+  encoder_extended_pixi->headerFormat = AVIF_HEADER_EXTENDED_PIXI;
   ASSERT_EQ(avifEncoderWrite(encoder_extended_pixi.get(), image.get(),
                              &encoded_extended_pixi),
             AVIF_RESULT_OK);
@@ -89,6 +89,24 @@
             AVIF_CHROMA_SAMPLE_POSITION_VERTICAL);
 }
 
+TEST(AvifHeaderFormatTest, ABI) {
+  // avifEncoder::headerFormat was of type avifHeaderFormat in libavif
+  // version 1.1.1:
+  //   https://github.com/AOMediaCodec/libavif/blob/v1.1.1/include/avif/avif.h#L1498
+  // It was later changed to avifHeaderFormatFlags to be able to combine
+  // multiple avifHeaderFormat features. Check that it was not an ABI
+  // incompatible change.
+  EXPECT_EQ(sizeof(avifEncoder::headerFormat), sizeof(avifHeaderFormat));
+
+#if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI)
+  // Check that the field can be assigned with a combination of flags without
+  // compile errors:
+  EncoderPtr encoder(avifEncoderCreate());
+  ASSERT_NE(encoder, nullptr);
+  encoder->headerFormat = AVIF_HEADER_DEFAULT | AVIF_HEADER_EXTENDED_PIXI;
+#endif  // AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI
+}
+
 //------------------------------------------------------------------------------
 
 }  // namespace