Commit cd5b55db8 for imagemagick.org

commit cd5b55db868630585b12357ee9545705d48e259e
Author: Dirk Lemstra <dirk@lemstra.org>
Date:   Sat Nov 14 12:49:14 2026 +0100

    Preserve neutral samples in grayscale conversions

diff --git a/MagickCore/accelerate-kernels-private.h b/MagickCore/accelerate-kernels-private.h
index 80807b6f2..d7f2f6224 100644
--- a/MagickCore/accelerate-kernels-private.h
+++ b/MagickCore/accelerate-kernels-private.h
@@ -528,7 +528,10 @@ OPENCL_ENDIF()
             blue=EncodePixelGamma(blue);
           }
           */
-          intensity=0.298839*red+0.586811*green+0.114350*blue;
+          if ((red == green) && (green == blue))
+            intensity=red;
+          else
+            intensity=0.298839*red+0.586811*green+0.114350*blue;
           break;
         }
       case Rec601LuminancePixelIntensityMethod:
@@ -541,7 +544,10 @@ OPENCL_ENDIF()
             blue=DecodePixelGamma(blue);
           }
           */
-          intensity=0.298839*red+0.586811*green+0.114350*blue;
+          if ((red == green) && (green == blue))
+            intensity=red;
+          else
+            intensity=0.298839*red+0.586811*green+0.114350*blue;
           break;
         }
       case Rec709LumaPixelIntensityMethod:
@@ -555,7 +561,10 @@ OPENCL_ENDIF()
             blue=EncodePixelGamma(blue);
           }
           */
-          intensity=0.212656*red+0.715158*green+0.072186*blue;
+          if ((red == green) && (green == blue))
+            intensity=red;
+          else
+            intensity=0.212656*red+0.715158*green+0.072186*blue;
           break;
         }
       case Rec709LuminancePixelIntensityMethod:
@@ -568,7 +577,10 @@ OPENCL_ENDIF()
             blue=DecodePixelGamma(blue);
           }
           */
-          intensity=0.212656*red+0.715158*green+0.072186*blue;
+          if ((red == green) && (green == blue))
+            intensity=red;
+          else
+            intensity=0.212656*red+0.715158*green+0.072186*blue;
           break;
         }
       case RMSPixelIntensityMethod:
diff --git a/MagickCore/enhance.c b/MagickCore/enhance.c
index 9f8d2cfd2..cae1bd0fb 100644
--- a/MagickCore/enhance.c
+++ b/MagickCore/enhance.c
@@ -2593,7 +2593,10 @@ MagickExport MagickBooleanType GrayscaleImage(Image *image,
               green=EncodePixelGamma(green);
               blue=EncodePixelGamma(blue);
             }
-          intensity=0.298839*red+0.586811*green+0.114350*blue;
+          if ((red == green) && (green == blue))
+            intensity=red;
+          else
+            intensity=0.298839*red+0.586811*green+0.114350*blue;
           break;
         }
         case Rec601LuminancePixelIntensityMethod:
@@ -2604,7 +2607,10 @@ MagickExport MagickBooleanType GrayscaleImage(Image *image,
               green=DecodePixelGamma(green);
               blue=DecodePixelGamma(blue);
             }
-          intensity=0.298839*red+0.586811*green+0.114350*blue;
+          if ((red == green) && (green == blue))
+            intensity=red;
+          else
+            intensity=0.298839*red+0.586811*green+0.114350*blue;
           break;
         }
         case Rec709LumaPixelIntensityMethod:
@@ -2616,7 +2622,10 @@ MagickExport MagickBooleanType GrayscaleImage(Image *image,
               green=EncodePixelGamma(green);
               blue=EncodePixelGamma(blue);
             }
-          intensity=0.212656*red+0.715158*green+0.072186*blue;
+          if ((red == green) && (green == blue))
+            intensity=red;
+          else
+            intensity=0.212656*red+0.715158*green+0.072186*blue;
           break;
         }
         case Rec709LuminancePixelIntensityMethod:
@@ -2627,7 +2636,10 @@ MagickExport MagickBooleanType GrayscaleImage(Image *image,
               green=DecodePixelGamma(green);
               blue=DecodePixelGamma(blue);
             }
-          intensity=0.212656*red+0.715158*green+0.072186*blue;
+          if ((red == green) && (green == blue))
+            intensity=red;
+          else
+            intensity=0.212656*red+0.715158*green+0.072186*blue;
           break;
         }
         case RMSPixelIntensityMethod:
diff --git a/tests/cli-pipe.tap b/tests/cli-pipe.tap
index f6dc56d0a..5d7ea3822 100755
--- a/tests/cli-pipe.tap
+++ b/tests/cli-pipe.tap
@@ -18,7 +18,7 @@
 #
 . ./common.shi
 . ${srcdir}/tests/common.shi
-echo "1..29"
+echo "1..33"

 ${MAGICK} pnm:- null:   < ${SRCDIR}/rose.pnm && echo "ok" || echo "not ok"
 ${MAGICK} pnm:- info:   < ${SRCDIR}/rose.pnm && echo "ok" || echo "not ok"
@@ -196,4 +196,22 @@ if [ "X$sample" = 'X40' ]; then
 else
   echo "not ok 29 - depth reduction of a mid-range sample: $sample"
 fi
+
+# Luma and luminance grayscale methods must preserve full-range neutral samples.
+test_number=30
+for method in Rec601Luma Rec601Luminance Rec709Luma Rec709Luminance; do
+  case "$method" in
+    *Luminance) colorspace="-set colorspace RGB" ;;
+    *) colorspace="" ;;
+  esac
+  sample=`LC_ALL=C ${MAGICK} -size 1x1 xc:white $colorspace \
+    -grayscale "$method" -depth 64 GRAY:- 2>/dev/null |
+    od -An -tx1 | tr -d ' \n'`
+  if [ "X$sample" = 'Xffffffffffffffff' ]; then
+    echo "ok $test_number - $method preserves a full-range neutral sample"
+  else
+    echo "not ok $test_number - $method preserves a full-range neutral sample: $sample"
+  fi
+  test_number=$((test_number+1))
+done
 :