Commit 3b8a277d4 for imagemagick.org

commit 3b8a277d499bfea7a936f21589fd1a93286ab893
Author: Madan mohan Manokar <madanmohan.manokar@amd.com>
Date:   Fri Nov 13 18:54:35 2026 +0530

    MagickCore: tile the vertical resize filter (#9094)

    VerticalFilter walks down one output column at a time, so each filter
    tap is a full row apart and every sample strides across the image.
    Filter a tile of up to 16 adjacent columns per tap row instead: the tap
    row of a tile is read contiguously and added to one sum per sample.
    This replaces the per-column loop, so there is a single code path for
    every image.

    Alpha-blended channels use the same alpha weights and gamma sums as
    before, and copied channels and write-masked pixels take the nearest
    source sample. Every sum adds its taps in the same order as before, so
    the output is unchanged.

    The tap loop is plain C with no target-specific code. GCC vectorizes it
    at -O2 when the tile width is constant.

diff --git a/MagickCore/resize.c b/MagickCore/resize.c
index 182b1730a..8de901551 100644
--- a/MagickCore/resize.c
+++ b/MagickCore/resize.c
@@ -3578,6 +3578,22 @@ static MagickBooleanType HorizontalFilter(
   return(status);
 }

+/*
+  VerticalFilter() filters a tile of adjacent columns per tap row, so each
+  tap row is read contiguously rather than a full row apart per sample.
+*/
+#define VerticalFilterTile  16
+
+static inline void VerticalFilterTap(double *magick_restrict pixel,
+  const Quantum *magick_restrict r,const double alpha,const ssize_t samples)
+{
+  ssize_t
+    i;
+
+  for (i=0; i < samples; i++)
+    pixel[i]+=alpha*(double) r[i];
+}
+
 static MagickBooleanType VerticalFilter(
   const ResizeFilter *magick_restrict resize_filter,
   const Image *magick_restrict image,Image *magick_restrict resize_image,
@@ -3654,6 +3670,9 @@ static MagickBooleanType VerticalFilter(
       bisect,
       density;

+    MagickBooleanType
+      blend;
+
     PixelChannel
       channel_map[MaxPixelChannels+1];

@@ -3667,6 +3686,7 @@ static MagickBooleanType VerticalFilter(
     ssize_t
       c,
       n,
+      nearest,
       start,
       stop,
       x;
@@ -3709,91 +3729,106 @@ static MagickBooleanType VerticalFilter(
         status=MagickFalse;
         continue;
       }
+    blend=MagickFalse;
     for (c=0; c < number_channels; c++)
     {
       channel_map[c]=GetPixelChannelChannel(image,c);
       traits_map[c]=GetPixelChannelTraits(image,channel_map[c]);
       resize_traits_map[c]=GetPixelChannelTraits(resize_image,channel_map[c]);
+      if ((resize_traits_map[c] & BlendPixelTrait) != 0)
+        blend=MagickTrue;
     }
-    for (x=0; x < (ssize_t) resize_image->columns; x++)
+    nearest=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
+      stop-1.0)+0.5)-start;
+    for (x=0; x < (ssize_t) resize_image->columns; x+=VerticalFilterTile)
     {
+      double
+        alpha,
+        gamma[VerticalFilterTile],
+        pixel[VerticalFilterTile*MaxPixelChannels];
+
       ssize_t
         i,
-        mask_offset;
+        j,
+        t,
+        tile;

       /*
-        Initialize the destination write mask from the nearest source pixel.
+        Accumulate each tap row of the tile into one sum per sample.
       */
-      mask_offset=(ssize_t) ((contribution[(ssize_t) (MagickMin(MagickMax(
-        bisect,(double) start),(double) stop-1.0)+0.5)-start].pixel-
-        contribution[0].pixel)*(ssize_t) image->columns+x);
-      SetPixelWriteMask(resize_image,GetPixelWriteMask(image,p+mask_offset*
-        number_channels),q);
-      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+      tile=MagickMin(VerticalFilterTile,(ssize_t) resize_image->columns-x);
+      (void) memset(pixel,0,(size_t) (tile*number_channels)*sizeof(*pixel));
+      (void) memset(gamma,0,sizeof(gamma));
+      for (j=0; j < n; j++)
       {
-        double
-          alpha,
-          gamma,
-          pixel;
-
-        PixelChannel
-          channel;
+        const Quantum
+          *magick_restrict r;

-        PixelTrait
-          resize_traits,
-          traits;
-
-        ssize_t
-          j,
-          k;
-
-        channel=channel_map[i];
-        traits=traits_map[i];
-        resize_traits=resize_traits_map[i];
-        if ((traits == UndefinedPixelTrait) ||
-            (resize_traits == UndefinedPixelTrait))
-          continue;
-        if (((resize_traits & CopyPixelTrait) != 0) ||
-            (GetPixelWriteMask(resize_image,q) <= (QuantumRange/2)))
-          {
-            j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
-              stop-1.0)+0.5);
-            k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
-              (ssize_t) image->columns+x);
-            SetPixelChannel(resize_image,channel,p[k*(ssize_t)
-              number_channels+i],q);
-            continue;
-          }
-        pixel=0.0;
-        if ((resize_traits & BlendPixelTrait) == 0)
+        r=p+((contribution[j].pixel-contribution[0].pixel)*(ssize_t)
+          image->columns+x)*number_channels;
+        if (blend == MagickFalse)
           {
             /*
-              No alpha blending.
+              The compiler vectorizes the tap only for a constant tile width.
             */
-            for (j=0; j < n; j++)
-            {
-              k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
-                (ssize_t) image->columns+x);
-              alpha=contribution[j].weight;
-              pixel+=alpha*(double) p[k*number_channels+i];
-            }
-            SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
+            if (tile == VerticalFilterTile)
+              VerticalFilterTap(pixel,r,contribution[j].weight,
+                VerticalFilterTile*number_channels);
+            else
+              VerticalFilterTap(pixel,r,contribution[j].weight,tile*
+                number_channels);
             continue;
           }
