Commit 02a8cbcc9 for imagemagick.org

commit 02a8cbcc99393cb9984f9f39f4b6da06b8139da4
Author: Cristy <urban-warrior@imagemagick.org>
Date:   Thu Oct 15 20:22:36 2026 -0400

    support -shuffle operator

diff --git a/MagickCore/list.c b/MagickCore/list.c
index 3491de5ec..584f99b5a 100644
--- a/MagickCore/list.c
+++ b/MagickCore/list.c
@@ -1337,24 +1337,24 @@ MagickExport void ReverseImageList(Image **images)
 %                                                                            %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  ShuffleImages() randomly shuffles the positions of the images in an image
-%  list.
+%  ShuffleImages() reorders the sequence of images in an image list based on
+%  ascending, descending, or random criteria.
 %
 %  The format of the ShuffleImages method is:
 %
-%      Image *ShuffleImages(Image **images,const ShuffleType shuffle_type,
+%      Image *ShuffleImages(Image **images,const ShuffleStrategy strategy,
 %        ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
 %    o images: the image list.
 %
-%    o shuffle_type: the shuffle type (Ascending, descending, random).
+%    o strategy: the shuffle strategy (ascending, descending, random).
 %
 %    o exception: return any errors or warnings in this structure.
 %
 */
-MagickExport Image *ShuffleImages(Image **images,const ShuffleType shuffle_type,
+MagickExport Image *ShuffleImages(Image **images,const ShuffleStrategy strategy,
   ExceptionInfo *exception)
 {
   Image
@@ -1369,50 +1369,115 @@ MagickExport Image *ShuffleImages(Image **images,const ShuffleType shuffle_type,
   ssize_t
     i;

-  /*
-    Ensure the image list is valid.
-  */
-  (void) shuffle_type;
   assert(images != (Image **) NULL);
   if (*images == (Image *) NULL)
     return((Image *) NULL);
-  if ((*images)->previous != (Image *) NULL)
-    *images=GetFirstImageInList(*images);
-  number_images=GetImageListLength(*images);
-  if (number_images <= 1)
+  if ((*images)->next == (Image *) NULL)
     return(*images);
-  /*
-    Convert the image list into a temporary array of image pointers.
-  */
-  image_array=ImageListToArray(*images,exception);
-  if (image_array == (Image **) NULL)
+  number_images=GetImageListLength(*images);
+  if (number_images == 0)
     return((Image *) NULL);
-  /*
-    Shuffle the array using the Fisher-Yates algorithm.
-  */
-  random_info=AcquireRandomInfo();
-  for (i=(ssize_t) number_images-1; i > 0; i--)
+  image_array=(Image **) AcquireQuantumMemory(number_images,
+    sizeof(*image_array));
+  if (image_array == (Image **) NULL)
+    {
+      ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
+        "MemoryAllocationFailed","%s",exception->reason);
+      return((Image *) NULL);
+    }
   {
-    ssize_t
+    Image
+      *image;
+
+    register ssize_t
       j;

-    Image *temp = image_array[i];
-    j=(ssize_t) (GetPseudoRandomValue(random_info)*(i+1));
-    if (j > i)
-      j=i;
-    image_array[i]=image_array[j];
-    image_array[j]=temp;
+    image=(*images);
+    for (j=0; j < (ssize_t) number_images; j++)
+    {
+      image_array[j]=image;
+      image=image->next;
+    }
+  }
+  switch (strategy)
+  {
+    case AscendingShuffle:
+    {
+      register ssize_t
+        j;
+
+      for (i=0; i < (ssize_t) (number_images-1); i++)
+      {
+        for (j=i+1; j < (ssize_t) number_images; j++)
+        {
+          if (LocaleCompare(image_array[j]->filename,image_array[i]->filename) < 0)
+            {
+              Image
+                *swap;
+
+              swap=image_array[i];
+              image_array[i]=image_array[j];
+              image_array[j]=swap;
+            }
+        }
+      }
+      break;
+    }
+    case DescendingShuffle:
+    {
+      register ssize_t
+        j;
+
+      for (i=0; i < (ssize_t) (number_images-1); i++)
+      {
+        for (j=i+1; j < (ssize_t) number_images; j++)
+        {
+          if (LocaleCompare(image_array[j]->filename,image_array[i]->filename) > 0)
+            {
+              Image
+                *swap;
+
+              swap=image_array[i];
+              image_array[i]=image_array[j];
+              image_array[j]=swap;
+            }
+        }
+      }
+      break;
+    }
+    case RandomShuffle:
+    {
+      random_info=AcquireRandomInfo();
+      for (i=(ssize_t) number_images-1; i > 0; i--)
+      {
+        ssize_t
+          j;
+
+        j=(ssize_t) (GetPseudoRandomValue(random_info)*(i+1));
+        if (j > i)
+          j=i;
+        {
+          Image
+            *swap;
+
+          swap=image_array[i];
+          image_array[i]=image_array[j];
+          image_array[j]=swap;
+        }
+      }
+      random_info=DestroyRandomInfo(random_info);
+      break;
+    }
+    case UndefinedShuffle:
+    default:
+      break;
   }
-  random_info=DestroyRandomInfo(random_info);
-  /*
-    Re-link the image list based on the shuffled order.
-  */
   *images=image_array[0];
   (*images)->previous=(Image *) NULL;
   for (i=0; i < (ssize_t) number_images; i++)
   {
-    image_array[i]->next=(i+1 < (ssize_t) number_images) ? image_array[i+1] :
-      (Image *) NULL;
+    image_array[i]->next=(i < (ssize_t) number_images-1) ?
+      image_array[i+1] : (Image *) NULL;
     image_array[i]->previous=(i > 0) ? image_array[i-1] : (Image *) NULL;
   }
   image_array=(Image **) RelinquishMagickMemory(image_array);
diff --git a/MagickCore/list.h b/MagickCore/list.h
index 8133f0b2e..5327cd50b 100644
--- a/MagickCore/list.h
+++ b/MagickCore/list.h
@@ -28,7 +28,7 @@ typedef enum
   RandomShuffle,
   AscendingShuffle,
   DescendingShuffle
-} ShuffleType;
+} ShuffleStrategy;

 extern MagickExport Image
   *CloneImageList(const Image *,ExceptionInfo *),
@@ -45,7 +45,7 @@ extern MagickExport Image
   *RemoveImageFromList(Image **),
   *RemoveLastImageFromList(Image **),
   *RemoveFirstImageFromList(Image **),
-  *ShuffleImages(Image **,const ShuffleType,ExceptionInfo *),
+  *ShuffleImages(Image **,const ShuffleStrategy,ExceptionInfo *),
   *SpliceImageIntoList(Image **,const size_t,const Image *),
   *SplitImageList(Image *),
   *SyncNextImageInList(const Image *);
diff --git a/MagickCore/option.c b/MagickCore/option.c
index 23cfa4ee0..ebf24a120 100644
--- a/MagickCore/option.c
+++ b/MagickCore/option.c
@@ -1128,7 +1128,7 @@ static const OptionInfo
     { "-shave", 1L, SimpleOperatorFlag, MagickFalse },
     { "+shear", 1L, DeprecateOptionFlag, MagickTrue },
     { "-shear", 1L, SimpleOperatorFlag, MagickFalse },
-    { "+shuffle", 0L, DeprecateOptionFlag | FireOptionFlag, MagickTrue },
+    { "+shuffle", 1L, DeprecateOptionFlag | FireOptionFlag, MagickTrue },
     { "-shuffle", 1L, ListOperatorFlag | FireOptionFlag, MagickFalse },
     { "-sigmoidal-contrast", 1L, SimpleOperatorFlag, MagickFalse },
     { "+sigmoidal-contrast", 1L, SimpleOperatorFlag, MagickFalse },
diff --git a/MagickWand/magick-cli.c b/MagickWand/magick-cli.c
index 62ce1859a..146a05b54 100644
--- a/MagickWand/magick-cli.c
+++ b/MagickWand/magick-cli.c
@@ -1018,6 +1018,7 @@ static MagickBooleanType MagickCommandUsage(void)
       "                       terms (coefficients and degree pairs).\n"
       "  -print string        interpret string and print to console\n"
       "  -process arguments   process the image with a custom image filter\n"
+      "  -shuffle strategy    reorders the sequence of images\n"
       "  -smush geometry      smush an image sequence together\n"
       "  -write filename      write images to this file",
     settings[] =
diff --git a/MagickWand/mogrify.c b/MagickWand/mogrify.c
index 96ad00430..331a4d3fc 100644
--- a/MagickWand/mogrify.c
+++ b/MagickWand/mogrify.c
@@ -3580,6 +3580,7 @@ static MagickBooleanType MogrifyUsage(void)
       "                       terms (coefficients and degree pairs).\n"
       "  -print string        interpret string and print to console\n"
       "  -process arguments   process the image with a custom image filter\n"
+      "  -shuffle strategy    reorders the sequence of images\n"
       "  -smush geometry      smush an image sequence together\n"
       "  -write filename      write images to this file",
     settings[] =
@@ -6131,6 +6132,24 @@ WandExport MagickBooleanType MogrifyImageCommand(ImageInfo *image_info,
               ThrowMogrifyInvalidArgumentException(option,argv[i]);
             break;
           }
+        if (LocaleCompare("shuffle",option+1) == 0)
+          {
+            ssize_t
+              strategy;
+
+            strategy=UndefinedShuffle;
+            if (*option == '+')
+              break;
+            i++;
+            if (i == (ssize_t) argc)
+              ThrowMogrifyException(OptionError,"MissingArgument",option);
+            strategy=ParseCommandOption(MagickShuffleOptions,MagickFalse,
+              argv[i]);
+            if (strategy < 0)
+              ThrowMogrifyException(OptionError,"UnrecognizedImageOrientation",
+                argv[i]);
+            break;
+          }
         if (LocaleCompare("sigmoidal-contrast",option+1) == 0)
           {
             i++;
@@ -8922,6 +8941,17 @@ WandExport MagickBooleanType MogrifyImageList(ImageInfo *image_info,
       }
       case 's':
       {
+        if (LocaleCompare("shuffle",option+1) == 0)
+          {
+            ShuffleStrategy
+              shuffle_type;
+
+            (void) SyncImagesSettings(mogrify_info,*images,exception);
+            shuffle_type=(ShuffleStrategy) ParseCommandOption(
+              MagickShuffleOptions,MagickFalse,argv[i+1]);
+            (void) ShuffleImages(&(*images),shuffle_type,exception);
+            break;
+          }
         if (LocaleCompare("smush",option+1) == 0)
           {
             Image
diff --git a/MagickWand/operation.c b/MagickWand/operation.c
index f5e9fb3d0..e251e0315 100644
--- a/MagickWand/operation.c
+++ b/MagickWand/operation.c
@@ -4287,20 +4287,6 @@ static MagickBooleanType CLIListOperatorImages(MagickCLI *cli_wand,
     {
       if (LocaleCompare("fft",option+1) == 0)
         {
-          new_images=ForwardFourierTransformImage(_images,IsNormalOp,
-           _exception);
-          break;
-        }
-      if (LocaleCompare("flatten",option+1) == 0)
-        {
-          /* REDIRECTED to use -layers flatten instead */
-          (void) CLIListOperatorImages(cli_wand,"-layers",option+1,NULL);
-          break;
-        }
-      if (LocaleCompare("fx",option+1) == 0)
-        {
-          new_images=FxImage(_images,arg1,_exception);
-          break;
         }
       CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
     }
@@ -4691,6 +4677,15 @@ static MagickBooleanType CLIListOperatorImages(MagickCLI *cli_wand,
     }
     case 's':
     {
+      if (LocaleCompare("shuffle",option+1) == 0)
+        {
+          parse=ParseCommandOption(MagickShuffleOptions,MagickFalse,arg1);
+          if ( parse < 0 )
+            CLIWandExceptArgBreak(OptionError,"UnrecognizedShuffleStrategy",
+              option,arg1);
+          (void) ShuffleImages(&_images,(ShuffleStrategy) parse,_exception);
+          break;
+        }
       if (LocaleCompare("smush",option+1) == 0)
         {
           /* FUTURE: this option needs more work to make better */