From 1c7767fc5f822c6edc104c1220d523e96fa20b5a Mon Sep 17 00:00:00 2001
From: Dirk Lemstra <dirk@lemstra.org>
Date: Thu, 9 Apr 2026 18:41:09 +0200
Subject: [PATCH] Patch to correct the sample size for 16 bit floats in the JXL
 encoder (GHSA-jvgr-9ph5-m8v4)

---
 coders/jxl.c | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

Index: ImageMagick-7.1.1-21/coders/jxl.c
===================================================================
--- ImageMagick-7.1.1-21.orig/coders/jxl.c
+++ ImageMagick-7.1.1-21/coders/jxl.c
@@ -22,7 +22,7 @@
 %  You may not use this file except in compliance with the License.  You may  %
 %  obtain a copy of the License at                                            %
 %                                                                             %
-%    https://imagemagick.org/script/license.php                               %
+%    https://imagemagick.org/license/                                         %
 %                                                                             %
 %  Unless required by applicable law or agreed to in writing, software        %
 %  distributed under the License is distributed on an "AS IS" BASIS,          %
@@ -43,17 +43,20 @@
 #include "MagickCore/blob.h"
 #include "MagickCore/blob-private.h"
 #include "MagickCore/cache.h"
+#include "MagickCore/channel.h"
 #include "MagickCore/colorspace-private.h"
 #include "MagickCore/exception.h"
 #include "MagickCore/exception-private.h"
 #include "MagickCore/image.h"
 #include "MagickCore/image-private.h"
+#include "MagickCore/layer.h"
 #include "MagickCore/list.h"
 #include "MagickCore/magick.h"
 #include "MagickCore/memory_.h"
 #include "MagickCore/monitor.h"
 #include "MagickCore/monitor-private.h"
 #include "MagickCore/option.h"
+#include "MagickCore/profile-private.h"
 #include "MagickCore/property.h"
 #include "MagickCore/resource_.h"
 #include "MagickCore/static.h"
@@ -64,6 +67,7 @@
 #include <jxl/decode.h>
 #include <jxl/encode.h>
 #include <jxl/thread_parallel_runner.h>
+#include <jxl/version.h>
 #endif
 
 /*
@@ -84,7 +88,7 @@ typedef struct MemoryManagerInfo
 */
 static MagickBooleanType
   WriteJXLImage(const ImageInfo *,Image *,ExceptionInfo *);
-
+
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %                                                                             %
@@ -274,7 +278,72 @@ static inline void JXLInitImage(Image *i
     }
 }
 