-        gamma=0.0;
-        for (j=0; j < n; j++)
+        for (t=0; t < tile; t++)
         {
-          k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
-            (ssize_t) image->columns+x);
           alpha=contribution[j].weight*QuantumScale*(double)
-            GetPixelAlpha(image,p+k*number_channels);
-          pixel+=alpha*(double) p[k*number_channels+i];
-          gamma+=alpha;
+            GetPixelAlpha(image,r+t*number_channels);
+          gamma[t]+=alpha;
+          for (i=0; i < number_channels; i++)
+            pixel[t*number_channels+i]+=((resize_traits_map[i] &
+              BlendPixelTrait) != 0 ? alpha : contribution[j].weight)*
+              (double) r[t*number_channels+i];
+        }
+      }
+      for (t=0; t < tile; t++)
+      {
+        const Quantum
+          *magick_restrict s;
+
+        Quantum
+          *magick_restrict d;
+
+        /*
+          Initialize the destination write mask from the nearest source pixel.
+        */
+        s=p+(nearest*(ssize_t) image->columns+x+t)*number_channels;
+        d=q+(x+t)*resize_channels;
+        SetPixelWriteMask(resize_image,GetPixelWriteMask(image,s),d);
+        for (i=0; i < number_channels; i++)
+        {
+          PixelTrait
+            resize_traits;
+
+          resize_traits=resize_traits_map[i];
+          if ((traits_map[i] == UndefinedPixelTrait) ||
+              (resize_traits == UndefinedPixelTrait))
+            continue;
+          if (((resize_traits & CopyPixelTrait) != 0) ||
+              (GetPixelWriteMask(resize_image,d) <= (QuantumRange/2)))
+            {
+              SetPixelChannel(resize_image,channel_map[i],s[i],d);
+              continue;
+            }
+          if ((resize_traits & BlendPixelTrait) == 0)
+            {
+              SetPixelChannel(resize_image,channel_map[i],ClampToQuantum(
+                pixel[t*number_channels+i]),d);
+              continue;
+            }
+          SetPixelChannel(resize_image,channel_map[i],ClampToQuantum(
+            MagickSafeReciprocal(gamma[t])*pixel[t*number_channels+i]),d);
         }
-        gamma=MagickSafeReciprocal(gamma);
-        SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
       }
-      q+=(ptrdiff_t) resize_channels;
     }
     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
       status=MagickFalse;