Commit 2b39b7598 for imagemagick.org
commit 2b39b75987d293616408c6ee3aed8b1f66ebd172
Author: Madars <mad182@gmail.com>
Date: Sat Mar 21 14:27:07 2026 +0200
Fix animated JXL frame delay handling (#8622)
Read per-frame duration from JxlFrameHeader during decode by calling
JxlDecoderGetFrameHeader() in the JXL_DEC_FRAME handler, setting
image->delay from frame_header.duration.
Write per-frame delay from image->delay during encode instead of
hardcoding frame_header.duration to 1. This preserves the original
animation timing when resizing or editing animated JXL files.
diff --git a/coders/jxl.c b/coders/jxl.c
index a1518d970..971b4b6d0 100644
--- a/coders/jxl.c
+++ b/coders/jxl.c
@@ -626,6 +626,9 @@ static Image *ReadJXLImage(const ImageInfo *image_info,
}
case JXL_DEC_FRAME:
{
+ JxlFrameHeader
+ frame_header;
+
if (image_count++ != 0)
{
JXLAddProfilesToImage(image,&exif_profile,&xmp_profile,exception);
@@ -638,6 +641,9 @@ static Image *ReadJXLImage(const ImageInfo *image_info,
image=SyncNextImageInList(image);
JXLInitImage(image,&basic_info);
}
+ (void) memset(&frame_header,0,sizeof(frame_header));
+ if (JxlDecoderGetFrameHeader(jxl_info,&frame_header) == JXL_DEC_SUCCESS)
+ image->delay=(size_t) frame_header.duration;
break;
}
case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
@@ -1070,7 +1076,6 @@ static MagickBooleanType WriteJXLImage(const ImageInfo *image_info,Image *image,
basic_info.animation.tps_numerator=(uint32_t) image->ticks_per_second;
basic_info.animation.tps_denominator=1;
JxlEncoderInitFrameHeader(&frame_header);
- frame_header.duration=1;
}
jxl_status=JxlEncoderSetBasicInfo(jxl_info,&basic_info);
if (jxl_status != JXL_ENC_SUCCESS)
@@ -1177,6 +1182,7 @@ static MagickBooleanType WriteJXLImage(const ImageInfo *image_info,Image *image,
if (basic_info.have_animation == JXL_TRUE)
{
+ frame_header.duration=(uint32_t) image->delay;
jxl_status=JxlEncoderSetFrameHeader(frame_settings,&frame_header);
if (jxl_status != JXL_ENC_SUCCESS)
break;