-static Image *ReadJXLImage(const ImageInfo *image_info,ExceptionInfo *exception)
+static inline MagickBooleanType JXLPatchExifProfile(StringInfo *exif_profile)
+{
+  size_t
+    exif_length;
+
+  StringInfo
+    *snippet;
+
+  unsigned char
+    *exif_datum;
+
+  unsigned int
+    offset=0;
+
+  if (GetStringInfoLength(exif_profile) < 4)
+    return(MagickFalse);
+
+  /*
+    Extract and cache Exif profile.
+  */
+  snippet=SplitStringInfo(exif_profile,4);
+  offset|=(unsigned int) (*(GetStringInfoDatum(snippet)+0)) << 24;
+  offset|=(unsigned int) (*(GetStringInfoDatum(snippet)+1)) << 16;
+  offset|=(unsigned int) (*(GetStringInfoDatum(snippet)+2)) << 8;
+  offset|=(unsigned int) (*(GetStringInfoDatum(snippet)+3)) << 0;
+  snippet=DestroyStringInfo(snippet);
+  /*
+    Strip any EOI marker if payload starts with a JPEG marker.
+  */
+  exif_length=GetStringInfoLength(exif_profile);
+  exif_datum=GetStringInfoDatum(exif_profile);
+  if ((exif_length > 2) && 
+      ((memcmp(exif_datum,"\xff\xd8",2) == 0) ||
+        (memcmp(exif_datum,"\xff\xe1",2) == 0)) &&
+      (memcmp(exif_datum+exif_length-2,"\xff\xd9",2) == 0))
+    SetStringInfoLength(exif_profile,exif_length-2);
+  /*
+    Skip to actual Exif payload.
+  */
+  if (offset < GetStringInfoLength(exif_profile))
+    (void) DestroyStringInfo(SplitStringInfo(exif_profile,offset));
+  return(MagickTrue);
+}
+
+static inline void JXLAddProfilesToImage(Image *image,
+  StringInfo **exif_profile,StringInfo **xmp_profile,ExceptionInfo *exception)
+{
+  if (*exif_profile != (StringInfo *) NULL)
+    {
+      if (JXLPatchExifProfile(*exif_profile) != MagickFalse)
+        {
+          (void) SetImageProfilePrivate(image,*exif_profile,exception);
+          *exif_profile=(StringInfo *) NULL;
+        }
+      else
+        *exif_profile=DestroyStringInfo(*exif_profile);
+    }
+  if (*xmp_profile != (StringInfo *) NULL)
+    {
+      (void) SetImageProfilePrivate(image,*xmp_profile,exception);
+      *xmp_profile=(StringInfo *) NULL;
+    }
+}
+
+static Image *ReadJXLImage(const ImageInfo *image_info,
+  ExceptionInfo *exception)
 {
   Image
     *image;
@@ -337,8 +406,8 @@ static Image *ReadJXLImage(const ImageIn
   /*
     Initialize JXL delegate library.
   */
-  memset(&basic_info,0,sizeof(basic_info));
-  memset(&pixel_format,0,sizeof(pixel_format));
+  (void) memset(&basic_info,0,sizeof(basic_info));
+  (void) memset(&pixel_format,0,sizeof(pixel_format));
   JXLSetMemoryManager(&memory_manager,&memory_manager_info,image,exception);
   jxl_info=JxlDecoderCreate(&memory_manager);
   if (jxl_info == (JxlDecoder *) NULL)
@@ -402,7 +471,7 @@ static Image *ReadJXLImage(const ImageIn
 
         remaining=JxlDecoderReleaseInput(jxl_info);
         if (remaining > 0)
-          memmove(pixels,pixels+input_size-remaining,remaining);
+          (void) memmove(pixels,pixels+input_size-remaining,remaining);
         count=ReadBlob(image,input_size-remaining,pixels+remaining);
         if (count <= 0)
           {
@@ -446,7 +515,7 @@ static Image *ReadJXLImage(const ImageIn
         StringInfo
           *profile;
 
-        memset(&color_encoding,0,sizeof(color_encoding));
+        (void) memset(&color_encoding,0,sizeof(color_encoding));
         JXLSetFormat(image,&pixel_format,exception);
 #if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0,9,0)
         jxl_status=JxlDecoderGetColorAsEncodedProfile(jxl_info,
@@ -536,27 +605,35 @@ static Image *ReadJXLImage(const ImageIn
 #endif
         if (jxl_status != JXL_DEC_SUCCESS)
           break;
-        profile=AcquireStringInfo(profile_size);
-#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0,9,0)
-        jxl_status=JxlDecoderGetColorAsICCProfile(jxl_info,
-          JXL_COLOR_PROFILE_TARGET_ORIGINAL,GetStringInfoDatum(profile),
-          profile_size);
+        profile=AcquireProfileStringInfo("icc",profile_size,exception);
+        if (profile != (StringInfo *) NULL)
+          {
+  #if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0,9,0)
+            jxl_status=JxlDecoderGetColorAsICCProfile(jxl_info,
+              JXL_COLOR_PROFILE_TARGET_ORIGINAL,GetStringInfoDatum(profile),
+              profile_size);
 #else
-        jxl_status=JxlDecoderGetColorAsICCProfile(jxl_info,&pixel_format,
-          JXL_COLOR_PROFILE_TARGET_ORIGINAL,GetStringInfoDatum(profile),
-          profile_size);
+            jxl_status=JxlDecoderGetColorAsICCProfile(jxl_info,&pixel_format,
+              JXL_COLOR_PROFILE_TARGET_ORIGINAL,GetStringInfoDatum(profile),
+              profile_size);
 #endif
-        if (jxl_status == JXL_DEC_SUCCESS)
-          (void) SetImageProfile(image,"icm",profile,exception);
-        profile=DestroyStringInfo(profile);
+            if (jxl_status == JXL_DEC_SUCCESS)
+              (void) SetImageProfilePrivate(image,profile,exception);
+            else
+              profile=DestroyStringInfo(profile);
+          }
         if (jxl_status == JXL_DEC_SUCCESS)
           jxl_status=JXL_DEC_COLOR_ENCODING;
         break;
       }
       case JXL_DEC_FRAME:
       {
+        JxlFrameHeader
+          frame_header;
+
         if (image_count++ != 0)
           {
+            JXLAddProfilesToImage(image,&exif_profile,&xmp_profile,exception);
             /*
               Allocate next image structure.
             */
@@ -566,13 +643,23 @@ static Image *ReadJXLImage(const ImageIn
             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;
+        if ((basic_info.have_animation == JXL_TRUE) &&
+            (basic_info.alpha_bits != 0))
+          image->dispose=BackgroundDispose;
         break;
       }
       case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
       {
         status=SetImageExtent(image,image->columns,image->rows,exception);
         if (status == MagickFalse)
-          break;
+          {
+            jxl_status=JXL_DEC_ERROR;
+            break;
+          }
+        (void) ResetImagePixels(image,exception);
         JXLSetFormat(image,&pixel_format,exception);
         if (extent == 0)
           {
@@ -651,18 +738,28 @@ static Image *ReadJXLImage(const ImageIn
             /*
               Read Exif profile.
             */
-            exif_profile=AcquireStringInfo((size_t) size);
-            jxl_status=JxlDecoderSetBoxBuffer(jxl_info,
-              GetStringInfoDatum(exif_profile),size);
+          if (exif_profile == (StringInfo *) NULL)
+            {
+              exif_profile=AcquireProfileStringInfo("exif",(size_t) size,
+                exception);
+              if (exif_profile != (StringInfo *) NULL)
+                jxl_status=JxlDecoderSetBoxBuffer(jxl_info,
+                  GetStringInfoDatum(exif_profile),(size_t) size);
+            }
           }
         if (LocaleNCompare(type,"xml ",sizeof(type)) == 0)
           {
             /*
               Read XMP profile.
             */
-            xmp_profile=AcquireStringInfo((size_t) size);
-            jxl_status=JxlDecoderSetBoxBuffer(jxl_info,
-              GetStringInfoDatum(xmp_profile),size);
+            if (xmp_profile == (StringInfo *) NULL)
+              {
+                xmp_profile=AcquireProfileStringInfo("xmp",(size_t) size,
+                  exception);
+                if (xmp_profile != (StringInfo *) NULL)
+                  jxl_status=JxlDecoderSetBoxBuffer(jxl_info,
+                    GetStringInfoDatum(xmp_profile),(size_t) size);
+              }
           }
         if (jxl_status == JXL_DEC_SUCCESS)
           jxl_status=JXL_DEC_BOX;
@@ -681,56 +778,7 @@ static Image *ReadJXLImage(const ImageIn
     }
   }
   (void) JxlDecoderReleaseBoxBuffer(jxl_info);
-  if ((exif_profile != (StringInfo *) NULL) &&
-      (GetStringInfoLength(exif_profile) > 4))
-    {
-      size_t
-        exif_length;
-
-      StringInfo
-        *snippet;
-
-      unsigned char
-        *exif_datum;
-
-      unsigned int
-        offset=0;
-
-      /*
-        Extract and cache Exif profile.
-      */
-      snippet=SplitStringInfo(exif_profile,4);
-      offset|=(unsigned int) (*(GetStringInfoDatum(snippet)+0)) << 24;
-      offset|=(unsigned int) (*(GetStringInfoDatum(snippet)+1)) << 16;
-      offset|=(unsigned int) (*(GetStringInfoDatum(snippet)+2)) << 8;
-      offset|=(unsigned int) (*(GetStringInfoDatum(snippet)+3)) << 0;
-      snippet=DestroyStringInfo(snippet);
-      /*
-        Strip any EOI marker if payload starts with a JPEG marker.
-      */
-      exif_length=GetStringInfoLength(exif_profile);
-      exif_datum=GetStringInfoDatum(exif_profile);
-      if ((exif_length > 2) && 
-          ((memcmp(exif_datum,"\xff\xd8",2) == 0) ||
-           (memcmp(exif_datum,"\xff\xe1",2) == 0)) &&
-          (memcmp(exif_datum+exif_length-2,"\xff\xd9",2) == 0))
-        SetStringInfoLength(exif_profile,exif_length-2);
-      /*
-        Skip to actual Exif payload.
-      */
-      if (offset < GetStringInfoLength(exif_profile))
-        (void) DestroyStringInfo(SplitStringInfo(exif_profile,offset));
-      (void) SetImageProfile(image,"exif",exif_profile,exception);
-      exif_profile=DestroyStringInfo(exif_profile);
-    }
-  if (xmp_profile != (StringInfo *) NULL)
-    {
-      /*
-        Cache XMP profile.
-      */
-      (void) SetImageProfile(image,"xmp",xmp_profile,exception);
-      xmp_profile=DestroyStringInfo(xmp_profile);
-    }
+  JXLAddProfilesToImage(image,&exif_profile,&xmp_profile,exception);
   output_buffer=(unsigned char *) RelinquishMagickMemory(output_buffer);
   pixels=(unsigned char *) RelinquishMagickMemory(pixels);
   if (runner != NULL)
@@ -738,7 +786,10 @@ static Image *ReadJXLImage(const ImageIn
   JxlDecoderDestroy(jxl_info);
   if (jxl_status == JXL_DEC_ERROR)
     ThrowReaderException(CorruptImageError,"UnableToReadImageData");
-  (void) CloseBlob(image);
+  if (CloseBlob(image) == MagickFalse)
+    status=MagickFalse;
+  if (status == MagickFalse)
+    return(DestroyImageList(image));
   return(GetFirstImageInList(image));
 }
 #endif
@@ -847,7 +898,7 @@ ModuleExport void UnregisterJXLImage(voi
 */
 
 static JxlEncoderStatus JXLWriteMetadata(const Image *image,
-  JxlEncoder *jxl_info)
+  JxlEncoder *jxl_info, const StringInfo *icc_profile)
 {
   JxlColorEncoding
     color_encoding;
@@ -855,6 +906,12 @@ static JxlEncoderStatus JXLWriteMetadata
   JxlEncoderStatus
     jxl_status;
 
+  if (icc_profile != (StringInfo *) NULL)
+    {
+      jxl_status=JxlEncoderSetICCProfile(jxl_info,(const uint8_t *)
+        GetStringInfoDatum(icc_profile),GetStringInfoLength(icc_profile));
+      return(jxl_status);
+    }
   (void) memset(&color_encoding,0,sizeof(color_encoding));
   color_encoding.color_space=JXL_COLOR_SPACE_RGB;
   if (IsRGBColorspace(image->colorspace) == MagickFalse)
@@ -867,13 +924,24 @@ static JxlEncoderStatus JXLWriteMetadata
   return(jxl_status);
 }
 
-static inline float JXLGetDistance(const ImageInfo *image_info)
+static inline float JXLGetDistance(float quality)
+{
+  return quality >= 100.0f ? 0.0f
+         : quality >= 30
+             ? 0.1f + (100 - quality) * 0.09f
+             : 53.0f / 3000.0f * quality * quality - 23.0f / 20.0f * quality + 25.0f;
+}
+
+static inline MagickBooleanType JXLSameFrameType(const Image *image,
+  const Image *frame)
 {
-  if (image_info->quality == 0)
-    return(1.0f);
-  if (image_info->quality >= 30)
-    return(0.1f+(float) (100-MagickMin(100,image_info->quality))*0.09f);
-  return(6.24f+(float) pow(2.5f,(30.0-image_info->quality)/5.0)/6.25f);
+  if (image->depth != frame->depth)
+    return(MagickFalse);
+  if (image->alpha_trait != frame->alpha_trait)
+    return(MagickFalse);
+  if (image->colorspace != frame->colorspace)
+    return(MagickFalse);
+  return(MagickTrue);
 }
 
 static MagickBooleanType WriteJXLImage(const ImageInfo *image_info,Image *image,
@@ -883,6 +951,7 @@ static MagickBooleanType WriteJXLImage(c
     *option;
 
   const StringInfo
+    *icc_profile = (StringInfo *) NULL,
     *exif_profile = (StringInfo *) NULL,
     *xmp_profile = (StringInfo *) NULL;
 
@@ -911,13 +980,14 @@ static MagickBooleanType WriteJXLImage(c
     status;
 
   MemoryInfo
-    *pixel_info;
+    *pixel_info = (MemoryInfo *) NULL;
 
   MemoryManagerInfo
     memory_manager_info;
 
   size_t
-    bytes_per_row;
+    channels_size,
+    sample_size;
 
   unsigned char
     *pixels;
@@ -942,12 +1012,45 @@ static MagickBooleanType WriteJXLImage(c
   if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) &&
       (IsCMYKColorspace(image->colorspace) == MagickFalse))
     (void) TransformImageColorspace(image,sRGBColorspace,exception);
+  if ((image_info->adjoin != MagickFalse) &&
+      (GetNextImageInList(image) != (Image *) NULL))
+    {
+      Image
+        *frame;
+
+      MagickBooleanType
+        has_alpha;
+
+      size_t
+        depth;
+
+      depth=image->depth;
+      has_alpha=MagickFalse;
+      for (frame=image; frame != (Image *) NULL; frame=GetNextImageInList(frame))
+      {
+        if ((frame->alpha_trait & BlendPixelTrait) != 0)
+          has_alpha=MagickTrue;
+        if (frame->depth > depth)
+          depth=frame->depth;
+      }
+      for (frame=image; frame != (Image *) NULL; frame=GetNextImageInList(frame))
+      {
+        frame->depth=depth;
+        if (has_alpha != MagickFalse)
+          {
+            if ((frame->alpha_trait & BlendPixelTrait) == 0)
+              (void) SetImageAlphaChannel(frame,OpaqueAlphaChannel,exception);
+          }
+        if (frame->colorspace != image->colorspace)
+          (void) TransformImageColorspace(frame,image->colorspace,exception);
+      }
+    }
   /*
     Initialize JXL delegate library.
   */
-  memset(&basic_info,0,sizeof(basic_info));
-  memset(&frame_header,0,sizeof(frame_header));
-  memset(&pixel_format,0,sizeof(pixel_format));
+  (void) memset(&basic_info,0,sizeof(basic_info));
+  (void) memset(&frame_header,0,sizeof(frame_header));
+  (void) memset(&pixel_format,0,sizeof(pixel_format));
   JXLSetMemoryManager(&memory_manager,&memory_manager_info,image,exception);
   jxl_info=JxlEncoderCreate(&memory_manager);
   if (jxl_info == (JxlEncoder *) NULL)
@@ -995,7 +1098,10 @@ static MagickBooleanType WriteJXLImage(c
       basic_info.num_extra_channels=1;
     }
   if (image_info->quality == 100)
-    basic_info.uses_original_profile=JXL_TRUE;
+    {
+      basic_info.uses_original_profile=JXL_TRUE;
+      icc_profile=GetImageProfile(image,"icc");
+    }
   if ((image_info->adjoin != MagickFalse) &&
       (GetNextImageInList(image) != (Image *) NULL))
     {
@@ -1004,7 +1110,6 @@ static MagickBooleanType WriteJXLImage(c
       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)
@@ -1026,9 +1131,9 @@ static MagickBooleanType WriteJXLImage(c
       (void) JxlEncoderSetFrameDistance(frame_settings,0.f);
       (void) JxlEncoderSetFrameLossless(frame_settings,JXL_TRUE);
     }
-  else
+  else if (image_info->quality != 0)
     (void) JxlEncoderSetFrameDistance(frame_settings,
-      JXLGetDistance(image_info));
+      JXLGetDistance((float) image_info->quality));
   option=GetImageOption(image_info,"jxl:effort");
   if (option != (const char *) NULL)
     (void) JxlEncoderFrameSettingsSetOption(frame_settings,
@@ -1071,7 +1176,7 @@ static MagickBooleanType WriteJXLImage(c
         }
       (void) JxlEncoderCloseBoxes(jxl_info);
     }
-  jxl_status=JXLWriteMetadata(image,jxl_info);
+  jxl_status=JXLWriteMetadata(image,jxl_info,icc_profile);
   if (jxl_status != JXL_ENC_SUCCESS)
     {
       JxlThreadParallelRunnerDestroy(runner);
@@ -1081,34 +1186,80 @@ static MagickBooleanType WriteJXLImage(c
   /*
     Write image as a JXL stream.
   */
-  bytes_per_row=image->columns*
-    (((image->alpha_trait & BlendPixelTrait) != 0) ? 4 : 3)*
-    ((pixel_format.data_type == JXL_TYPE_FLOAT) ? sizeof(float) :
-     (pixel_format.data_type == JXL_TYPE_UINT16) ? sizeof(short) :
-     sizeof(char));
+  sample_size=sizeof(char);
+  if ((pixel_format.data_type == JXL_TYPE_FLOAT) ||
+      (pixel_format.data_type == JXL_TYPE_FLOAT16))
+    sample_size=sizeof(float);
+  else
+    if (pixel_format.data_type == JXL_TYPE_UINT16)
+      sample_size=sizeof(short);
   if (IsGrayColorspace(image->colorspace) != MagickFalse)
-    bytes_per_row=image->columns*
-      (((image->alpha_trait & BlendPixelTrait) != 0) ? 2 : 1)*
-      ((pixel_format.data_type == JXL_TYPE_FLOAT) ? sizeof(float) :
-       (pixel_format.data_type == JXL_TYPE_UINT16) ? sizeof(short) :
-       sizeof(char));
-  pixel_info=AcquireVirtualMemory(bytes_per_row,image->rows*sizeof(*pixels));
-  if (pixel_info == (MemoryInfo *) NULL)
-    {
-      JxlThreadParallelRunnerDestroy(runner);
-      JxlEncoderDestroy(jxl_info);
-      ThrowWriterException(CoderError,"MemoryAllocationFailed");
-    }
+    channels_size=(((image->alpha_trait & BlendPixelTrait) != 0) ? 2U : 1U)*
+      sample_size;
+  else
+    channels_size=(((image->alpha_trait & BlendPixelTrait) != 0) ? 4U : 3U)*
+      sample_size;
   do
   {
     Image
       *next;
 
+    size_t
+      bytes_per_row;
+
+    if (HeapOverflowSanityCheckGetSize(image->columns,channels_size,&bytes_per_row) != MagickFalse)
+      {
+        (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
+          "MemoryAllocationFailed","`%s'",image->filename);
+        status=MagickFalse;
+        break;
+      }
+    pixel_info=AcquireVirtualMemory(bytes_per_row,image->rows*sizeof(*pixels));
+    if (pixel_info == (MemoryInfo *) NULL)
+      {
+        (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
+          "MemoryAllocationFailed","`%s'",image->filename);
+        status=MagickFalse;
+        break;
+      }
+
     if (basic_info.have_animation == JXL_TRUE)
       {
+        JxlBlendInfo
+          alpha_blend_info;
+
+        frame_header.duration=(uint32_t) image->delay;
+        if ((image->previous == (Image *) NULL) ||
+            (image->previous->dispose == BackgroundDispose) ||
+            (image->previous->dispose == PreviousDispose))
+          {
+            frame_header.layer_info.blend_info.blendmode=JXL_BLEND_REPLACE;
+            frame_header.layer_info.blend_info.source=0;
+          }
+        else
+          {
+            frame_header.layer_info.blend_info.blendmode=JXL_BLEND_BLEND;
+            frame_header.layer_info.blend_info.source=1;
+          }
+        frame_header.layer_info.save_as_reference=1;
+        if ((image->page.width != 0) && (image->page.height != 0))
+          {
+            frame_header.layer_info.have_crop=JXL_TRUE;
+            frame_header.layer_info.crop_x0=(int32_t) image->page.x;
+            frame_header.layer_info.crop_y0=(int32_t) image->page.y;
+            frame_header.layer_info.xsize=(uint32_t) image->columns;
+            frame_header.layer_info.ysize=(uint32_t) image->rows;
+          }
         jxl_status=JxlEncoderSetFrameHeader(frame_settings,&frame_header);
         if (jxl_status != JXL_ENC_SUCCESS)
           break;
+        if (basic_info.num_extra_channels > 0)
+          {
+            JxlEncoderInitBlendInfo(&alpha_blend_info);
+            alpha_blend_info.blendmode=frame_header.layer_info.blend_info.blendmode;
+            alpha_blend_info.source=frame_header.layer_info.blend_info.source;
+            (void) JxlEncoderSetExtraChannelBlendInfo(frame_settings,0,&alpha_blend_info);
+          }
       }
     pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
     if (IsGrayColorspace(image->colorspace) != MagickFalse)
@@ -1137,16 +1288,18 @@ static MagickBooleanType WriteJXLImage(c
     next=GetNextImageInList(image);
     if (next == (Image*) NULL)
       break;
-    if ((next->columns != image->columns) || (next->rows != image->rows))
+    if (JXLSameFrameType(image,next) == MagickFalse)
       {
        (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
          "FramesNotSameDimensions","`%s'",image->filename);
        status=MagickFalse;
        break;
       }
+    pixel_info=RelinquishVirtualMemory(pixel_info);
     image=SyncNextImageInList(image);
   } while (image_info->adjoin != MagickFalse);
-  pixel_info=RelinquishVirtualMemory(pixel_info);
+  if (pixel_info != (MemoryInfo *) NULL)
+    pixel_info=RelinquishVirtualMemory(pixel_info);
   if (jxl_status == JXL_ENC_SUCCESS)
     {
       unsigned char
@@ -1192,7 +1345,8 @@ static MagickBooleanType WriteJXLImage(c
   JxlEncoderDestroy(jxl_info);
   if (jxl_status != JXL_ENC_SUCCESS)
     ThrowWriterException(CoderError,"UnableToWriteImageData");
-  (void) CloseBlob(image);
+  if (CloseBlob(image) == MagickFalse)
+    status=MagickFalse;
   return(status);
 }
 #endif
