Commit ac9f64a1e for imagemagick.org
commit ac9f64a1e9eb7671d994e35132287594fd993cbb
Author: Greg B <64932474+gregbenz@users.noreply.github.com>
Date: Sat Sep 5 17:26:33 2026 -0500
fix: preserve destination on UHDR encoder failure (#8941)
diff --git a/Makefile.in b/Makefile.in
index bf451aea2..ddfde75e2 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -6276,6 +6276,7 @@ TESTS_TESTS = \
tests/cli-pipe.tap \
tests/cli-svg.tap \
tests/cli-uhdr.tap \
+ tests/cli-uhdr-encoder.tap \
tests/validate-colorspace.tap \
tests/validate-compare.tap \
tests/validate-composite.tap \
diff --git a/coders/uhdr.c b/coders/uhdr.c
index 4b1f8bd2c..ad7e71aa2 100644
--- a/coders/uhdr.c
+++ b/coders/uhdr.c
@@ -1286,15 +1286,17 @@ static MagickBooleanType WriteUHDRImage(const ImageInfo *image_info,
resized_gainmap_profile=TransformGainMapProfile(image_info,image,
gainmap_profile,&gainmap_transform_status,exception);
if (gainmap_transform_status == MagickFalse)
- return(MagickFalse);
+ {
+ status=MagickFalse;
+ goto cleanup;
+ }
if (resized_gainmap_profile != (StringInfo *) NULL)
gainmap_profile=(const StringInfo *) resized_gainmap_profile;
base_image_profile=EncodeBaseImageProfile(image_info,image,exception);
if (base_image_profile == (StringInfo *) NULL)
{
- if (resized_gainmap_profile != (StringInfo *) NULL)
- resized_gainmap_profile=DestroyStringInfo(resized_gainmap_profile);
- return(MagickFalse);
+ status=MagickFalse;
+ goto cleanup;
}
base_image.data=(void *) GetStringInfoDatum(base_image_profile);
base_image.data_sz=GetStringInfoLength(base_image_profile);
@@ -1322,12 +1324,7 @@ static MagickBooleanType WriteUHDRImage(const ImageInfo *image_info,
GetHDRGMProperty("HDRCapacityMax",hdr_capacity_max);
GetHDRGMPropertyInt("UseBaseColorGrade",use_base_cg);
if (status == MagickFalse)
- {
- if (resized_gainmap_profile != (StringInfo *) NULL)
- resized_gainmap_profile=DestroyStringInfo(resized_gainmap_profile);
- base_image_profile=DestroyStringInfo(base_image_profile);
- return(MagickFalse);
- }
+ goto cleanup;
preserve_gainmap=MagickTrue;
}
@@ -1341,16 +1338,8 @@ static MagickBooleanType WriteUHDRImage(const ImageInfo *image_info,
{
(void) ThrowMagickException(exception,GetMagickModule(),ConfigureWarning,
"invalid hdr color transfer received, ","%s","exiting ... ");
- return(MagickFalse);
- }
- status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
- if (status == MagickFalse)
- {
- if (resized_gainmap_profile != (StringInfo *) NULL)
- resized_gainmap_profile=DestroyStringInfo(resized_gainmap_profile);
- if (base_image_profile != (StringInfo *) NULL)
- base_image_profile=DestroyStringInfo(base_image_profile);
- return(status);
+ status=MagickFalse;
+ goto cleanup;
}
/*
@@ -1770,16 +1759,22 @@ next_image:
{
uhdr_compressed_image_t *output = uhdr_get_encoded_stream(handle);
- (void) WriteBlob(image, output->data_sz, output->data);
+ status=OpenBlob(image_info,images,WriteBinaryBlobMode,exception);
+ if (status != MagickFalse)
+ {
+ if (WriteBlob(images,output->data_sz,output->data) !=
+ (ssize_t) output->data_sz)
+ status=MagickFalse;
+ if (CloseBlob(images) == MagickFalse)
+ status=MagickFalse;
+ }
uhdr_release_encoder(handle);
}
}
#undef CHECK_IF_ERR
- if (CloseBlob(image) == MagickFalse)
- status = MagickFalse;
-
+cleanup:
if (hdrImgDescriptor.planes[UHDR_PLANE_Y])
RelinquishMagickMemory(hdrImgDescriptor.planes[UHDR_PLANE_Y]);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c9e547caa..bf1ee4131 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -45,6 +45,7 @@ TESTS_TESTS = \
tests/cli-pipe.tap \
tests/cli-svg.tap \
tests/cli-uhdr.tap \
+ tests/cli-uhdr-encoder.tap \
tests/validate-colorspace.tap \
tests/validate-compare.tap \
tests/validate-composite.tap \
diff --git a/tests/cli-uhdr-encoder.tap b/tests/cli-uhdr-encoder.tap
new file mode 100755
index 000000000..02e35f002
--- /dev/null
+++ b/tests/cli-uhdr-encoder.tap
@@ -0,0 +1,99 @@
+#!/bin/sh
+# Regression tests for safe Ultra HDR encoder failures.
+. ./common.shi
+. ${srcdir}/tests/common.shi
+
+encoder_error=cli-uhdr-encoder-failure.jpg
+encoder_sdr=cli-uhdr-encoder-sdr.miff
+encoder_hdr=cli-uhdr-encoder-hdr.miff
+pattern_output=cli-uhdr-encoder-pattern-%d.jpg
+pattern_first=cli-uhdr-encoder-pattern-0.jpg
+pattern_wrong=cli-uhdr-encoder-pattern-1.jpg
+
+cleanup()
+{
+ rm -f "$encoder_error" "$encoder_sdr" "$encoder_hdr" \
+ "$pattern_first" "$pattern_wrong"
+}
+
+cleanup
+if ! ${MAGICK} -list configure 2>/dev/null | awk '
+ $1 == "DELEGATES" {
+ for (i=2; i <= NF; i++)
+ {
+ if ($i == "jpeg")
+ jpeg=1
+ if ($i == "uhdr")
+ uhdr=1
+ }
+ }
+ END { exit(jpeg && uhdr ? 0 : 1) }
+ '; then
+ echo "1..0 # SKIP JPEG or UHDR coder unavailable"
+ exit 0
+fi
+
+echo "1..6"
+if ! ${MAGICK} "${SRCDIR}/rose.pnm" -depth 8 "$encoder_sdr" \
+ >/dev/null 2>&1 ||
+ ! ${MAGICK} "${SRCDIR}/rose.pnm" -depth 16 "$encoder_hdr" \
+ >/dev/null 2>&1; then
+ echo "Bail out! unable to create encoder test inputs"
+ cleanup
+ exit 0
+fi
+
+printf 'existing encoder destination' > "$encoder_error"
+encoder_error_message=`${MAGICK} "$encoder_sdr" "$encoder_hdr" \
+ -define uhdr:hdr-color-transfer=linear \
+ -define uhdr:gainmap-quality=101 "UHDR:$encoder_error" 2>&1`
+encoder_error_status=$?
+if [ "$encoder_error_status" = "0" ]; then
+ echo "not ok - invalid encoder setting fails"
+else
+ echo "ok - invalid encoder setting fails"
+fi
+case "$encoder_error_message" in
+ *quality*101*) echo "ok - invalid setting reaches the UHDR encoder" ;;
+ *)
+ echo "not ok - invalid setting reaches the UHDR encoder"
+ echo "# got '$encoder_error_message'"
+ ;;
+esac
+encoder_error_contents=`cat "$encoder_error"`
+if [ "X$encoder_error_contents" = "Xexisting encoder destination" ]; then
+ echo "ok - encoder failure preserves its destination"
+else
+ echo "not ok - encoder failure preserves its destination"
+ echo "# expected 'existing encoder destination', got '$encoder_error_contents'"
+fi
+
+printf 'existing second scene' > "$pattern_wrong"
+pattern_status=0
+${MAGICK} "$encoder_sdr" "$encoder_hdr" \
+ -define uhdr:hdr-color-transfer=linear "UHDR:$pattern_output" \
+ >/dev/null 2>&1 || pattern_status=$?
+if [ "$pattern_status" = "0" ]; then
+ echo "ok - patterned output succeeds"
+else
+ echo "not ok - patterned output succeeds"
+fi
+pattern_decode=`${IDENTIFY} -quiet -format '%m|%[profiles]' \
+ "UHDR:$pattern_first" 2>/dev/null`
+case "$pattern_status:$pattern_decode" in
+ 0:UHDR\|*hdrgm*)
+ echo "ok - patterned output writes a decodable UHDR to the first scene" ;;
+ *)
+ echo "not ok - patterned output writes a decodable UHDR to the first scene"
+ echo "# status=$pattern_status decode='$pattern_decode'"
+ ;;
+esac
+if printf 'existing second scene' | cmp -s - "$pattern_wrong"; then
+ echo "ok - patterned output preserves the second scene destination"
+else
+ echo "not ok - patterned output preserves the second scene destination"
+ pattern_wrong_size=`wc -c < "$pattern_wrong" 2>/dev/null`
+ echo "# destination changed or is missing (size=$pattern_wrong_size)"
+fi
+cleanup
+: