Commit 2ac118e9a for imagemagick.org

commit 2ac118e9a637b214602b3b20064566ad37b1a4b6
Author: Madars <mad182@gmail.com>
Date:   Thu Jun 18 13:48:22 2026 +0300

    heic: add primary still image to animated AVIF for Firefox compatibility (#8804)

    Animated AVIF sequences written by ImageMagick were missing a meta box
    with a primary item (pitm).  Firefox and some other software requires
    this to properly decode animated AVIF (AVIS) files.

    After encoding the sequence track, encode the first frame as a primary
    still image via heif_context_encode_image() to generate the meta box
    with pitm and iprp.  This produces a dual-format file that works both
    as a still image container and as a sequence, matching the structure
    produced by compliant encoders.

diff --git a/coders/heic.c b/coders/heic.c
index 3b06ed48e..eb6a99bfd 100644
--- a/coders/heic.c
+++ b/coders/heic.c
@@ -1872,6 +1872,66 @@ static MagickBooleanType WriteHEICSequenceImage(const ImageInfo *image_info,
       error=heif_track_encode_end_of_sequence(track,heif_encoder);
       if (IsHEIFSuccess(image,&error,exception) == MagickFalse)
         status=MagickFalse;
+      /*
+        Encode the first frame as a primary still image to generate the meta
+        box with pitm (primary item).  Firefox and some other software requires
+        this to decode animated AVIF files.
+      */
+      if (status != MagickFalse)
+        {
+          Image
+            *first_image;
+
+          struct heif_image
+            *still_image;
+
+          first_image=GetFirstImageInList(image);
+          colorspace=heif_colorspace_YCbCr;
+          chroma=lossless != MagickFalse ? heif_chroma_444 : heif_chroma_420;
+          if ((first_image->alpha_trait & BlendPixelTrait) != 0)
+            {
+              colorspace=heif_colorspace_RGB;
+              chroma=heif_chroma_interleaved_RGBA;
+              if (first_image->depth > 8)
+                chroma=heif_chroma_interleaved_RRGGBBAA_LE;
+            }
+          else
+            if (IssRGBCompatibleColorspace(first_image->colorspace) !=
+                MagickFalse)
+              {
+                colorspace=heif_colorspace_RGB;
+                chroma=heif_chroma_interleaved_RGB;
+                if (first_image->depth > 8)
+                  chroma=heif_chroma_interleaved_RRGGBB_LE;
+                if (GetPixelChannels(first_image) == 1)
+                  {
+                    colorspace=heif_colorspace_monochrome;
+                    chroma=heif_chroma_monochrome;
+                  }
+              }
+          still_image=(struct heif_image *) NULL;
+          error=heif_image_create((int) first_image->columns,
+            (int) first_image->rows,colorspace,chroma,&still_image);
+          if (IsHEIFSuccess(image,&error,exception) != MagickFalse)
+            {
+              if (colorspace == heif_colorspace_YCbCr)
+                status=WriteHEICImageYCbCr(first_image,still_image,exception);
+              else
+                if (first_image->depth > 8)
+                  status=WriteHEICImageRRGGBBAA(first_image,still_image,
+                    exception);
+                else
+                  status=WriteHEICImageRGBA(first_image,still_image,exception);
+              if (status != MagickFalse)
+                {
+                  error=heif_context_encode_image(heif_context,still_image,
+                    heif_encoder,(struct heif_encoding_options *) NULL,
+                    (struct heif_image_handle **) NULL);
+                  status=IsHEIFSuccess(image,&error,exception);
+                }
+              heif_image_release(still_image);
+            }
+        }
       if (status != MagickFalse)
         {
           writer.writer_api_version=1;