Commit ecae45e65 for imagemagick.org

commit ecae45e659793d55ff928f3289796799b65e3a8c
Author: Madars <mad182@gmail.com>
Date:   Thu Apr 9 11:08:09 2026 +0300

    Fix MNG animation speed for sub-frame animations with offsets (#8666)

    Skip writing FRAM mode 4 disposal chunks in WriteOnePNGImage when
    DEFI handles frame positioning. The extra FRAM chunks with delay=0
    were conflicting with the FRAM mode 3 already set by WriteMNGImage,
    causing incorrect playback speed.

diff --git a/coders/png.c b/coders/png.c
index 97dacd8fa..efd13412b 100644
--- a/coders/png.c
+++ b/coders/png.c
@@ -697,6 +697,7 @@ typedef struct _MngWriteInfo
     have_global_plte,
     have_global_srgb,
     is_palette,
+    need_defi,
     need_fram,
     preserve_colormap,
     preserve_iCCP,
@@ -11333,9 +11334,10 @@ static MagickBooleanType WriteOnePNGImage(MngWriteInfo *mng_info,
   if (mng_info->need_fram != MagickFalse &&
       (int) image->dispose == BackgroundDispose)
     {
-      if (mng_info->page.x || mng_info->page.y ||
+      if (mng_info->need_defi == MagickFalse &&
+          (mng_info->page.x || mng_info->page.y ||
           (ping_width != mng_info->page.width) ||
-          (ping_height != mng_info->page.height))
+          (ping_height != mng_info->page.height)))
         {
           unsigned char
             chunk[32];
@@ -12888,7 +12890,6 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
   volatile int
     need_local_plte,
     all_images_are_gray,
-    need_defi,
     use_global_plte;

   unsigned char
@@ -13002,7 +13003,7 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
   use_global_plte=MagickFalse;
   all_images_are_gray=MagickFalse;
   need_local_plte=MagickTrue;
-  need_defi=MagickFalse;
+  mng_info->need_defi=MagickFalse;
   need_matte=MagickFalse;
   mng_info->framing_mode=1;
   mng_info->old_framing_mode=1;
@@ -13060,7 +13061,7 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
           }

         if (next_image->page.x || next_image->page.y)
-          need_defi=MagickTrue;
+          mng_info->need_defi=MagickTrue;

         if (next_image->alpha_trait != UndefinedPixelTrait)
           need_matte=MagickTrue;
@@ -13191,7 +13192,7 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
          if (final_delay > 125)
            mng_info->need_fram=MagickTrue;

-         if (need_defi && final_delay > 2 && (final_delay != 4) &&
+         if (mng_info->need_defi && final_delay > 2 && (final_delay != 4) &&
             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
             (final_delay != 25) && (final_delay != 50) &&
             (final_delay != (size_t) image->ticks_per_second))
@@ -13222,7 +13223,7 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
        {
          if (need_matte)
            {
-             if (need_defi || mng_info->need_fram != MagickFalse || use_global_plte)
+             if (mng_info->need_defi || mng_info->need_fram != MagickFalse || use_global_plte)
                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */

              else
@@ -13231,7 +13232,7 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,

          else
            {
-             if (need_defi || mng_info->need_fram != MagickFalse || use_global_plte)
+             if (mng_info->need_defi || mng_info->need_fram != MagickFalse || use_global_plte)
                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */

              else
@@ -13243,7 +13244,7 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
        {
          if (need_matte)
            {
-             if (need_defi || mng_info->need_fram != MagickFalse || use_global_plte)
+             if (mng_info->need_defi || mng_info->need_fram != MagickFalse || use_global_plte)
                PNGLong(chunk+28,11L);    /* simplicity=LC */

              else
@@ -13252,7 +13253,7 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,

          else
            {
-             if (need_defi || mng_info->need_fram != MagickFalse || use_global_plte)
+             if (mng_info->need_defi || mng_info->need_fram != MagickFalse || use_global_plte)
                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */

              else
@@ -13549,7 +13550,7 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
         else
           mng_info->have_global_plte=MagickFalse;
       }
-    if (need_defi)
+    if (mng_info->need_defi)
       {
         ssize_t
           previous_x,