/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%          OOO   PPPP   EEEE  RRRR    AA   TTTTT  III   OOO   N   N           %
%         O   O  P   P  E     R   R  A  A    T     I   O   O  NN  N           %
%         O   O  PPPP   EEE   RRRR   AAAA    T     I   O   O  N N N           %
%         O   O  P      E     R R    A  A    T     I   O   O  N  NN           %
%          OOO   P      EEEE  R  RR  A  A    T    III   OOO   N   N           %
%                                                                             %
%                                                                             %
%                         CLI Magick Option Methods                           %
%                                                                             %
%                              Dragon Computing                               %
%                              Anthony Thyssen                                %
%                               September 2011                                %
%                                                                             %
%                                                                             %
%  Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization      %
%  dedicated to making software imaging solutions freely available.           %
%                                                                             %
%  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                               %
%                                                                             %
%  Unless required by applicable law or agreed to in writing, software        %
%  distributed under the License is distributed on an "AS IS" BASIS,          %
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
%  See the License for the specific language governing permissions and        %
%  limitations under the License.                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Apply the given options (settings, and simple, or sequence operations) to
% the given image(s) according to the current "image_info", "draw_info", and
% "quantize_info" settings, stored in a special CLI Image Wand.
%
% The final goal is to allow the execution in a strict one option at a time
% manner that is needed for 'pipelining and file scripting' of options in
% IMv7.
%
% This the modern command-line parser as opposed to mogrify.c which embeds the
% legacy parser.
%
% Anthony Thyssen, September 2011
*/

/*
  Include declarations.
*/
#include "MagickWand/studio.h"
#include "MagickWand/MagickWand.h"
#include "MagickWand/magick-wand-private.h"
#include "MagickWand/mogrify.h"
#include "MagickWand/operation.h"
#include "MagickWand/wand.h"
#include "MagickWand/wandcli.h"
#include "MagickWand/wandcli-private.h"
#include "MagickCore/color-private.h"
#include "MagickCore/composite-private.h"
#include "MagickCore/image-private.h"
#include "MagickCore/monitor-private.h"
#include "MagickCore/string-private.h"
#include "MagickCore/thread-private.h"
#include "MagickCore/timer-private.h"

/*
  Constant declaration.
*/
static const char
  MogrifyAlphaColor[] = "#bdbdbd",  /* slightly darker gray */
  MogrifyBackgroundColor[] = "#fff",  /* white */
  MogrifyBorderColor[] = "#dfdfdf";  /* sRGB gray */

/*
  Define declarations.
*/
#define USE_WAND_METHODS  1
#define MAX_STACK_DEPTH  32
#define UNDEFINED_COMPRESSION_QUALITY  0UL

/* FUTURE: why is this default so specific? */
#define DEFAULT_DISSIMILARITY_THRESHOLD "0.31830988618379067154"

/* For Debugging Geometry Input */
#define ReportGeometry(flags,info) \
  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n", \
       flags, info.rho, info.sigma, info.xi, info.psi )

/*
** Function to report on the progress of image operations
*/
static MagickBooleanType MonitorProgress(const char *text,
  const MagickOffsetType offset,const MagickSizeType extent,
  void *wand_unused(client_data))
{
  char
    message[MagickPathExtent],
    tag[MagickPathExtent];

  const char
    *locale_message;

  char
    *p;

  magick_unreferenced(client_data);

  if ((extent <= 1) || (offset < 0) || (offset >= (MagickOffsetType) extent))
    return(MagickTrue);
  if ((offset != (MagickOffsetType) (extent-1)) && ((offset % 50) != 0))
    return(MagickTrue);
  (void) CopyMagickString(tag,text,MagickPathExtent);
  p=strrchr(tag,'/');
  if (p != (char *) NULL)
    *p='\0';
  (void) FormatLocaleString(message,MagickPathExtent,"Monitor/%s",tag);
  locale_message=GetLocaleMessage(message);
  if (locale_message == message)
    locale_message=tag;
  if (p == (char *) NULL)
    (void) FormatLocaleFile(stderr,"%s: %ld of %lu, %02ld%% complete\r",
      locale_message,(long) offset,(unsigned long) extent,(long)
      (100L*offset/(extent-1)));
  else
    (void) FormatLocaleFile(stderr,"%s[%s]: %ld of %lu, %02ld%% complete\r",
      locale_message,p+1,(long) offset,(unsigned long) extent,(long)
      (100L*offset/(extent-1)));
  if (offset == (MagickOffsetType) (extent-1))
    (void) FormatLocaleFile(stderr,"\n");
  (void) fflush(stderr);
  return(MagickTrue);
}

/*
** GetImageCache() will read an image into a image cache if not already
** present then return the image that is in the cache under that filename.
*/
static inline Image *GetImageCache(const ImageInfo *image_info,const char *path,
  ExceptionInfo *exception)
{
  char
    key[MagickPathExtent];

  ExceptionInfo
    *sans_exception;

  Image
    *image;

  ImageInfo
    *read_info;

  (void) FormatLocaleString(key,MagickPathExtent,"cache:%s",path);
  sans_exception=AcquireExceptionInfo();
  image=(Image *) GetImageRegistry(ImageRegistryType,key,sans_exception);
  sans_exception=DestroyExceptionInfo(sans_exception);
  if (image != (Image *) NULL)
    return(image);
  read_info=CloneImageInfo(image_info);
  if (path != (const char *) NULL)
    (void) CopyMagickString(read_info->filename,path,MagickPathExtent);
  image=ReadImage(read_info,exception);
  read_info=DestroyImageInfo(read_info);
  if (image != (Image *) NULL)
    (void) SetImageRegistry(ImageRegistryType,key,image,exception);
  return(image);
}

/*
  SparseColorOption() parse the complex -sparse-color argument into an
  an array of floating point values than call SparseColorImage().
  Argument is a complex mix of floating-point pixel coodinates, and color
  specifications (or direct floating point numbers).  The number of floats
  needed to represent a color varies depending on the current channel
  setting.

  This really should be in MagickCore, so that other API's can make use of it.
*/
static Image *SparseColorOption(const Image *image,
  const SparseColorMethod method,const char *arguments,ExceptionInfo *exception)
{
  char
    token[MagickPathExtent];

  const char
    *p;

  double
    *sparse_arguments;

  Image
    *sparse_image;

  PixelInfo
    color;

  MagickBooleanType
    error;

  size_t
    x;

  size_t
    number_arguments,
    number_colors;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  /*
    Limit channels according to image
    add up number of values needed per color.
  */
  number_colors=0;
  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
    number_colors++;
  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
    number_colors++;
  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
    number_colors++;
  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
      (image->colorspace == CMYKColorspace))
    number_colors++;
  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
      image->alpha_trait != UndefinedPixelTrait)
    number_colors++;

  /*
    Read string, to determine number of arguments needed,
  */
  p=arguments;
  x=0;
  while( *p != '\0' )
  {
    (void) GetNextToken(p,&p,MagickPathExtent,token);
    if (*token == ',') continue;
    if ( isalpha((int) ((unsigned char) *token)) || *token == '#' )
      x += number_colors;  /* color argument found */
    else
      x++;   /* floating point argument */
  }
  /* control points and color values */
  if ((x % (2+number_colors)) != 0)
    {
      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
        "InvalidArgument","'%s': %s", "sparse-color",
        "Invalid number of Arguments");
      return( (Image *) NULL);
    }
  error=MagickFalse;
  number_arguments=x;

  /* Allocate and fill in the floating point arguments */
  sparse_arguments=(double *) AcquireQuantumMemory(number_arguments,
    sizeof(*sparse_arguments));
  if (sparse_arguments == (double *) NULL) {
    (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
      "MemoryAllocationFailed","%s","SparseColorOption");
    return( (Image *) NULL);
  }
  (void) memset(sparse_arguments,0,number_arguments*
    sizeof(*sparse_arguments));
  p=arguments;
  x=0;
  while ((*p != '\0') && (x < number_arguments))
  {
    /* X coordinate */
    *token=',';
    while (*token == ',')
      (void) GetNextToken(p,&p,MagickPathExtent,token);
    if (*token == '\0')
      break;
    if ( isalpha((int) ((unsigned char) *token)) || *token == '#' ) {
      (void) ThrowMagickException(exception,GetMagickModule(),
            OptionError, "InvalidArgument", "'%s': %s", "sparse-color",
            "Color found, instead of X-coord");
      error=MagickTrue;
      break;
    }
    sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
    /* Y coordinate */
    *token=',';
    while (*token == ',')
      (void) GetNextToken(p,&p,MagickPathExtent,token);
    if (*token == '\0')
      break;
    if ( isalpha((int) ((unsigned char) *token)) || *token == '#' ) {
      (void) ThrowMagickException(exception,GetMagickModule(),
            OptionError, "InvalidArgument", "'%s': %s", "sparse-color",
            "Color found, instead of Y-coord");
      error=MagickTrue;
      break;
    }
    sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
    /* color name or function given in string argument */
    *token=',';
    while (*token == ',')
      (void) GetNextToken(p,&p,MagickPathExtent,token);
    if (*token == '\0') break;
    if ( isalpha((int) ((unsigned char) *token)) || *token == '#' ) {
      /* Color string given */
      (void) QueryColorCompliance(token,AllCompliance,&color,
                exception);
      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
        sparse_arguments[x++] = QuantumScale*color.red;
      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
        sparse_arguments[x++] = QuantumScale*color.green;
      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
        sparse_arguments[x++] = QuantumScale*color.blue;
      if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
          (image->colorspace == CMYKColorspace))
        sparse_arguments[x++] = QuantumScale*color.black;
      if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
          image->alpha_trait != UndefinedPixelTrait)
        sparse_arguments[x++] = QuantumScale*color.alpha;
    }
    else {
      /* Colors given as a set of floating point values - experimental */
      /* NB: token contains the first floating point value to use! */
      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
        {
          while (*token == ',')
            (void) GetNextToken(p,&p,MagickPathExtent,token);
          if ((*token == '\0') || isalpha((int) ((unsigned char) *token)) || *token == '#' )
            break;
          sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
          *token=','; /* used this token - get another */
        }
      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
        {
          while (*token == ',')
            (void) GetNextToken(p,&p,MagickPathExtent,token);
          if ((*token == '\0') || isalpha((int) ((unsigned char) *token)) || *token == '#' )
            break;
          sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
          *token=','; /* used this token - get another */
        }
      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
        {
          while (*token == ',')
           (void) GetNextToken(p,&p,MagickPathExtent,token);
          if ((*token == '\0') || isalpha((int) ((unsigned char) *token)) || *token == '#' )
            break;
          sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
          *token = ','; /* used this token - get another */
        }
      if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
          (image->colorspace == CMYKColorspace))
        {
          while (*token == ',')
            (void) GetNextToken(p,&p,MagickPathExtent,token);
          if ((*token == '\0') || isalpha((int) ((unsigned char) *token)) || *token == '#' )
            break;
          sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
          *token=','; /* used this token - get another */
        }
      if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
          image->alpha_trait != UndefinedPixelTrait)
        {
          while (*token == ',')
            (void) GetNextToken(p,&p,MagickPathExtent,token);
          if ((*token == '\0') || isalpha((int) ((unsigned char) *token)) || *token == '#' )
            break;
          sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
          *token = ','; /* used this token - get another */
        }
    }
  }
  if (error != MagickFalse)
    {
      sparse_arguments=(double *) RelinquishMagickMemory(sparse_arguments);
      return((Image *) NULL);
    }
  if (number_arguments != x)
    {
      sparse_arguments=(double *) RelinquishMagickMemory(sparse_arguments);
      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
        "InvalidArgument","'%s': %s","sparse-color","Argument Parsing Error");
      return((Image *) NULL);
    }
  /* Call the Sparse Color Interpolation function with the parsed arguments */
  sparse_image=SparseColorImage(image,method,number_arguments,sparse_arguments,
    exception);
  sparse_arguments=(double *) RelinquishMagickMemory(sparse_arguments);
  return( sparse_image );
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   C L I S e t t i n g O p t i o n I n f o                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  CLISettingOptionInfo() applies a single settings option into a CLI wand
%  holding the image_info, draw_info, quantize_info structures that will be
%  used when processing the images.
%
%  These options do no require images to be present in the CLI wand for them
%  to be able to be set, in which case they will generally be applied to image
%  that are read in later
%
%  Options handled by this function are listed in CommandOptions[] of
%  "option.c" that is one of "SettingOptionFlags" option flags.
%
%  The format of the CLISettingOptionInfo method is:
%
%    void CLISettingOptionInfo(MagickCLI *cli_wand,
%               const char *option, const char *arg1, const char *arg2)
%
%  A description of each parameter follows:
%
%    o cli_wand: structure holding settings to be applied
%
%    o option: The option string to be set
%
%    o arg1, arg2: optional argument strings to the operation
%        arg2 is currently only used by "-limit"
%
*/
WandPrivate void CLISettingOptionInfo(MagickCLI *cli_wand,
     const char *option,const char *arg1n, const char *arg2n)
{
  ssize_t
    parse;     /* option argument parsing (string to value table lookup) */

  const char    /* percent escaped versions of the args */
    *arg1,
    *arg2;

#define _image_info       (cli_wand->wand.image_info)
#define _image            (cli_wand->wand.images)
#define _exception        (cli_wand->wand.exception)
#define _draw_info        (cli_wand->draw_info)
#define _quantize_info    (cli_wand->quantize_info)
#define IfSetOption       (*option=='-')
#define ArgBoolean        IfSetOption ? MagickTrue : MagickFalse
#define ArgBooleanNot     IfSetOption ? MagickFalse : MagickTrue
#define ArgBooleanString  (IfSetOption?"true":"false")
#define ArgOption(def)    (IfSetOption?arg1:(const char *)(def))

  assert(cli_wand != (MagickCLI *) NULL);
  assert(cli_wand->signature == MagickWandSignature);
  assert(cli_wand->wand.signature == MagickWandSignature);

  if (cli_wand->wand.debug != MagickFalse)
    (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
         "- Setting Option: %s \"%s\" \"%s\"", option,arg1n,arg2n);

  arg1 = arg1n,
  arg2 = arg2n;

#if 1
#define _process_flags    (cli_wand->process_flags)
#define _option_type      ((CommandOptionFlags) cli_wand->command->flags)
  /* Interpret Percent Escapes in Arguments - using first image */
  if ( (((_process_flags & ProcessInterpretProperities) != 0 )
        || ((_option_type & AlwaysInterpretArgsFlag) != 0)
       )  && ((_option_type & NeverInterpretArgsFlag) == 0) ) {
    /* Interpret Percent escapes in argument 1 */
    if (arg1n != (char *) NULL) {
      arg1=InterpretImageProperties(_image_info,_image,arg1n,_exception);
      if (arg1 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg1=arg1n;  /* use the given argument as is */
      }
    }
    if (arg2n != (char *) NULL) {
      arg2=InterpretImageProperties(_image_info,_image,arg2n,_exception);
      if (arg2 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg2=arg2n;  /* use the given argument as is */
      }
    }
  }
#undef _process_flags
#undef _option_type
#endif

  switch (*(option+1))
  {
    case 'a':
    {
      if (LocaleCompare("adjoin",option+1) == 0)
        {
          _image_info->adjoin = ArgBoolean;
          break;
        }
      if (LocaleCompare("affine",option+1) == 0)
        {
          CLIWandWarnReplaced("-draw 'affine ...'");
          if (IfSetOption)
            (void) ParseAffineGeometry(arg1,&_draw_info->affine,_exception);
          else
            GetAffineMatrix(&_draw_info->affine);
          break;
        }
      if (LocaleCompare("antialias",option+1) == 0)
        {
          _image_info->antialias =
            _draw_info->stroke_antialias =
              _draw_info->text_antialias = ArgBoolean;
          break;
        }
      if (LocaleCompare("attenuate",option+1) == 0)
        {
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,ArgOption("1.0"));
          break;
        }
      if (LocaleCompare("authenticate",option+1) == 0)
        {
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'b':
    {
      if (LocaleCompare("background",option+1) == 0)
        {
          /* FUTURE: both _image_info attribute & ImageOption in use!
             _image_info only used directly for generating new images.
             SyncImageSettings() used to set per-image attribute.

             FUTURE: if _image_info->background_color is not set then
             we should fall back to per-image background_color

             At this time -background will 'wipe out' the per-image
             background color!

             Better error handling of QueryColorCompliance() needed.
          */
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          (void) QueryColorCompliance(ArgOption(MogrifyBackgroundColor),AllCompliance,
             &_image_info->background_color,_exception);
          break;
        }
      if (LocaleCompare("bias",option+1) == 0)
        {
          /* FUTURE: bias OBSOLETED, replaced by Artifact "convolve:bias"
             as it is actually rarely used except in direct convolve operations
             Usage outside a direct convolve operation is actally non-sensible!

             SyncImageSettings() used to set per-image attribute.
          */
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,"convolve:bias",ArgOption(NULL));
          break;
        }
      if (LocaleCompare("black-point-compensation",option+1) == 0)
        {
          /* Used as a image chromaticity setting
             SyncImageSettings() used to set per-image attribute.
          */
          (void) SetImageOption(_image_info,option+1,ArgBooleanString);
          break;
        }
      if (LocaleCompare("blue-primary",option+1) == 0)
        {
          /* Image chromaticity X,Y  NB: Y=X if Y not defined
             Used by many coders including PNG
             SyncImageSettings() used to set per-image attribute.
          */
          arg1=ArgOption("0.0");
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("bordercolor",option+1) == 0)
        {
          /* FUTURE: both _image_info attribute & ImageOption in use!
             SyncImageSettings() used to set per-image attribute.
             Better error checking of QueryColorCompliance().
          */
          if (IfSetOption)
            {
              (void) SetImageOption(_image_info,option+1,arg1);
              (void) QueryColorCompliance(arg1,AllCompliance,
                  &_image_info->border_color,_exception);
              (void) QueryColorCompliance(arg1,AllCompliance,
                  &_draw_info->border_color,_exception);
              break;
            }
          (void) DeleteImageOption(_image_info,option+1);
          (void) QueryColorCompliance(MogrifyBorderColor,AllCompliance,
            &_image_info->border_color,_exception);
          (void) QueryColorCompliance(MogrifyBorderColor,AllCompliance,
            &_draw_info->border_color,_exception);
          break;
        }
      if (LocaleCompare("box",option+1) == 0)
        {
          CLIWandWarnReplaced("-undercolor");
          CLISettingOptionInfo(cli_wand,"-undercolor",arg1, arg2);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'c':
    {
      if (LocaleCompare("cache",option+1) == 0)
        {
          MagickSizeType
            limit;

          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          limit=MagickResourceInfinity;
          if (LocaleCompare("unlimited",arg1) != 0)
            limit=(MagickSizeType) SiPrefixToDoubleInterval(arg1,100.0);
          (void) SetMagickResourceLimit(MemoryResource,limit);
          (void) SetMagickResourceLimit(MapResource,2*limit);
          break;
        }
      if (LocaleCompare("caption",option+1) == 0)
        {
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          break;
        }
      if (LocaleCompare("colorspace",option+1) == 0)
        {
          /* Setting used for new images via AquireImage()
             But also used as a SimpleImageOperator
             Undefined colorspace means don't modify images on
             read or as a operation */
          parse=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
             ArgOption("undefined"));
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedColorspace",option,
              arg1);
          _image_info->colorspace=(ColorspaceType) parse;
          break;
        }
      if (LocaleCompare("comment",option+1) == 0)
        {
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          break;
        }
      if (LocaleCompare("compose",option+1) == 0)
        {
          /* FUTURE: _image_info should be used,
             SyncImageSettings() used to set per-image attribute. - REMOVE

             This setting should NOT be used to set image 'compose'
             "-layer" operators shoud use _image_info if defined otherwise
             they should use a per-image compose setting.
          */
          parse = ParseCommandOption(MagickComposeOptions,MagickFalse,
                          ArgOption("undefined"));
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedComposeOperator",
                                      option,arg1);
          _image_info->compose=(CompositeOperator) parse;
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          break;
        }
      if (LocaleCompare("compress",option+1) == 0)
        {
          /* FUTURE: What should be used?  _image_info  or ImageOption ???
             The former is more efficent, but Crisy prefers the latter!
             SyncImageSettings() used to set per-image attribute.

             The coders appears to use _image_info, not Image_Option
             however the image attribute (for save) is set from the
             ImageOption!

             Note that "undefined" is a different setting to "none".
          */
          parse = ParseCommandOption(MagickCompressOptions,MagickFalse,
                     ArgOption("undefined"));
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedImageCompression",
                                      option,arg1);
          _image_info->compression=(CompressionType) parse;
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'd':
    {
      if (LocaleCompare("debug",option+1) == 0)
        {
          /* SyncImageSettings() used to set per-image attribute. */
          arg1=ArgOption("none");
          parse = ParseCommandOption(MagickLogEventOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedEventType",
                                      option,arg1);
          (void) SetLogEventMask(arg1);
          _image_info->debug=IsEventLogging();   /* extract logging*/
          cli_wand->wand.debug=IsEventLogging();
          break;
        }
      if (LocaleCompare("define",option+1) == 0)
        {
          if (LocaleNCompare(arg1,"registry:",9) == 0)
            {
              if (IfSetOption)
                (void) DefineImageRegistry(StringRegistryType,arg1+9,_exception);
              else
                (void) DeleteImageRegistry(arg1+9);
              break;
            }
          /* DefineImageOption() equals SetImageOption() but with '=' */
          if (IfSetOption)
            (void) DefineImageOption(_image_info,arg1);
          else if (DeleteImageOption(_image_info,arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"NoSuchOption",option,arg1);
          break;
        }
      if (LocaleCompare("delay",option+1) == 0)
        {
          /* Only used for new images via AcquireImage()
             FUTURE: Option should also be used for "-morph" (color morphing)
          */
          arg1=ArgOption("0");
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("density",option+1) == 0)
        {
          /* FUTURE: strings used in _image_info attr and _draw_info!
             Basically as density can be in a XxY form!

             SyncImageSettings() used to set per-image attribute.
          */
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          (void) CloneString(&_image_info->density,ArgOption(NULL));
          (void) CloneString(&_draw_info->density,_image_info->density);
          break;
        }
      if (LocaleCompare("depth",option+1) == 0)
        {
          /* This is also a SimpleImageOperator! for 8->16 vaule trunc !!!!
             SyncImageSettings() used to set per-image attribute.
          */
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          _image_info->depth=IfSetOption?StringToUnsignedLong(arg1)
                                       :MAGICKCORE_QUANTUM_DEPTH;
          break;
        }
      if (LocaleCompare("direction",option+1) == 0)
        {
          /* Image Option is only used to set _draw_info */
          arg1=ArgOption("undefined");
          parse = ParseCommandOption(MagickDirectionOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedDirectionType",
                                      option,arg1);
          _draw_info->direction=(DirectionType) parse;
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("display",option+1) == 0)
        {
          (void) CloneString(&_image_info->server_name,ArgOption(NULL));
          (void) CloneString(&_draw_info->server_name,_image_info->server_name);
          break;
        }
      if (LocaleCompare("dispose",option+1) == 0)
        {
          /* only used in setting new images */
          arg1=ArgOption("undefined");
          parse = ParseCommandOption(MagickDisposeOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedDisposeMethod",
                                      option,arg1);
          (void) SetImageOption(_image_info,option+1,ArgOption("undefined"));
          break;
        }
      if (LocaleCompare("dissimilarity-threshold",option+1) == 0)
        {
          /* FUTURE: this is only used by CompareImages() which is used
             only by the "compare" CLI program at this time.  */
          arg1=ArgOption(DEFAULT_DISSIMILARITY_THRESHOLD);
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("dither",option+1) == 0)
        {
          /* _image_info attr (on/off), _quantize_info attr (on/off)
             but also ImageInfo and _quantize_info method!
             FUTURE: merge the duality of the dithering options
          */
          _image_info->dither = ArgBoolean;
          (void) SetImageOption(_image_info,option+1,ArgOption("none"));
          _quantize_info->dither_method=(DitherMethod) ParseCommandOption(
             MagickDitherOptions,MagickFalse,ArgOption("none"));
          if (_quantize_info->dither_method == NoDitherMethod)
            _image_info->dither = MagickFalse;
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'e':
    {
      if (LocaleCompare("encoding",option+1) == 0)
        {
          (void) CloneString(&_draw_info->encoding,ArgOption("undefined"));
          (void) SetImageOption(_image_info,option+1,_draw_info->encoding);
          break;
        }
      if (LocaleCompare("endian",option+1) == 0)
        {
          /* Both _image_info attr and ImageInfo */
          arg1 = ArgOption("undefined");
          parse = ParseCommandOption(MagickEndianOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedEndianType",
                                      option,arg1);
          /* FUTURE: check alloc/free of endian string!  - remove? */
          _image_info->endian=(EndianType) (*arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("extract",option+1) == 0)
        {
          (void) CloneString(&_image_info->extract,ArgOption(NULL));
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'f':
    {
      if (LocaleCompare("family",option+1) == 0)
        {
          (void) CloneString(&_draw_info->family,ArgOption(NULL));
          break;
        }
      if (LocaleCompare("features",option+1) == 0)
        {
          (void) SetImageOption(_image_info,"identify:features",
            ArgBooleanString);
          if (IfSetOption)
            (void) SetImageArtifact(_image,"verbose","true");
          break;
        }
      if (LocaleCompare("fill",option+1) == 0)
        {
          /* Set "fill" OR "fill-pattern" in _draw_info
             The original fill color is preserved if a fill-pattern is given.
             That way it does not effect other operations that directly using
             the fill color and, can be retored using "+tile".
          */
          MagickBooleanType
            status;

          ExceptionInfo
            *sans;

          PixelInfo
            color;

          arg1 = ArgOption("none");  /* +fill turns it off! */
          (void) SetImageOption(_image_info,option+1,arg1);
          if (_draw_info->fill_pattern != (Image *) NULL)
            _draw_info->fill_pattern=DestroyImage(_draw_info->fill_pattern);

          /* is it a color or a image? -- ignore exceptions */
          sans=AcquireExceptionInfo();
          status=QueryColorCompliance(arg1,AllCompliance,&color,sans);
          sans=DestroyExceptionInfo(sans);

          if (status == MagickFalse)
            _draw_info->fill_pattern=GetImageCache(_image_info,arg1,_exception);
          else
            _draw_info->fill=color;
          break;
        }
      if (LocaleCompare("filter",option+1) == 0)
        {
          /* SyncImageSettings() used to set per-image attribute. */
          arg1 = ArgOption("undefined");
          parse = ParseCommandOption(MagickFilterOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedImageFilter",
                                      option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("font",option+1) == 0)
        {
          (void) CloneString(&_draw_info->font,ArgOption(NULL));
          (void) CloneString(&_image_info->font,_draw_info->font);
          break;
        }
      if (LocaleCompare("format",option+1) == 0)
        {
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          break;
        }
      if (LocaleCompare("fuzz",option+1) == 0)
        {
          /* Option used to set image fuzz! unless blank canvas (from color)
             Image attribute used for color compare operations
             SyncImageSettings() used to set per-image attribute.

             FUTURE: Can't find anything else using _image_info->fuzz directly!
                     convert structure attribute to 'option' string
          */
          arg1=ArgOption("0");
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          _image_info->fuzz=StringToDoubleInterval(arg1,(double)
                QuantumRange+1.0);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'g':
    {
      if (LocaleCompare("gravity",option+1) == 0)
        {
          /* SyncImageSettings() used to set per-image attribute. */
          arg1 = ArgOption("none");
          parse = ParseCommandOption(MagickGravityOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedGravityType",
                                      option,arg1);
          _draw_info->gravity=(GravityType) parse;
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("green-primary",option+1) == 0)
        {
          /* Image chromaticity X,Y  NB: Y=X if Y not defined
             SyncImageSettings() used to set per-image attribute.
             Used directly by many coders
          */
          arg1=ArgOption("0.0");
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'h':
    {
      if (LocaleCompare("highlight-color",option+1) == 0)
        {
          /* FUTURE: this is only used by CompareImages() which is used
             only by the "compare" CLI program at this time.  */
          (void) SetImageOption(_image_info,"compare:highlight-color",
            ArgOption(NULL));
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'i':
    {
      if (LocaleCompare("illuminant",option+1) == 0)
        {
          (void) SetImageOption(_image_info,"color:illuminant",
            ArgOption(NULL));
          break;
        }
      if (LocaleCompare("intensity",option+1) == 0)
        {
          arg1 = ArgOption("undefined");
          parse = ParseCommandOption(MagickPixelIntensityOptions,MagickFalse,
            arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedIntensityType",
              option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("intent",option+1) == 0)
        {
          /* Only used by coders: MIFF, MPC, BMP, PNG
             and for image profile call to AcquireTransformThreadSet()
             SyncImageSettings() used to set per-image attribute.
          */
          arg1 = ArgOption("undefined");
          parse = ParseCommandOption(MagickIntentOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedIntentType",
                                      option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("interlace",option+1) == 0)
        {
          /* _image_info is directly used by coders (so why an image setting?)
             SyncImageSettings() used to set per-image attribute.
          */
          arg1 = ArgOption("undefined");
          parse = ParseCommandOption(MagickInterlaceOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedInterlaceType",
                                      option,arg1);
          _image_info->interlace=(InterlaceType) parse;
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("interline-spacing",option+1) == 0)
        {
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1, ArgOption(NULL));
          _draw_info->interline_spacing=StringToDouble(ArgOption("0"),
               (char **) NULL);
          break;
        }
      if (LocaleCompare("interpolate",option+1) == 0)
        {
          /* SyncImageSettings() used to set per-image attribute. */
          arg1 = ArgOption("undefined");
          parse = ParseCommandOption(MagickInterpolateOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedInterpolateMethod",
                                      option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("interword-spacing",option+1) == 0)
        {
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1, ArgOption(NULL));
          _draw_info->interword_spacing=StringToDouble(ArgOption("0"),(char **) NULL);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'k':
    {
      if (LocaleCompare("kerning",option+1) == 0)
        {
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          _draw_info->kerning=StringToDouble(ArgOption("0"),(char **) NULL);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'l':
    {
      if (LocaleCompare("label",option+1) == 0)
        {
          /* only used for new images - not in SyncImageOptions() */
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          break;
        }
      if (LocaleCompare("limit",option+1) == 0)
        {
          MagickSizeType
            limit;

          limit=MagickResourceInfinity;
          parse= ParseCommandOption(MagickResourceOptions,MagickFalse,arg1);
          if ( parse < 0 )
            CLIWandExceptArgBreak(OptionError,"UnrecognizedResourceType",
                option,arg1);
          if (LocaleCompare("unlimited",arg2) != 0)
            limit=(MagickSizeType) SiPrefixToDoubleInterval(arg2,100.0);
          (void) SetMagickResourceLimit((ResourceType)parse,limit);
          break;
        }
      if (LocaleCompare("log",option+1) == 0)
        {
          if (IfSetOption) {
            if ((strchr(arg1,'%') == (char *) NULL))
              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
            (void) SetLogFormat(arg1);
          }
          break;
        }
      if (LocaleCompare("lowlight-color",option+1) == 0)
        {
          /* FUTURE: this is only used by CompareImages() which is used
             only by the "compare" CLI program at this time.  */
          (void) SetImageOption(_image_info,"compare:lowlight-color",
            ArgOption(NULL));
          break;
        }
      if (LocaleCompare("loop",option+1) == 0)
        {
          /* SyncImageSettings() used to set per-image attribute. */
          arg1=ArgOption("0");
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'm':
    {
      if (LocaleCompare("mattecolor",option+1) == 0)
        {
          /* SyncImageSettings() used to set per-image attribute. */
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          (void) QueryColorCompliance(ArgOption(MogrifyAlphaColor),
            AllCompliance,&_image_info->matte_color,_exception);
          break;
        }
      if (LocaleCompare("metric",option+1) == 0)
        {
          /* FUTURE: this is only used by CompareImages() which is used
             only by the "compare" CLI program at this time.  */
          parse=ParseCommandOption(MagickMetricOptions,MagickFalse,arg1);
          if ( parse < 0 )
            CLIWandExceptArgBreak(OptionError,"UnrecognizedMetricType",
                option,arg1);
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          break;
        }
      if (LocaleCompare("moments",option+1) == 0)
        {
          (void) SetImageOption(_image_info,"identify:moments",
            ArgBooleanString);
          if (IfSetOption)
            (void) SetImageArtifact(_image,"verbose","true");
          break;
        }
      if (LocaleCompare("monitor",option+1) == 0)
        {
          (void) SetImageInfoProgressMonitor(_image_info, IfSetOption?
                MonitorProgress: (MagickProgressMonitor) NULL, (void *) NULL);
          break;
        }
      if (LocaleCompare("monochrome",option+1) == 0)
        {
          /* Setting (used by some input coders!) -- why?
             Warning: This is also Special '-type' SimpleOperator
          */
          _image_info->monochrome= ArgBoolean;
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'o':
    {
      if (LocaleCompare("orient",option+1) == 0)
        {
          /* Is not used when defining for new images.
             This makes it more of a 'operation' than a setting
             FUTURE: make set meta-data operator instead.
             SyncImageSettings() used to set per-image attribute.
          */
          parse=ParseCommandOption(MagickOrientationOptions,MagickFalse,
               ArgOption("undefined"));
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedImageOrientation",
                                      option,arg1);
          _image_info->orientation=(OrientationType)parse;
          (void) SetImageOption(_image_info,option+1, ArgOption(NULL));
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'p':
    {
      if (LocaleCompare("page",option+1) == 0)
        {
          /* Only used for new images and image generators.
             SyncImageSettings() used to set per-image attribute. ?????
             That last is WRONG!!!!
             FUTURE: adjust named 'page' sizes according density
          */
          char
            *canonical_page,
            page[MagickPathExtent];

          const char
            *image_option;

          MagickStatusType
            flags;

          RectangleInfo
            geometry;

          if (!IfSetOption)
            {
              (void) DeleteImageOption(_image_info,option+1);
              (void) CloneString(&_image_info->page,(char *) NULL);
              break;
            }
          (void) memset(&geometry,0,sizeof(geometry));
          image_option=GetImageOption(_image_info,"page");
          if (image_option != (const char *) NULL)
            flags=ParseAbsoluteGeometry(image_option,&geometry);
          canonical_page=GetPageGeometry(arg1);
          flags=ParseAbsoluteGeometry(canonical_page,&geometry);
          canonical_page=DestroyString(canonical_page);
          (void) FormatLocaleString(page,MagickPathExtent,"%lux%lu",
            (unsigned long) geometry.width,(unsigned long) geometry.height);
          if (((flags & XValue) != 0) || ((flags & YValue) != 0))
            (void) FormatLocaleString(page,MagickPathExtent,"%lux%lu%+ld%+ld",
              (unsigned long) geometry.width,(unsigned long) geometry.height,
              (long) geometry.x,(long) geometry.y);
          (void) SetImageOption(_image_info,option+1,page);
          (void) CloneString(&_image_info->page,page);
          break;
        }
      if (LocaleCompare("ping",option+1) == 0)
        {
          _image_info->ping=ArgBoolean;
          break;
        }
      if (LocaleCompare("pointsize",option+1) == 0)
        {
          if (IfSetOption) {
            if (IsGeometry(arg1) == MagickFalse)
              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
            _image_info->pointsize =
            _draw_info->pointsize =
              StringToDouble(arg1,(char **) NULL);
          }
          else {
            _image_info->pointsize=0.0; /* unset pointsize */
            _draw_info->pointsize=12.0;
          }
          break;
        }
      if (LocaleCompare("precision",option+1) == 0)
        {
          arg1=ArgOption("-1");
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetMagickPrecision(StringToInteger(arg1));
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'q':
    {
      if (LocaleCompare("quality",option+1) == 0)
        {
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          _image_info->quality= IfSetOption ? StringToUnsignedLong(arg1)
                                            : UNDEFINED_COMPRESSION_QUALITY;
          (void) SetImageOption(_image_info,option+1,ArgOption("0"));
          break;
        }
      if (LocaleCompare("quantize",option+1) == 0)
        {
          /* Just a set direct in _quantize_info */
          arg1=ArgOption("undefined");
          parse=ParseCommandOption(MagickColorspaceOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedColorspace",
                 option,arg1);
          _quantize_info->colorspace=(ColorspaceType)parse;
          break;
        }
      if (LocaleCompare("quiet",option+1) == 0)
        {
          /* FUTURE: if two -quiet is performed you can not do +quiet!
             This needs to be checked over thoughly.
          */
          static WarningHandler
            warning_handler = (WarningHandler) NULL;

          WarningHandler
            tmp = SetWarningHandler((WarningHandler) NULL);

          if ( tmp != (WarningHandler) NULL)
            warning_handler = tmp; /* remember the old handler */
          if (!IfSetOption)        /* set the old handler */
            warning_handler=SetWarningHandler(warning_handler);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'r':
    {
      if (LocaleCompare("red-primary",option+1) == 0)
        {
          /* Image chromaticity X,Y  NB: Y=X if Y not defined
             Used by many coders
             SyncImageSettings() used to set per-image attribute.
          */
          arg1=ArgOption("0.0");
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("regard-warnings",option+1) == 0)
        /* FUTURE: to be replaced by a 'fatal-level' type setting */
        break;
      if (LocaleCompare("render",option+1) == 0)
        {
          /* _draw_info only setting */
          _draw_info->render= ArgBooleanNot;
          break;
        }
      if (LocaleCompare("respect-parenthesis",option+1) == 0)
        {
          /* link image and setting stacks - option is itself saved on stack! */
          (void) SetImageOption(_image_info,option+1,ArgBooleanString);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 's':
    {
      if (LocaleCompare("sampling-factor",option+1) == 0)
        {
          /* FUTURE: should be converted to jpeg:sampling_factor */
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) CloneString(&_image_info->sampling_factor,ArgOption(NULL));
          break;
        }
      if (LocaleCompare("scene",option+1) == 0)
        {
          /* SyncImageSettings() used to set this as a per-image attribute.
             What ??? Why ????
          */
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          _image_info->scene=StringToUnsignedLong(ArgOption("0"));
          break;
        }
      if (LocaleCompare("seed",option+1) == 0)
        {
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          SetRandomSecretKey(
               IfSetOption ? (unsigned long) StringToUnsignedLong(arg1)
                           : (unsigned long) time((time_t *) NULL));
          break;
        }
      if (LocaleCompare("size",option+1) == 0)
        {
          /* FUTURE: string in _image_info -- convert to Option ???
             Look at the special handling for "size" in SetImageOption()
           */
          (void) CloneString(&_image_info->size,ArgOption(NULL));
          break;
        }
      if (LocaleCompare("stretch",option+1) == 0)
        {
          arg1=ArgOption("undefined");
          parse = ParseCommandOption(MagickStretchOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedStretchType",
                 option,arg1);
          _draw_info->stretch=(StretchType) parse;
          break;
        }
      if (LocaleCompare("stroke",option+1) == 0)
        {
          /* set stroke color OR stroke-pattern
             UPDATE: ensure stroke color is not destroyed is a pattern
             is given. Just in case the color is also used for other purposes.
           */
          MagickBooleanType
            status;

          ExceptionInfo
            *sans;

          PixelInfo
            color;

          arg1 = ArgOption("none");  /* +fill turns it off! */
          (void) SetImageOption(_image_info,option+1,arg1);
          if (_draw_info->stroke_pattern != (Image *) NULL)
            _draw_info->stroke_pattern=DestroyImage(_draw_info->stroke_pattern);

          /* is it a color or a image? -- ignore exceptions */
          sans=AcquireExceptionInfo();
          status=QueryColorCompliance(arg1,AllCompliance,&color,sans);
          sans=DestroyExceptionInfo(sans);

          if (status == MagickFalse)
            _draw_info->stroke_pattern=GetImageCache(_image_info,arg1,_exception);
          else
            _draw_info->stroke=color;
          break;
        }
      if (LocaleCompare("strokewidth",option+1) == 0)
        {
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          _draw_info->stroke_width=StringToDouble(ArgOption("1.0"),
               (char **) NULL);
          break;
        }
      if (LocaleCompare("style",option+1) == 0)
        {
          arg1=ArgOption("undefined");
          parse = ParseCommandOption(MagickStyleOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedStyleType",
                 option,arg1);
          _draw_info->style=(StyleType) parse;
          break;
        }
#if 0
      if (LocaleCompare("subimage-search",option+1) == 0)
        {
        /* FUTURE: this is only used by CompareImages() which is used
            only by the "compare" CLI program at this time.  */
          (void) SetImageOption(_image_info,option+1,ArgBooleanString);
          break;
        }
#endif
      if (LocaleCompare("synchronize",option+1) == 0)
        {
          /* FUTURE: syncronize to storage - but what does that mean? */
          _image_info->synchronize = ArgBoolean;
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 't':
    {
      if (LocaleCompare("taint",option+1) == 0)
        {
          /* SyncImageSettings() used to set per-image attribute. */
          (void) SetImageOption(_image_info,option+1,ArgBooleanString);
          break;
        }
      if (LocaleCompare("texture",option+1) == 0)
        {
          /* Note: arguments do not have percent escapes expanded */
          /* FUTURE: move _image_info string to option splay-tree
             Other than "montage" what uses "texture" ????
          */
          (void) CloneString(&_image_info->texture,ArgOption(NULL));
          break;
        }
      if (LocaleCompare("tile",option+1) == 0)
        {
          /* Note: arguments do not have percent escapes expanded */
          _draw_info->fill_pattern=IfSetOption
                                 ?GetImageCache(_image_info,arg1,_exception)
                                 :DestroyImage(_draw_info->fill_pattern);
          break;
        }
      if (LocaleCompare("tile-offset",option+1) == 0)
        {
          /* SyncImageSettings() used to set per-image attribute. ??? */
          arg1=ArgOption("0");
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("transparent-color",option+1) == 0)
        {
          /* FUTURE: both _image_info attribute & ImageOption in use!
             _image_info only used for generating new images.
             SyncImageSettings() used to set per-image attribute.

             Note that +transparent-color, means fall-back to image
             attribute so ImageOption is deleted, not set to a default.
          */
          if (IfSetOption && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          (void) QueryColorCompliance(ArgOption("none"),AllCompliance,
              &_image_info->transparent_color,_exception);
          break;
        }
      if (LocaleCompare("treedepth",option+1) == 0)
        {
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          _quantize_info->tree_depth=StringToUnsignedLong(ArgOption("0"));
          break;
        }
      if (LocaleCompare("type",option+1) == 0)
        {
          /* SyncImageSettings() used to set per-image attribute. */
          parse=ParseCommandOption(MagickTypeOptions,MagickFalse,
               ArgOption("undefined"));
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedImageType",
                 option,arg1);
          _image_info->type=(ImageType) parse;
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'u':
    {
      if (LocaleCompare("undercolor",option+1) == 0)
        {
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          (void) QueryColorCompliance(ArgOption("none"),AllCompliance,
               &_draw_info->undercolor,_exception);
          break;
        }
      if (LocaleCompare("units",option+1) == 0)
        {
          /* SyncImageSettings() used to set per-image attribute.
             Should this effect _draw_info X and Y resolution?
             FUTURE: this probably should be part of the density setting
          */
          parse=ParseCommandOption(MagickResolutionOptions,MagickFalse,
               ArgOption("undefined"));
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedUnitsType",
                 option,arg1);
          _image_info->units=(ResolutionType) parse;
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'v':
    {
      if (LocaleCompare("verbose",option+1) == 0)
        {
          /* FUTURE: Remember all options become image artifacts
             _image_info->verbose is only used by coders.
          */
          (void) SetImageOption(_image_info,option+1,ArgBooleanString);
          _image_info->verbose= ArgBoolean;
          _image_info->ping=MagickFalse; /* verbose can't be a ping */
          break;
        }
      if (LocaleCompare("virtual-pixel",option+1) == 0)
        {
          /* SyncImageSettings() used to set per-image attribute.
             This is VERY deep in the image caching structure.
          */
          parse=ParseCommandOption(MagickVirtualPixelOptions,MagickFalse,
               ArgOption("undefined"));
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedVirtualPixelMethod",
                 option,arg1);
          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'w':
    {
      if (LocaleCompare("weight",option+1) == 0)
        {
          ssize_t
            weight;

          weight=ParseCommandOption(MagickWeightOptions,MagickFalse,arg1);
          if (weight == -1)
            weight=(ssize_t) StringToUnsignedLong(arg1);
          _draw_info->weight=(size_t) weight;
          break;
        }
      if (LocaleCompare("white-point",option+1) == 0)
        {
          /* Used as a image chromaticity setting
             SyncImageSettings() used to set per-image attribute.
          */
          arg1=ArgOption("0.0");
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    default:
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
  }

  /* clean up percent escape interpreted strings */
  if ((arg1 && arg1n) && (arg1 != arg1n ))
    arg1=DestroyString((char *) arg1);
  if ((arg2 && arg2n) && (arg2 != arg2n ))
    arg2=DestroyString((char *) arg2);

#undef _image_info
#undef _exception
#undef _draw_info
#undef _quantize_info
#undef IfSetOption
#undef ArgBoolean
#undef ArgBooleanNot
#undef ArgBooleanString
#undef ArgOption

  return;
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
+     C L I S i m p l e O p e r a t o r I m a g e s                           %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  CLISimpleOperatorImages() applys one simple image operation given to all
%  the images in the CLI wand, using any per-image or global settings that was
%  previously saved in the CLI wand.
%
%  It is assumed that any such settings are up-to-date.
%
%  The format of the WandSimpleOperatorImages method is:
%
%    MagickBooleanType CLISimpleOperatorImages(MagickCLI *cli_wand,const char *option,
%      const char *arg1, const char *arg2,ExceptionInfo *exception)
%
%  A description of each parameter follows:
%
%    o cli_wand: structure holding settings and images to be operated on
%
%    o option:  The option string for the operation
%
%    o arg1, arg2: optional argument strings to the operation
%
*/

/*
  CLISimpleOperatorImage() is an Internal subrountine to apply one simple
  image operation to the current image pointed to by the CLI wand.

  The image in the list may be modified in three different ways...
    * directly modified (EG: -negate, -gamma, -level, -annotate, -draw),
    * replaced by a new image (EG: -spread, -resize, -rotate, -morphology)
    * one image replace by a list of images (-separate and -crop only!)

  In each case the result replaces the single original image in the list, as
  well as the pointer to the modified image (last image added if replaced by a
  list of images) is returned.

  As the image pointed to may be replaced, the first image in the list may
  also change.  GetFirstImageInList() should be used by caller if they wish
  return the Image pointer to the first image in list.
*/
static MagickBooleanType CLISimpleOperatorImage(MagickCLI *cli_wand,
  const char *option, const char *arg1n, const char *arg2n,
  ExceptionInfo *exception)
{
  Image *
    new_image;

  GeometryInfo
    geometry_info;

  RectangleInfo
    geometry;

  MagickStatusType
    flags;

  ssize_t
    parse;

  const char    /* percent escaped versions of the args */
    *arg1,
    *arg2;

#define _image_info       (cli_wand->wand.image_info)
#define _image            (cli_wand->wand.images)
#define _exception        (cli_wand->wand.exception)
#define _draw_info        (cli_wand->draw_info)
#define _quantize_info    (cli_wand->quantize_info)
#define _process_flags    (cli_wand->process_flags)
#define _option_type      ((CommandOptionFlags) cli_wand->command->flags)
#define IfNormalOp        (*option=='-')
#define IfPlusOp          (*option!='-')
#define IsNormalOp        IfNormalOp ? MagickTrue : MagickFalse
#define IsPlusOp          IfNormalOp ? MagickFalse : MagickTrue

  assert(cli_wand != (MagickCLI *) NULL);
  assert(cli_wand->signature == MagickWandSignature);
  assert(cli_wand->wand.signature == MagickWandSignature);
  assert(_image != (Image *) NULL);             /* an image must be present */
  if (cli_wand->wand.debug != MagickFalse)
    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);

  arg1 = arg1n,
  arg2 = arg2n;

  /* Interpret Percent Escapes in Arguments - using first image */
  if ( (((_process_flags & ProcessInterpretProperities) != 0 )
        || ((_option_type & AlwaysInterpretArgsFlag) != 0)
       )  && ((_option_type & NeverInterpretArgsFlag) == 0) ) {
    /* Interpret Percent escapes in argument 1 */
    if (arg1n != (char *) NULL) {
      arg1=InterpretImageProperties(_image_info,_image,arg1n,_exception);
      if (arg1 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg1=arg1n;  /* use the given argument as is */
      }
    }
    if (arg2n != (char *) NULL) {
      arg2=InterpretImageProperties(_image_info,_image,arg2n,_exception);
      if (arg2 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg2=arg2n;  /* use the given argument as is */
      }
    }
  }
#undef _process_flags
#undef _option_type

#if 0
  (void) FormatLocaleFile(stderr,
    "CLISimpleOperatorImage: \"%s\" \"%s\" \"%s\"\n",option,arg1,arg2);
#endif

  new_image = (Image *) NULL; /* the replacement image, if not null at end */
  SetGeometryInfo(&geometry_info);

  switch (*(option+1))
  {
    case 'a':
    {
      if (LocaleCompare("adaptive-blur",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          new_image=AdaptiveBlurImage(_image,geometry_info.rho,
            geometry_info.sigma,_exception);
          break;
        }
      if (LocaleCompare("adaptive-resize",option+1) == 0)
        {
          /* FUTURE: Roll into a resize special operator */
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
          new_image=AdaptiveResizeImage(_image,geometry.width,geometry.height,
            _exception);
          break;
        }
      if (LocaleCompare("adaptive-sharpen",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          new_image=AdaptiveSharpenImage(_image,geometry_info.rho,
            geometry_info.sigma,_exception);
          break;
        }
      if (LocaleCompare("alpha",option+1) == 0)
        {
          parse=ParseCommandOption(MagickAlphaChannelOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedAlphaChannelOption",
              option,arg1);
          (void) SetImageAlphaChannel(_image,(AlphaChannelOption) parse,
            _exception);
          break;
        }
      if (LocaleCompare("annotate",option+1) == 0)
        {
          char
            buffer[MagickPathExtent];

          SetGeometryInfo(&geometry_info);
          flags=ParseGeometry(arg1,&geometry_info);
          if (flags == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=geometry_info.rho;
          (void) CloneString(&_draw_info->text,arg2);
          (void) FormatLocaleString(buffer,MagickPathExtent,"%+f%+f",
            geometry_info.xi,geometry_info.psi);
          (void) CloneString(&_draw_info->geometry,buffer);
          _draw_info->affine.sx=cos(DegreesToRadians(
            fmod(geometry_info.rho,360.0)));
          _draw_info->affine.rx=sin(DegreesToRadians(
            fmod(geometry_info.rho,360.0)));
          _draw_info->affine.ry=(-sin(DegreesToRadians(
            fmod(geometry_info.sigma,360.0))));
          _draw_info->affine.sy=cos(DegreesToRadians(
            fmod(geometry_info.sigma,360.0)));
          (void) AnnotateImage(_image,_draw_info,_exception);
          GetAffineMatrix(&_draw_info->affine);
          break;
        }
      if (LocaleCompare("auto-gamma",option+1) == 0)
        {
          (void) AutoGammaImage(_image,_exception);
          break;
        }
      if (LocaleCompare("auto-level",option+1) == 0)
        {
          (void) AutoLevelImage(_image,_exception);
          break;
        }
      if (LocaleCompare("auto-orient",option+1) == 0)
        {
          new_image=AutoOrientImage(_image,_image->orientation,_exception);
          break;
        }
      if (LocaleCompare("auto-threshold",option+1) == 0)
        {
          AutoThresholdMethod
            method;

          method=(AutoThresholdMethod) ParseCommandOption(
            MagickAutoThresholdOptions,MagickFalse,arg1);
          (void) AutoThresholdImage(_image,method,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'b':
    {
      if (LocaleCompare("bilateral-blur",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=geometry_info.rho;
          if ((flags & XiValue) == 0)
            geometry_info.xi=1.0*sqrt(geometry_info.rho*geometry_info.rho+
              geometry_info.sigma*geometry_info.sigma);
          if ((flags & PsiValue) == 0)
            geometry_info.psi=0.25*sqrt(geometry_info.rho*geometry_info.rho+
              geometry_info.sigma*geometry_info.sigma);
          new_image=BilateralBlurImage(_image,(size_t) geometry_info.rho,
            (size_t) geometry_info.sigma,geometry_info.xi,geometry_info.psi,
           _exception);
          break;
        }
      if (LocaleCompare("black-threshold",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) BlackThresholdImage(_image,arg1,_exception);
          break;
        }
      if (LocaleCompare("blue-shift",option+1) == 0)
        {
          geometry_info.rho=1.5;
          if (IfNormalOp) {
            flags=ParseGeometry(arg1,&geometry_info);
            if ((flags & RhoValue) == 0)
              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          }
          new_image=BlueShiftImage(_image,geometry_info.rho,_exception);
          break;
        }
      if (LocaleCompare("blur",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          new_image=BlurImage(_image,geometry_info.rho,geometry_info.sigma,
           _exception);
          break;
        }
      if (LocaleCompare("border",option+1) == 0)
        {
          CompositeOperator
            compose;

          const char*
            value;

          flags=ParsePageGeometry(_image,arg1,&geometry,_exception);
          if ((flags & (WidthValue | HeightValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          compose=OverCompositeOp;
          value=GetImageOption(_image_info,"compose");
          if (value != (const char *) NULL)
            compose=(CompositeOperator) ParseCommandOption(MagickComposeOptions,
              MagickFalse,value);
          new_image=BorderImage(_image,&geometry,compose,_exception);
          break;
        }
      if (LocaleCompare("brightness-contrast",option+1) == 0)
        {
          double
            brightness,
            contrast;

          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          brightness=geometry_info.rho;
          contrast=0.0;
          if ((flags & SigmaValue) != 0)
            contrast=geometry_info.sigma;
          (void) BrightnessContrastImage(_image,brightness,contrast,
            _exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'c':
    {
      if (LocaleCompare("canny",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          if ((flags & XiValue) == 0)
            geometry_info.xi=10;
          if ((flags & PsiValue) == 0)
            geometry_info.psi=30;
          if ((flags & PercentValue) != 0)
            {
              geometry_info.xi/=100.0;
              geometry_info.psi/=100.0;
            }
          new_image=CannyEdgeImage(_image,geometry_info.rho,geometry_info.sigma,
            geometry_info.xi,geometry_info.psi,_exception);
          break;
        }
      if (LocaleCompare("cdl",option+1) == 0)
        {
          char
            *color_correction_collection; /* Note: arguments do not have percent escapes expanded */

          /*
            Color correct with a color decision list.
          */
          color_correction_collection=FileToString(arg1,~0UL,_exception);
          if (color_correction_collection == (char *) NULL)
            break;
          (void) ColorDecisionListImage(_image,color_correction_collection,
            _exception);
          break;
        }
      if (LocaleCompare("channel",option+1) == 0)
        {
          if (IfPlusOp)
            {
              (void) SetPixelChannelMask(_image,DefaultChannels);
              break;
            }
          parse=ParseChannelOption(arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedChannelType",option,
              arg1);
          (void) SetPixelChannelMask(_image,(ChannelType) parse);
          break;
        }
      if (LocaleCompare("charcoal",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          if ((flags & XiValue) == 0)
            geometry_info.xi=1.0;
          new_image=CharcoalImage(_image,geometry_info.rho,geometry_info.sigma,
            _exception);
          break;
        }
      if (LocaleCompare("chop",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) ParseGravityGeometry(_image,arg1,&geometry,_exception);
          new_image=ChopImage(_image,&geometry,_exception);
          break;
        }
      if (LocaleCompare("clahe",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          flags=ParseGeometry(arg1,&geometry_info);
          flags=ParseRegionGeometry(_image,arg1,&geometry,_exception);
          (void) CLAHEImage(_image,geometry.width,geometry.height,
            (size_t) geometry.x,geometry_info.psi,_exception);
          break;
        }
      if (LocaleCompare("clamp",option+1) == 0)
        {
          (void) ClampImage(_image,_exception);
          break;
        }
      if (LocaleCompare("clip",option+1) == 0)
        {
          if (IfNormalOp)
            (void) ClipImage(_image,_exception);
          else /* "+mask" remove the write mask */
            (void) SetImageMask(_image,WritePixelMask,(const Image *) NULL,
              _exception);
          break;
        }
      if (LocaleCompare("clip-mask",option+1) == 0)
        {
          Image
            *clip_mask;

          if (IfPlusOp) {
            /* use "+clip-mask" Remove the write mask for -clip-path */
            (void) SetImageMask(_image,WritePixelMask,(const Image *) NULL,
             _exception);
            break;
          }
          clip_mask=GetImageCache(_image_info,arg1,_exception);
          if (clip_mask == (Image *) NULL)
            break;
          (void) SetImageMask(_image,WritePixelMask,clip_mask,_exception);
          clip_mask=DestroyImage(clip_mask);
          break;
        }
      if (LocaleCompare("clip-path",option+1) == 0)
        {
          (void) ClipImagePath(_image,arg1,IsNormalOp,_exception);
          /* Note: Use "+clip-mask" remove the write mask added */
          break;
        }
      if (LocaleCompare("colorize",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=ColorizeImage(_image,arg1,&_draw_info->fill,_exception);
          break;
        }
      if (LocaleCompare("color-matrix",option+1) == 0)
        {
          KernelInfo
            *kernel;

          kernel=AcquireKernelInfo(arg1,exception);
          if (kernel == (KernelInfo *) NULL)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=ColorMatrixImage(_image,kernel,_exception);
          kernel=DestroyKernelInfo(kernel);
          break;
        }
      if (LocaleCompare("colors",option+1) == 0)
        {
          /* Reduce the number of colors in the image.
             FUTURE: also provide 'plus version with image 'color counts'
          */
          _quantize_info->number_colors=StringToUnsignedLong(arg1);
          _quantize_info->measure_error=_image_info->verbose;
          if (_quantize_info->number_colors == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((_image->storage_class == DirectClass) ||
              _image->colors > _quantize_info->number_colors)
            (void) QuantizeImage(_quantize_info,_image,_exception);
          else
            (void) CompressImageColormap(_image,_exception);
          break;
        }
      if (LocaleCompare("colorspace",option+1) == 0)
        {
          /* WARNING: this is both a image_info setting (already done)
                      and a operator to change image colorspace.

             FUTURE: default colorspace should be sRGB!
             Unless some type of 'linear colorspace' mode is set.

             Note that +colorspace sets "undefined" or no effect on
             new images, but forces images already in memory back to RGB!
             That seems to be a little strange!
          */
          (void) TransformImageColorspace(_image,
                    IfNormalOp ? _image_info->colorspace : sRGBColorspace,
                    _exception);
          break;
        }
      if (LocaleCompare("color-threshold",option+1) == 0)
        {
          PixelInfo
            start,
            stop;

          /*
            Color threshold image.
          */
          if (*option == '+')
            (void) GetColorRange("white-black",&start,&stop,_exception);
          else
            (void) GetColorRange(arg1,&start,&stop,_exception);
          (void) ColorThresholdImage(_image,&start,&stop,_exception);
          break;
        }
      if (LocaleCompare("connected-components",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=ConnectedComponentsImage(_image,(size_t)
            StringToInteger(arg1),(CCObjectInfo **) NULL,_exception);
          break;
        }
      if (LocaleCompare("contrast",option+1) == 0)
        {
          CLIWandWarnReplaced(IfNormalOp?"-level":"+level");
          (void) ContrastImage(_image,IsNormalOp,_exception);
          break;
        }
      if (LocaleCompare("contrast-stretch",option+1) == 0)
        {
          double
            black_point,
            white_point;

          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          black_point=geometry_info.rho;
          white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma :
            black_point;
          if ((flags & PercentValue) != 0)
            {
              black_point*=(double) _image->columns*_image->rows/100.0;
              white_point*=(double) _image->columns*_image->rows/100.0;
            }
          white_point=(double) _image->columns*_image->rows-white_point;
          (void) ContrastStretchImage(_image,black_point,white_point,
            _exception);
          break;
        }
      if (LocaleCompare("convolve",option+1) == 0)
        {
          double
            gamma;

          KernelInfo
            *kernel_info;

          ssize_t
            j;

          kernel_info=AcquireKernelInfo(arg1,exception);
          if (kernel_info == (KernelInfo *) NULL)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          gamma=0.0;
          for (j=0; j < (ssize_t) (kernel_info->width*kernel_info->height); j++)
            gamma+=kernel_info->values[j];
          gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
          for (j=0; j < (ssize_t) (kernel_info->width*kernel_info->height); j++)
            kernel_info->values[j]*=gamma;
          new_image=MorphologyImage(_image,CorrelateMorphology,1,kernel_info,
            _exception);
          kernel_info=DestroyKernelInfo(kernel_info);
          break;
        }
      if (LocaleCompare("crop",option+1) == 0)
        {
          /* WARNING: This can generate multiple images! */
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=CropImageToTiles(_image,arg1,_exception);
          break;
        }
      if (LocaleCompare("cycle",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) CycleColormapImage(_image,(ssize_t) StringToLong(arg1),
            _exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'd':
    {
      if (LocaleCompare("decipher",option+1) == 0)
        {
          /* Note: arguments do not have percent escapes expanded */
          StringInfo
            *passkey;

          passkey=FileToStringInfo(arg1,~0UL,_exception);
          if (passkey == (StringInfo *) NULL)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);

          (void) PasskeyDecipherImage(_image,passkey,_exception);
          passkey=DestroyStringInfo(passkey);
          break;
        }
      if (LocaleCompare("depth",option+1) == 0)
        {
          /* The _image_info->depth setting has already been set
             We just need to apply it to all images in current sequence

             WARNING: Depth from 8 to 16 causes 'quantum rounding to images!
             That is it really is an operation, not a setting! Arrgghhh

             FUTURE: this should not be an operator!!!
          */
          (void) SetImageDepth(_image,_image_info->depth,_exception);
          break;
        }
      if (LocaleCompare("deskew",option+1) == 0)
        {
          double
            threshold;

          if (IfNormalOp) {
            if (IsGeometry(arg1) == MagickFalse)
              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
            threshold=StringToDoubleInterval(arg1,(double) QuantumRange+1.0);
          }
          else
            threshold=40.0*QuantumRange/100.0;
          new_image=DeskewImage(_image,threshold,_exception);
          break;
        }
      if (LocaleCompare("despeckle",option+1) == 0)
        {
          new_image=DespeckleImage(_image,_exception);
          break;
        }
      if (LocaleCompare("distort",option+1) == 0)
        {
          double
            *args;

          ssize_t
            count;

          parse = ParseCommandOption(MagickDistortOptions,MagickFalse,arg1);
          if ( parse < 0 )
             CLIWandExceptArgBreak(OptionError,"UnrecognizedDistortMethod",
                                      option,arg1);
          if ((DistortMethod) parse == ResizeDistortion)
            {
               double
                 resize_args[2];
               /* Special Case - Argument is actually a resize geometry!
               ** Convert that to an appropriate distortion argument array.
               ** FUTURE: make a separate special resize operator
                    Roll into a resize special operator */
               if (IsGeometry(arg2) == MagickFalse)
                 CLIWandExceptArgBreak(OptionError,"InvalidGeometry",
                                           option,arg2);
               (void) ParseRegionGeometry(_image,arg2,&geometry,_exception);
               resize_args[0]=(double) geometry.width;
               resize_args[1]=(double) geometry.height;
               new_image=DistortImage(_image,(DistortMethod) parse,
                    (size_t)2,resize_args,MagickTrue,_exception);
               break;
            }
          /* convert argument string into an array of doubles */
          args = StringToArrayOfDoubles(arg2,&count,_exception);
          if (args == (double *) NULL )
            CLIWandExceptArgBreak(OptionError,"InvalidNumberList",option,arg2);

          new_image=DistortImage(_image,(DistortMethod) parse,(size_t)
             count,args,IsPlusOp,_exception);
          args=(double *) RelinquishMagickMemory(args);
          break;
        }
      if (LocaleCompare("draw",option+1) == 0)
        {
          (void) CloneString(&_draw_info->primitive,arg1);
          (void) DrawImage(_image,_draw_info,_exception);
          (void) CloneString(&_draw_info->primitive,(char *) NULL);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'e':
    {
      if (LocaleCompare("edge",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=EdgeImage(_image,geometry_info.rho,_exception);
          break;
        }
      if (LocaleCompare("emboss",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          new_image=EmbossImage(_image,geometry_info.rho,
            geometry_info.sigma,_exception);
          break;
        }
      if (LocaleCompare("encipher",option+1) == 0)
        {
          /* Note: arguments do not have percent escapes expanded */
          StringInfo
            *passkey;

          passkey=FileToStringInfo(arg1,~0UL,_exception);
          if (passkey != (StringInfo *) NULL)
            {
              (void) PasskeyEncipherImage(_image,passkey,_exception);
              passkey=DestroyStringInfo(passkey);
            }
          break;
        }
      if (LocaleCompare("enhance",option+1) == 0)
        {
          new_image=EnhanceImage(_image,_exception);
          break;
        }
      if (LocaleCompare("equalize",option+1) == 0)
        {
          (void) EqualizeImage(_image,_exception);
          break;
        }
      if (LocaleCompare("evaluate",option+1) == 0)
        {
          double
            constant;

          parse = ParseCommandOption(MagickEvaluateOptions,MagickFalse,arg1);
          if ( parse < 0 )
            CLIWandExceptArgBreak(OptionError,"UnrecognizedEvaluateOperator",
                 option,arg1);
          if (IsGeometry(arg2) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg2);
          constant=StringToDoubleInterval(arg2,(double) QuantumRange+1.0);
          (void) EvaluateImage(_image,(MagickEvaluateOperator)parse,constant,
               _exception);
          break;
        }
      if (LocaleCompare("extent",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          flags=ParseGravityGeometry(_image,arg1,&geometry,_exception);
          if (geometry.width == 0)
            geometry.width=_image->columns;
          if (geometry.height == 0)
            geometry.height=_image->rows;
          new_image=ExtentImage(_image,&geometry,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'f':
    {
      if (LocaleCompare("features",option+1) == 0)
        {
          CLIWandWarnReplaced("-version -define identify:features=");
          if (*option == '+')
            {
              (void) DeleteImageArtifact(_image,"identify:features");
              break;
            }
          (void) SetImageArtifact(_image,"identify:features",arg1);
          (void) SetImageArtifact(_image,"verbose","true");
          break;
        }
      if (LocaleCompare("flip",option+1) == 0)
        {
          new_image=FlipImage(_image,_exception);
          break;
        }
      if (LocaleCompare("flop",option+1) == 0)
        {
          new_image=FlopImage(_image,_exception);
          break;
        }
      if (LocaleCompare("floodfill",option+1) == 0)
        {
          PixelInfo
            target;

          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) ParsePageGeometry(_image,arg1,&geometry,_exception);
          (void) QueryColorCompliance(arg2,AllCompliance,&target,_exception);
          (void) FloodfillPaintImage(_image,_draw_info,&target,geometry.x,
            geometry.y,IsPlusOp,_exception);
          break;
        }
      if (LocaleCompare("frame",option+1) == 0)
        {
          FrameInfo
            frame_info;

          CompositeOperator
            compose;

          const char*
            value;

          value=GetImageOption(_image_info,"compose");
            compose=OverCompositeOp;  /* use Over not _image->compose */
          if (value != (const char *) NULL)
            compose=(CompositeOperator) ParseCommandOption(MagickComposeOptions,
              MagickFalse,value);
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          flags=ParsePageGeometry(_image,arg1,&geometry,_exception);
          frame_info.width=geometry.width;
          frame_info.height=geometry.height;
          frame_info.outer_bevel=geometry.x;
          frame_info.inner_bevel=geometry.y;
          frame_info.x=(ssize_t) frame_info.width;
          frame_info.y=(ssize_t) frame_info.height;
          frame_info.width=_image->columns+2*frame_info.width;
          frame_info.height=_image->rows+2*frame_info.height;
          new_image=FrameImage(_image,&frame_info,compose,_exception);
          break;
        }
      if (LocaleCompare("function",option+1) == 0)
        {
          double
            *args;

          ssize_t
            count;

          parse=ParseCommandOption(MagickFunctionOptions,MagickFalse,arg1);
          if ( parse < 0 )
            CLIWandExceptArgBreak(OptionError,"UnrecognizedFunction",
                 option,arg1);
          /* convert argument string into an array of doubles */
          args = StringToArrayOfDoubles(arg2,&count,_exception);
          if (args == (double *) NULL )
            CLIWandExceptArgBreak(OptionError,"InvalidNumberList",option,arg2);

          (void) FunctionImage(_image,(MagickFunction)parse,(size_t) count,args,
               _exception);
          args=(double *) RelinquishMagickMemory(args);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'g':
    {
      if (LocaleCompare("gamma",option+1) == 0)
        {
          double
            constant;

          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          constant=StringToDouble(arg1,(char **) NULL);
#if 0
          /* Using Gamma, via a cache */
          if (IfPlusOp)
            constant=PerceptibleReciprocal(constant);
          (void) GammaImage(_image,constant,_exception);
#else
          /* Using Evaluate POW, direct update of values - more accurite */
          if (IfNormalOp)
            constant=PerceptibleReciprocal(constant);
          (void) EvaluateImage(_image,PowEvaluateOperator,constant,_exception);
          _image->gamma*=StringToDouble(arg1,(char **) NULL);
#endif
          /* Set gamma setting -- Old meaning of "+gamma"
           * _image->gamma=StringToDouble(arg1,(char **) NULL);
           */
          break;
        }
      if (LocaleCompare("gaussian-blur",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          new_image=GaussianBlurImage(_image,geometry_info.rho,
            geometry_info.sigma,_exception);
          break;
        }
      if (LocaleCompare("gaussian",option+1) == 0)
        {
          CLIWandWarnReplaced("-gaussian-blur");
          (void) CLISimpleOperatorImage(cli_wand,"-gaussian-blur",arg1,NULL,exception);
        }
      if (LocaleCompare("geometry",option+1) == 0)
        {
          /*
            Record Image offset for composition. (A Setting)
            Resize last _image. (ListOperator)  -- DEPRECIATE
            FUTURE: Why if no 'offset' does this resize ALL images?
            Also why is the setting recorded in the IMAGE non-sense!
          */
          if (IfPlusOp)
            { /* remove the previous composition geometry offset! */
              if (_image->geometry != (char *) NULL)
                _image->geometry=DestroyString(_image->geometry);
              break;
            }
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          flags=ParseRegionGeometry(_image,arg1,&geometry,_exception);
          if (((flags & XValue) != 0) || ((flags & YValue) != 0))
            (void) CloneString(&_image->geometry,arg1);
          else
            new_image=ResizeImage(_image,geometry.width,geometry.height,
              _image->filter,_exception);
          break;
        }
      if (LocaleCompare("grayscale",option+1) == 0)
        {
          parse=ParseCommandOption(MagickPixelIntensityOptions,
            MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedIntensityMethod",
              option,arg1);
          (void) GrayscaleImage(_image,(PixelIntensityMethod) parse,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'h':
    {
      if (LocaleCompare("hough-lines",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=geometry_info.rho;
          if ((flags & XiValue) == 0)
            geometry_info.xi=40;
          new_image=HoughLineImage(_image,(size_t) geometry_info.rho,
            (size_t) geometry_info.sigma,(size_t) geometry_info.xi,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'i':
    {
      if (LocaleCompare("identify",option+1) == 0)
        {
          const char
            *format,
            *text;

          format=GetImageOption(_image_info,"format");
          if (format == (char *) NULL)
            {
              (void) IdentifyImage(_image,stdout,_image_info->verbose,
                _exception);
              break;
            }
          text=InterpretImageProperties(_image_info,_image,format,_exception);
          if (text == (char *) NULL)
            CLIWandExceptionBreak(OptionWarning,"InterpretPropertyFailure",
              option);
          (void) fputs(text,stdout);
          text=DestroyString((char *)text);
          break;
        }
      if (LocaleCompare("implode",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=ImplodeImage(_image,geometry_info.rho,_image->interpolate,
               _exception);
          break;
        }
      if (LocaleCompare("integral",option+1) == 0)
        {
          new_image=IntegralImage(_image,_exception);
          break;
        }
      if (LocaleCompare("interpolative-resize",option+1) == 0)
        {
          /* FUTURE: New to IMv7
               Roll into a resize special operator */
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
          new_image=InterpolativeResizeImage(_image,geometry.width,
            geometry.height,_image->interpolate,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'k':
    {
      if (LocaleCompare("kmeans",option+1) == 0)
        {
          /*
            K-means clustering.
          */
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=100.0;
          if ((flags & XiValue) == 0)
            geometry_info.xi=0.01;
          (void) KmeansImage(_image,(size_t) geometry_info.rho,(size_t)
           geometry_info.sigma,geometry_info.xi,_exception);
          break;
        }
      if (LocaleCompare("kuwahara",option+1) == 0)
        {
          /*
            Edge preserving blur.
          */
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=geometry_info.rho-0.5;
          new_image=KuwaharaImage(_image,geometry_info.rho,geometry_info.sigma,
           _exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'l':
    {
      if (LocaleCompare("lat",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          if ((flags & PercentValue) != 0)
            geometry_info.xi=(double) QuantumRange*geometry_info.xi/100.0;
          new_image=AdaptiveThresholdImage(_image,(size_t) geometry_info.rho,
               (size_t) geometry_info.sigma,(double) geometry_info.xi,
               _exception);
          break;
        }
      if (LocaleCompare("level",option+1) == 0)
        {
          double
            black_point,
            gamma,
            white_point;

          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          black_point=geometry_info.rho;
          white_point=(double) QuantumRange;
          if ((flags & SigmaValue) != 0)
            white_point=geometry_info.sigma;
          gamma=1.0;
          if ((flags & XiValue) != 0)
            gamma=geometry_info.xi;
          if ((flags & PercentValue) != 0)
            {
              black_point*=(double) (QuantumRange/100.0);
              white_point*=(double) (QuantumRange/100.0);
            }
          if ((flags & SigmaValue) == 0)
            white_point=(double) QuantumRange-black_point;
          if (IfPlusOp || ((flags & AspectValue) != 0))
            (void) LevelizeImage(_image,black_point,white_point,gamma,_exception);
          else
            (void) LevelImage(_image,black_point,white_point,gamma,_exception);
          break;
        }
      if (LocaleCompare("level-colors",option+1) == 0)
        {
          char
            token[MagickPathExtent];

          const char
            *p;

          PixelInfo
            black_point,
            white_point;

          p=(const char *) arg1;
          (void) GetNextToken(p,&p,MagickPathExtent,token);  /* get black point color */
          if ((isalpha((int) ((unsigned char) *token)) != 0) || ((*token == '#') != 0))
            (void) QueryColorCompliance(token,AllCompliance,
                      &black_point,_exception);
          else
            (void) QueryColorCompliance("#000000",AllCompliance,
                      &black_point,_exception);
          if (isalpha((int) ((unsigned char) *token)) || (*token == '#'))
            (void) GetNextToken(p,&p,MagickPathExtent,token);
          if (*token == '\0')
            white_point=black_point; /* set everything to that color */
          else
            {
              if ((isalpha((int) ((unsigned char) *token)) == 0) && ((*token == '#') == 0))
                (void) GetNextToken(p,&p,MagickPathExtent,token); /* Get white point color. */
              if ((isalpha((int) ((unsigned char) *token)) != 0) || ((*token == '#') != 0))
                (void) QueryColorCompliance(token,AllCompliance,
                           &white_point,_exception);
              else
                (void) QueryColorCompliance("#ffffff",AllCompliance,
                           &white_point,_exception);
            }
          (void) LevelImageColors(_image,&black_point,&white_point,
                     IsPlusOp,_exception);
          break;
        }
      if (LocaleCompare("linear-stretch",option+1) == 0)
        {
          double
            black_point,
            white_point;

          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          black_point=geometry_info.rho;
          white_point=(double) _image->columns*_image->rows;
          if ((flags & SigmaValue) != 0)
            white_point=geometry_info.sigma;
          if ((flags & PercentValue) != 0)
            {
              black_point*=(double) _image->columns*_image->rows/100.0;
              white_point*=(double) _image->columns*_image->rows/100.0;
            }
          if ((flags & SigmaValue) == 0)
            white_point=(double) _image->columns*_image->rows-
              black_point;
          (void) LinearStretchImage(_image,black_point,white_point,_exception);
          break;
        }
      if (LocaleCompare("liquid-rescale",option+1) == 0)
        {
          /* FUTURE: Roll into a resize special operator */
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          flags=ParseRegionGeometry(_image,arg1,&geometry,_exception);
          if ((flags & XValue) == 0)
            geometry.x=1;
          if ((flags & YValue) == 0)
            geometry.y=0;
          new_image=LiquidRescaleImage(_image,geometry.width,
            geometry.height,1.0*geometry.x,1.0*geometry.y,_exception);
          break;
        }
      if (LocaleCompare("local-contrast",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            geometry_info.rho=10;
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=12.5;
          new_image=LocalContrastImage(_image,geometry_info.rho,
            geometry_info.sigma,exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'm':
    {
      if (LocaleCompare("magnify",option+1) == 0)
        {
          new_image=MagnifyImage(_image,_exception);
          break;
        }
      if (LocaleCompare("map",option+1) == 0)
        {
          CLIWandWarnReplaced("-remap");
          (void) CLISimpleOperatorImage(cli_wand,"-remap",NULL,NULL,exception);
          break;
        }
      if (LocaleCompare("mask",option+1) == 0)
        {
          Image
            *mask;

          if (IfPlusOp)
            {
              /*
                Remove a mask.
              */
              (void) SetImageMask(_image,WritePixelMask,(const Image *) NULL,
                _exception);
              break;
            }
          /*
            Set the image mask.
          */
          mask=GetImageCache(_image_info,arg1,_exception);
          if (mask == (Image *) NULL)
            break;
          (void) SetImageMask(_image,WritePixelMask,mask,_exception);
          mask=DestroyImage(mask);
          break;
        }
      if (LocaleCompare("matte",option+1) == 0)
        {
          CLIWandWarnReplaced(IfNormalOp?"-alpha Set":"-alpha Off");
          (void) SetImageAlphaChannel(_image,IfNormalOp ? SetAlphaChannel :
            DeactivateAlphaChannel, _exception);
          break;
        }
      if (LocaleCompare("mean-shift",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          if ((flags & XiValue) == 0)
            geometry_info.xi=0.10*QuantumRange;
          if ((flags & PercentValue) != 0)
            geometry_info.xi=(double) QuantumRange*geometry_info.xi/100.0;
          new_image=MeanShiftImage(_image,(size_t) geometry_info.rho,
            (size_t) geometry_info.sigma,geometry_info.xi,_exception);
          break;
        }
      if (LocaleCompare("median",option+1) == 0)
        {
          CLIWandWarnReplaced("-statistic Median");
          (void) CLISimpleOperatorImage(cli_wand,"-statistic","Median",arg1,exception);
          break;
        }
      if (LocaleCompare("mode",option+1) == 0)
        {
          /* FUTURE: note this is also a special "montage" option */
          CLIWandWarnReplaced("-statistic Mode");
          (void) CLISimpleOperatorImage(cli_wand,"-statistic","Mode",arg1,exception);
          break;
        }
      if (LocaleCompare("modulate",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) ModulateImage(_image,arg1,_exception);
          break;
        }
      if (LocaleCompare("monitor",option+1) == 0)
        {
          (void) SetImageProgressMonitor(_image, IfNormalOp ? MonitorProgress :
                (MagickProgressMonitor) NULL,(void *) NULL);
          break;
        }
      if (LocaleCompare("monochrome",option+1) == 0)
        {
          (void) SetImageType(_image,BilevelType,_exception);
          break;
        }
      if (LocaleCompare("morphology",option+1) == 0)
        {
          char
            token[MagickPathExtent];

          const char
            *p;

          KernelInfo
            *kernel;

          ssize_t
            iterations;

          p=arg1;
          (void) GetNextToken(p,&p,MagickPathExtent,token);
          parse=ParseCommandOption(MagickMorphologyOptions,MagickFalse,token);
          if ( parse < 0 )
            CLIWandExceptArgBreak(OptionError,"UnrecognizedFunction",option,
              arg1);
          iterations=1L;
          (void) GetNextToken(p,&p,MagickPathExtent,token);
          if ((*p == ':') || (*p == ','))
            (void) GetNextToken(p,&p,MagickPathExtent,token);
          if ((*p != '\0'))
            iterations=(ssize_t) StringToLong(p);
          kernel=AcquireKernelInfo(arg2,exception);
          if (kernel == (KernelInfo *) NULL)
            CLIWandExceptArgBreak(OptionError,"UnabletoParseKernel",option,arg2);
          new_image=MorphologyImage(_image,(MorphologyMethod)parse,iterations,
            kernel,_exception);
          kernel=DestroyKernelInfo(kernel);
          break;
        }
      if (LocaleCompare("motion-blur",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          new_image=MotionBlurImage(_image,geometry_info.rho,
            geometry_info.sigma,geometry_info.xi,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'n':
    {
      if (LocaleCompare("negate",option+1) == 0)
        {
          (void) NegateImage(_image, IsPlusOp, _exception);
          break;
        }
      if (LocaleCompare("noise",option+1) == 0)
        {
          double
            attenuate;

          const char*
            value;

          if (IfNormalOp)
            {
              CLIWandWarnReplaced("-statistic NonPeak");
              (void) CLISimpleOperatorImage(cli_wand,"-statistic","NonPeak",arg1,exception);
              break;
            }
          parse=ParseCommandOption(MagickNoiseOptions,MagickFalse,arg1);
          if ( parse < 0 )
            CLIWandExceptArgBreak(OptionError,"UnrecognizedNoiseType",
                option,arg1);
          attenuate=1.0;
          value=GetImageOption(_image_info,"attenuate");
          if  (value != (const char *) NULL)
            attenuate=StringToDouble(value,(char **) NULL);
          new_image=AddNoiseImage(_image,(NoiseType)parse,attenuate,
               _exception);
          break;
        }
      if (LocaleCompare("normalize",option+1) == 0)
        {
          (void) NormalizeImage(_image,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'o':
    {
      if (LocaleCompare("opaque",option+1) == 0)
        {
          PixelInfo
            target;

          (void) QueryColorCompliance(arg1,AllCompliance,&target,_exception);
          (void) OpaquePaintImage(_image,&target,&_draw_info->fill,IsPlusOp,
               _exception);
          break;
        }
      if (LocaleCompare("ordered-dither",option+1) == 0)
        {
          (void) OrderedDitherImage(_image,arg1,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'p':
    {
      if (LocaleCompare("paint",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=OilPaintImage(_image,geometry_info.rho,geometry_info.sigma,
               _exception);
          break;
        }
      if (LocaleCompare("perceptible",option+1) == 0)
        {
          (void) PerceptibleImage(_image,StringToDouble(arg1,(char **) NULL),
            _exception);
          break;
        }
      if (LocaleCompare("polaroid",option+1) == 0)
        {
          const char
            *caption;

          double
            angle;

          if (IfPlusOp) {
            RandomInfo
              *random_info;

            random_info=AcquireRandomInfo();
            angle=22.5*(GetPseudoRandomValue(random_info)-0.5);
            random_info=DestroyRandomInfo(random_info);
          }
          else {
            flags=ParseGeometry(arg1,&geometry_info);
            if ((flags & RhoValue) == 0)
              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
            angle=geometry_info.rho;
          }
          caption=GetImageProperty(_image,"caption",_exception);
          new_image=PolaroidImage(_image,_draw_info,caption,angle,
            _image->interpolate,_exception);
          break;
        }
      if (LocaleCompare("posterize",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) PosterizeImage(_image,(size_t) geometry_info.rho,
            _quantize_info->dither_method,_exception);
          break;
        }
      if (LocaleCompare("preview",option+1) == 0)
        {
          /* FUTURE: should be a 'Genesis' option?
             Option however is also in WandSettingOptionInfo()
             Why???
          */
          parse=ParseCommandOption(MagickPreviewOptions, MagickFalse,arg1);
          if ( parse < 0 )
            CLIWandExceptArgBreak(OptionError,"UnrecognizedPreviewType",
                option,arg1);
          new_image=PreviewImage(_image,(PreviewType)parse,_exception);
          break;
        }
      if (LocaleCompare("profile",option+1) == 0)
        {
          const char
            *name;

          const StringInfo
            *profile;

          Image
            *profile_image;

          ImageInfo
            *profile_info;

          /* Note: arguments do not have percent escapes expanded */
          if (IfPlusOp)
            { /* Remove a profile from the _image.  */
              (void) ProfileImage(_image,arg1,(const unsigned char *)
                NULL,0,_exception);
              break;
            }
          /* Associate a profile with the _image.  */
          profile_info=CloneImageInfo(_image_info);
          profile=GetImageProfile(_image,"iptc");
          if (profile != (const StringInfo *) NULL)
            profile_info->profile=(void *) CloneStringInfo(profile);
          profile_image=GetImageCache(profile_info,arg1,_exception);
          profile_info=DestroyImageInfo(profile_info);
          if (profile_image == (Image *) NULL)
            {
              StringInfo
                *new_profile;

              profile_info=CloneImageInfo(_image_info);
              (void) CopyMagickString(profile_info->filename,arg1,
                MagickPathExtent);
              new_profile=FileToStringInfo(profile_info->filename,~0UL,
                _exception);
              if (new_profile != (StringInfo *) NULL)
                {
                  (void) SetImageInfo(profile_info,0,_exception);
                  (void) ProfileImage(_image,profile_info->magick,
                    GetStringInfoDatum(new_profile),(size_t)
                    GetStringInfoLength(new_profile),_exception);
                  new_profile=DestroyStringInfo(new_profile);
                }
              profile_info=DestroyImageInfo(profile_info);
              break;
            }
          ResetImageProfileIterator(profile_image);
          name=GetNextImageProfile(profile_image);
          while (name != (const char *) NULL)
          {
            profile=GetImageProfile(profile_image,name);
            if (profile != (const StringInfo *) NULL)
              (void) ProfileImage(_image,name,GetStringInfoDatum(profile),
                (size_t) GetStringInfoLength(profile),_exception);
            name=GetNextImageProfile(profile_image);
          }
          profile_image=DestroyImage(profile_image);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'r':
    {
      if (LocaleCompare("raise",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          flags=ParsePageGeometry(_image,arg1,&geometry,_exception);
          (void) RaiseImage(_image,&geometry,IsNormalOp,_exception);
          break;
        }
      if (LocaleCompare("random-threshold",option+1) == 0)
        {
          double
            min_threshold,
            max_threshold;

          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          min_threshold=0.0;
          max_threshold=(double) QuantumRange;
          flags=ParseGeometry(arg1,&geometry_info);
          min_threshold=geometry_info.rho;
          max_threshold=geometry_info.sigma;
          if ((flags & SigmaValue) == 0)
            max_threshold=min_threshold;
          if (strchr(arg1,'%') != (char *) NULL)
            {
              max_threshold*=(double) (0.01*QuantumRange);
              min_threshold*=(double) (0.01*QuantumRange);
            }
          (void) RandomThresholdImage(_image,min_threshold,max_threshold,
            _exception);
          break;
        }
      if (LocaleCompare("range-threshold",option+1) == 0)
        {
          /*
            Range threshold image.
          */
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=geometry_info.rho;
          if ((flags & XiValue) == 0)
            geometry_info.xi=geometry_info.sigma;
          if ((flags & PsiValue) == 0)
            geometry_info.psi=geometry_info.xi;
          if (strchr(arg1,'%') != (char *) NULL)
            {
              geometry_info.rho*=(double) (0.01*QuantumRange);
              geometry_info.sigma*=(double) (0.01*QuantumRange);
              geometry_info.xi*=(double) (0.01*QuantumRange);
              geometry_info.psi*=(double) (0.01*QuantumRange);
            }
          (void) RangeThresholdImage(_image,geometry_info.rho,
            geometry_info.sigma,geometry_info.xi,geometry_info.psi,exception);
          break;
        }
      if (LocaleCompare("read-mask",option+1) == 0)
        {
          /* Note: arguments do not have percent escapes expanded */
          Image
            *mask;

          if (IfPlusOp)
            { /* Remove a mask. */
              (void) SetImageMask(_image,ReadPixelMask,(const Image *) NULL,
                _exception);
              break;
            }
          /* Set the image mask. */
          mask=GetImageCache(_image_info,arg1,_exception);
          if (mask == (Image *) NULL)
            break;
          (void) SetImageMask(_image,ReadPixelMask,mask,_exception);
          mask=DestroyImage(mask);
          break;
        }
      if (LocaleCompare("recolor",option+1) == 0)
        {
          CLIWandWarnReplaced("-color-matrix");
          (void) CLISimpleOperatorImage(cli_wand,"-color-matrix",arg1,NULL,
            exception);
        }
      if (LocaleCompare("region",option+1) == 0)
        {
          if (*option == '+')
            {
              (void) SetImageRegionMask(_image,WritePixelMask,
                (const RectangleInfo *) NULL,_exception);
              break;
            }
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) ParseGravityGeometry(_image,arg1,&geometry,_exception);
          (void) SetImageRegionMask(_image,WritePixelMask,&geometry,_exception);
          break;
        }
      if (LocaleCompare("remap",option+1) == 0)
        {
          /* Note: arguments do not have percent escapes expanded */
          Image
            *remap_image;

          remap_image=GetImageCache(_image_info,arg1,_exception);
          if (remap_image == (Image *) NULL)
            break;
          (void) RemapImage(_quantize_info,_image,remap_image,_exception);
          remap_image=DestroyImage(remap_image);
          break;
        }
      if (LocaleCompare("repage",option+1) == 0)
        {
          if (IfNormalOp)
            {
              if (IsGeometry(arg1) == MagickFalse)
                CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,
                  arg1);
              (void) ResetImagePage(_image,arg1);
            }
          else
            (void) ParseAbsoluteGeometry("0x0+0+0",&_image->page);
          break;
        }
      if (LocaleCompare("resample",option+1) == 0)
        {
          /* FUTURE: Roll into a resize special operation */
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=geometry_info.rho;
          new_image=ResampleImage(_image,geometry_info.rho,
            geometry_info.sigma,_image->filter,_exception);
          break;
        }
      if (LocaleCompare("resize",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
          new_image=ResizeImage(_image,geometry.width,geometry.height,
            _image->filter,_exception);
          break;
        }
      if (LocaleCompare("roll",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          flags=ParsePageGeometry(_image,arg1,&geometry,_exception);
          if ((flags & PercentValue) != 0)
            {
              geometry.x*=(double) _image->columns/100.0;
              geometry.y*=(double) _image->rows/100.0;
            }
          new_image=RollImage(_image,geometry.x,geometry.y,_exception);
          break;
        }
      if (LocaleCompare("rotate",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & GreaterValue) != 0 && (_image->columns <= _image->rows))
            break;
          if ((flags & LessValue) != 0 && (_image->columns >= _image->rows))
            break;
          new_image=RotateImage(_image,geometry_info.rho,_exception);
          break;
        }
      if (LocaleCompare("rotational-blur",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=RotationalBlurImage(_image,geometry_info.rho,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 's':
    {
      if (LocaleCompare("sample",option+1) == 0)
        {
          /* FUTURE: Roll into a resize special operator */
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
          new_image=SampleImage(_image,geometry.width,geometry.height,
            _exception);
          break;
        }
      if (LocaleCompare("scale",option+1) == 0)
        {
          /* FUTURE: Roll into a resize special operator */
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
          new_image=ScaleImage(_image,geometry.width,geometry.height,
            _exception);
          break;
        }
      if (LocaleCompare("segment",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          (void) SegmentImage(_image,_image->colorspace,
            _image_info->verbose,geometry_info.rho,geometry_info.sigma,
            _exception);
          break;
        }
      if (LocaleCompare("selective-blur",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          if ((flags & PercentValue) != 0)
            geometry_info.xi=(double) QuantumRange*geometry_info.xi/100.0;
          new_image=SelectiveBlurImage(_image,geometry_info.rho,
            geometry_info.sigma,geometry_info.xi,_exception);
          break;
        }
      if (LocaleCompare("separate",option+1) == 0)
        {
          /* WARNING: This can generate multiple images! */
          /* FUTURE - this may be replaced by a "-channel" method */
          new_image=SeparateImages(_image,_exception);
          break;
        }
      if (LocaleCompare("sepia-tone",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=SepiaToneImage(_image,StringToDoubleInterval(arg1,
                 (double) QuantumRange+1.0),_exception);
          break;
        }
      if (LocaleCompare("shade",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if (((flags & RhoValue) == 0) || ((flags & SigmaValue) == 0))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=ShadeImage(_image,IsNormalOp,geometry_info.rho,
               geometry_info.sigma,_exception);
          break;
        }
      if (LocaleCompare("shadow",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          if ((flags & XiValue) == 0)
            geometry_info.xi=4.0;
          if ((flags & PsiValue) == 0)
            geometry_info.psi=4.0;
          new_image=ShadowImage(_image,geometry_info.rho,geometry_info.sigma,
            (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t)
            ceil(geometry_info.psi-0.5),_exception);
          break;
        }
      if (LocaleCompare("sharpen",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          if ((flags & XiValue) == 0)
            geometry_info.xi=0.0;
          new_image=SharpenImage(_image,geometry_info.rho,geometry_info.sigma,
           _exception);
          break;
        }
      if (LocaleCompare("shave",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          flags=ParsePageGeometry(_image,arg1,&geometry,_exception);
          new_image=ShaveImage(_image,&geometry,_exception);
          break;
        }
      if (LocaleCompare("shear",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=geometry_info.rho;
          new_image=ShearImage(_image,geometry_info.rho,geometry_info.sigma,
            _exception);
          break;
        }
      if (LocaleCompare("sigmoidal-contrast",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=(double) QuantumRange/2.0;
          if ((flags & PercentValue) != 0)
            geometry_info.sigma=(double) QuantumRange*geometry_info.sigma/
              100.0;
          (void) SigmoidalContrastImage(_image,IsNormalOp,geometry_info.rho,
               geometry_info.sigma,_exception);
          break;
        }
      if (LocaleCompare("sketch",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          new_image=SketchImage(_image,geometry_info.rho,
            geometry_info.sigma,geometry_info.xi,_exception);
          break;
        }
      if (LocaleCompare("solarize",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) SolarizeImage(_image,StringToDoubleInterval(arg1,(double)
             QuantumRange+1.0),_exception);
          break;
        }
      if (LocaleCompare("sort-pixels",option+1) == 0)
        {
          (void) SortImagePixels(_image,_exception);
          break;
        }
      if (LocaleCompare("sparse-color",option+1) == 0)
        {
          parse=ParseCommandOption(MagickSparseColorOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedSparseColorMethod",
              option,arg1);
          new_image=SparseColorOption(_image,(SparseColorMethod)parse,arg2,
            _exception);
          break;
        }
      if (LocaleCompare("splice",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          flags=ParseGravityGeometry(_image,arg1,&geometry,_exception);
          new_image=SpliceImage(_image,&geometry,_exception);
          break;
        }
      if (LocaleCompare("spread",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg2);
          new_image=SpreadImage(_image,_image->interpolate,geometry_info.rho,
           _exception);
          break;
        }
      if (LocaleCompare("statistic",option+1) == 0)
        {
          parse=ParseCommandOption(MagickStatisticOptions,MagickFalse,arg1);
          if ( parse < 0 )
            CLIWandExceptArgBreak(OptionError,"UnrecognizedStatisticType",
                 option,arg1);
          flags=ParseGeometry(arg2,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg2);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=geometry_info.rho;
          new_image=StatisticImage(_image,(StatisticType)parse,
               (size_t) geometry_info.rho,(size_t) geometry_info.sigma,
               _exception);
          break;
        }
      if (LocaleCompare("strip",option+1) == 0)
        {
          (void) StripImage(_image,_exception);
          break;
        }
      if (LocaleCompare("swirl",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=SwirlImage(_image,geometry_info.rho,
            _image->interpolate,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 't':
    {
      if (LocaleCompare("threshold",option+1) == 0)
        {
          double
            threshold;

          threshold=(double) QuantumRange/2;
          if (IfNormalOp) {
            if (IsGeometry(arg1) == MagickFalse)
              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
            threshold=StringToDoubleInterval(arg1,(double) QuantumRange+1.0);
          }
          (void) BilevelImage(_image,threshold,_exception);
          break;
        }
      if (LocaleCompare("thumbnail",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
          new_image=ThumbnailImage(_image,geometry.width,geometry.height,
            _exception);
          break;
        }
      if (LocaleCompare("tint",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          new_image=TintImage(_image,arg1,&_draw_info->fill,_exception);
          break;
        }
      if (LocaleCompare("transform",option+1) == 0)
        {
          CLIWandWarnReplaced("+distort AffineProjection");
          new_image=AffineTransformImage(_image,&_draw_info->affine,_exception);
          break;
        }
      if (LocaleCompare("transparent",option+1) == 0)
        {
          PixelInfo
            target;

          (void) QueryColorCompliance(arg1,AllCompliance,&target,_exception);
          (void) TransparentPaintImage(_image,&target,(Quantum)
            TransparentAlpha,IsPlusOp,_exception);
          break;
        }
      if (LocaleCompare("transpose",option+1) == 0)
        {
          new_image=TransposeImage(_image,_exception);
          break;
        }
      if (LocaleCompare("transverse",option+1) == 0)
        {
          new_image=TransverseImage(_image,_exception);
          break;
        }
      if (LocaleCompare("trim",option+1) == 0)
        {
          new_image=TrimImage(_image,_exception);
          break;
        }
      if (LocaleCompare("type",option+1) == 0)
        {
          /* Note that "type" setting should have already been defined */
          (void) SetImageType(_image,_image_info->type,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'u':
    {
      if (LocaleCompare("unique",option+1) == 0)
        {
          /* FUTURE: move to SyncImageSettings() and AcqireImage()???
             Option is not documented, bt appears to be for "identify".
             We may need a identify specific verbose!
          */
          if (IsPlusOp) {
              (void) DeleteImageArtifact(_image,"identify:unique-colors");
              break;
            }
          (void) SetImageArtifact(_image,"identify:unique-colors","true");
          (void) SetImageArtifact(_image,"verbose","true");
          break;
        }
      if (LocaleCompare("unique-colors",option+1) == 0)
        {
          new_image=UniqueImageColors(_image,_exception);
          break;
        }
      if (LocaleCompare("unsharp",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          if ((flags & XiValue) == 0)
            geometry_info.xi=1.0;
          if ((flags & PsiValue) == 0)
            geometry_info.psi=0.05;
          new_image=UnsharpMaskImage(_image,geometry_info.rho,
            geometry_info.sigma,geometry_info.xi,geometry_info.psi,_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'v':
    {
      if (LocaleCompare("verbose",option+1) == 0)
        {
          /* FUTURE: move to SyncImageSettings() and AcquireImage()???
             three places!   ImageArtifact   ImageOption  _image_info->verbose
             Some how new images also get this artifact!
          */
          (void) SetImageArtifact(_image,option+1,IfNormalOp ? "true" :
            "false" );
          break;
        }
      if (LocaleCompare("vignette",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          if ((flags & XiValue) == 0)
            geometry_info.xi=0.1*_image->columns;
          if ((flags & PsiValue) == 0)
            geometry_info.psi=0.1*_image->rows;
          if ((flags & PercentValue) != 0)
            {
              geometry_info.xi*=(double) _image->columns/100.0;
              geometry_info.psi*=(double) _image->rows/100.0;
            }
          new_image=VignetteImage(_image,geometry_info.rho,geometry_info.sigma,
            (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t)
            ceil(geometry_info.psi-0.5),_exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'w':
    {
      if (LocaleCompare("wave",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & (RhoValue|SigmaValue)) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=1.0;
          new_image=WaveImage(_image,geometry_info.rho,geometry_info.sigma,
            _image->interpolate,_exception);
          break;
        }
      if (LocaleCompare("wavelet-denoise",option+1) == 0)
        {
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          if ((flags & PercentValue) != 0)
            {
              geometry_info.rho=QuantumRange*geometry_info.rho/100.0;
              geometry_info.sigma=QuantumRange*geometry_info.sigma/100.0;
            }
          if ((flags & SigmaValue) == 0)
            geometry_info.sigma=0.0;
          new_image=WaveletDenoiseImage(_image,geometry_info.rho,
            geometry_info.sigma,_exception);
          break;
        }
      if (LocaleCompare("white-balance",option+1) == 0)
        {
          (void) WhiteBalanceImage(_image,_exception);
          break;
        }
      if (LocaleCompare("white-threshold",option+1) == 0)
        {
          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          (void) WhiteThresholdImage(_image,arg1,_exception);
          break;
        }
      if (LocaleCompare("write-mask",option+1) == 0)
        {
          /* Note: arguments do not have percent escapes expanded */
          Image
            *mask;

          if (IfPlusOp)
            { /* Remove a mask. */
              (void) SetImageMask(_image,WritePixelMask,(const Image *) NULL,
                _exception);
              break;
            }
          /* Set the image mask. */
          mask=GetImageCache(_image_info,arg1,_exception);
          if (mask == (Image *) NULL)
            break;
          (void) SetImageMask(_image,WritePixelMask,mask,_exception);
          mask=DestroyImage(mask);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    default:
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
  }
  /* clean up percent escape interpreted strings */
  if (arg1 != arg1n )
    arg1=DestroyString((char *)arg1);
  if (arg2 != arg2n )
    arg2=DestroyString((char *)arg2);

  /* Replace current image with any image that was generated
     and set image point to last image (so image->next is correct) */
  if (new_image != (Image *) NULL)
    ReplaceImageInListReturnLast(&_image,new_image);

  return(MagickTrue);
#undef _image_info
#undef _draw_info
#undef _quantize_info
#undef _image
#undef _exception
#undef IfNormalOp
#undef IfPlusOp
#undef IsNormalOp
#undef IsPlusOp
}

WandPrivate MagickBooleanType CLISimpleOperatorImages(MagickCLI *cli_wand,
  const char *option,const char *arg1,const char *arg2,ExceptionInfo *exception)
{
#if !USE_WAND_METHODS
  size_t
    n,
    i;
#endif

  assert(cli_wand != (MagickCLI *) NULL);
  assert(cli_wand->signature == MagickWandSignature);
  assert(cli_wand->wand.signature == MagickWandSignature);
  assert(cli_wand->wand.images != (Image *) NULL); /* images must be present */

  if (cli_wand->wand.debug != MagickFalse)
    (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
         "- Simple Operator: %s \"%s\" \"%s\"", option,arg1,arg2);

#if !USE_WAND_METHODS
  /* FUTURE add appropriate tracing */
  i=0;
  n=GetImageListLength(cli_wand->wand.images);
  cli_wand->wand.images=GetFirstImageInList(cli_wand->wand.images);
  while (1) {
    i++;
    CLISimpleOperatorImage(cli_wand, option, arg1, arg2,exception);
    if ( cli_wand->wand.images->next == (Image *) NULL )
      break;
    cli_wand->wand.images=cli_wand->wand.images->next;
  }
  assert( i == n );
  cli_wand->wand.images=GetFirstImageInList(cli_wand->wand.images);
#else
  MagickResetIterator(&cli_wand->wand);
  while (MagickNextImage(&cli_wand->wand) != MagickFalse)
    (void) CLISimpleOperatorImage(cli_wand, option, arg1, arg2,exception);
  MagickResetIterator(&cli_wand->wand);
#endif
  return(MagickTrue);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
+     C L I L i s t O p e r a t o r I m a g e s                               %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  CLIListOperatorImages() applies a single operation that is apply to the
%  entire image list as a whole. The result is often a complete replacment
%  of the image list with a completely new list, or with just a single image
%  result.
%
%  The format of the MogrifyImage method is:
%
%    MagickBooleanType CLIListOperatorImages(MagickCLI *cli_wand,
%      const char *option,const char *arg1,const char *arg2)
%
%  A description of each parameter follows:
%
%    o cli_wand: structure holding settings to be applied
%
%    o option:  The option string for the operation
%
%    o arg1, arg2: optional argument strings to the operation
%        arg2 is currently not used
%
*/
WandPrivate MagickBooleanType CLIListOperatorImages(MagickCLI *cli_wand,
  const char *option,const char *arg1n,const char *arg2n)
{
  const char    /* percent escaped versions of the args */
    *arg1,
    *arg2;

  Image
    *new_images;

  MagickStatusType
    status;

  ssize_t
    parse;

#define _image_info     (cli_wand->wand.image_info)
#define _images         (cli_wand->wand.images)
#define _exception      (cli_wand->wand.exception)
#define _draw_info      (cli_wand->draw_info)
#define _quantize_info  (cli_wand->quantize_info)
#define _process_flags  (cli_wand->process_flags)
#define _option_type    ((CommandOptionFlags) cli_wand->command->flags)
#define IfNormalOp      (*option=='-')
#define IfPlusOp        (*option!='-')
#define IsNormalOp      IfNormalOp ? MagickTrue : MagickFalse

  assert(cli_wand != (MagickCLI *) NULL);
  assert(cli_wand->signature == MagickWandSignature);
  assert(cli_wand->wand.signature == MagickWandSignature);
  assert(_images != (Image *) NULL);             /* _images must be present */

  if (cli_wand->wand.debug != MagickFalse)
    (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
       "- List Operator: %s \"%s\" \"%s\"", option,
       arg1n == (const char *) NULL ? "null" : arg1n,
       arg2n == (const char *) NULL ? "null" : arg2n);

  arg1 = arg1n;
  arg2 = arg2n;

  /* Interpret Percent Escapes in Arguments - using first image */
  if ( (((_process_flags & ProcessInterpretProperities) != 0 )
        || ((_option_type & AlwaysInterpretArgsFlag) != 0)
       )  && ((_option_type & NeverInterpretArgsFlag) == 0) ) {
    /* Interpret Percent escapes in argument 1 */
    if (arg1n != (char *) NULL) {
      arg1=InterpretImageProperties(_image_info,_images,arg1n,_exception);
      if (arg1 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg1=arg1n;  /* use the given argument as is */
      }
    }
    if (arg2n != (char *) NULL) {
      arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
      if (arg2 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg2=arg2n;  /* use the given argument as is */
      }
    }
  }
#undef _process_flags
#undef _option_type

  status=MagickTrue;
  new_images=NewImageList();

  switch (*(option+1))
  {
    case 'a':
    {
      if (LocaleCompare("append",option+1) == 0)
        {
          new_images=AppendImages(_images,IsNormalOp,_exception);
          break;
        }
      if (LocaleCompare("average",option+1) == 0)
        {
          CLIWandWarnReplaced("-evaluate-sequence Mean");
          (void) CLIListOperatorImages(cli_wand,"-evaluate-sequence","Mean",
            NULL);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'c':
    {
      if (LocaleCompare("channel-fx",option+1) == 0)
        {
          new_images=ChannelFxImage(_images,arg1,_exception);
          break;
        }
      if (LocaleCompare("clut",option+1) == 0)
        {
          Image
            *clut_image;

          /* FUTURE - make this a compose option, and thus can be used
             with layers compose or even compose last image over all other
             _images.
          */
          new_images=RemoveFirstImageFromList(&_images);
          clut_image=RemoveFirstImageFromList(&_images);
          /* FUTURE - produce Exception, rather than silent fail */
          if (clut_image == (Image *) NULL)
            {
              (void) ThrowMagickException(_exception,GetMagickModule(),
                OptionError,"ImageSequenceRequired","`%s'",option);
              new_images=DestroyImage(new_images);
              status=MagickFalse;
              break;
            }
          (void) ClutImage(new_images,clut_image,new_images->interpolate,
            _exception);
          clut_image=DestroyImage(clut_image);
          break;
        }
      if (LocaleCompare("coalesce",option+1) == 0)
        {
          new_images=CoalesceImages(_images,_exception);
          break;
        }
      if (LocaleCompare("combine",option+1) == 0)
        {
          parse=(ssize_t) _images->colorspace;
          if (_images->number_channels < GetImageListLength(_images))
            parse=sRGBColorspace;
          if ( IfPlusOp )
            parse=ParseCommandOption(MagickColorspaceOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedColorspace",option,
              arg1);
          new_images=CombineImages(_images,(ColorspaceType) parse,_exception);
          break;
        }
      if (LocaleCompare("compare",option+1) == 0)
        {
          double
            distortion;

          Image
            *image,
            *reconstruct_image;

          MetricType
            metric;

          /*
            Mathematically and visually annotate the difference between an
            image and its reconstruction.
          */
          image=RemoveFirstImageFromList(&_images);
          reconstruct_image=RemoveFirstImageFromList(&_images);
          /* FUTURE - produce Exception, rather than silent fail */
          if (reconstruct_image == (Image *) NULL)
            {
              (void) ThrowMagickException(_exception,GetMagickModule(),
                OptionError,"ImageSequenceRequired","`%s'",option);
              image=DestroyImage(image);
              status=MagickFalse;
              break;
            }
          metric=UndefinedErrorMetric;
          option=GetImageOption(_image_info,"metric");
          if (option != (const char *) NULL)
            metric=(MetricType) ParseCommandOption(MagickMetricOptions,
              MagickFalse,option);
          new_images=CompareImages(image,reconstruct_image,metric,&distortion,
            _exception);
          (void) distortion;
          reconstruct_image=DestroyImage(reconstruct_image);
          image=DestroyImage(image);
          break;
        }
      if (LocaleCompare("complex",option+1) == 0)
        {
          parse=ParseCommandOption(MagickComplexOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedEvaluateOperator",
              option,arg1);
          new_images=ComplexImages(_images,(ComplexOperator) parse,_exception);
          break;
        }
      if (LocaleCompare("composite",option+1) == 0)
        {
          CompositeOperator
            compose;

          const char*
            value;

          MagickBooleanType
            clip_to_self;

          Image
            *mask_image,
            *source_image;

          RectangleInfo
            geometry;

          /* Compose value from "-compose" option only */
          value=GetImageOption(_image_info,"compose");
          if (value == (const char *) NULL)
            compose=OverCompositeOp;  /* use Over not source_image->compose */
          else
            compose=(CompositeOperator) ParseCommandOption(MagickComposeOptions,
              MagickFalse,value);

          /* Get "clip-to-self" expert setting (false is normal) */
          clip_to_self=GetCompositeClipToSelf(compose);
          value=GetImageOption(_image_info,"compose:clip-to-self");
          if (value != (const char *) NULL)
            clip_to_self=IsStringTrue(value);
          value=GetImageOption(_image_info,"compose:outside-overlay");
          if (value != (const char *) NULL)
            clip_to_self=IsStringFalse(value);  /* deprecated */

          new_images=RemoveFirstImageFromList(&_images);
          source_image=RemoveFirstImageFromList(&_images);
          if (source_image == (Image *) NULL)
            {
              (void) ThrowMagickException(_exception,GetMagickModule(),
                OptionError,"ImageSequenceRequired","`%s'",option);
              new_images=DestroyImage(new_images);
              status=MagickFalse;
              break;
            }

          /* FUTURE - this should not be here! - should be part of -geometry */
          if (source_image->geometry != (char *) NULL)
            {
              RectangleInfo
                resize_geometry;

              (void) ParseRegionGeometry(source_image,source_image->geometry,
                &resize_geometry,_exception);
              if ((source_image->columns != resize_geometry.width) ||
                  (source_image->rows != resize_geometry.height))
                {
                  Image
                    *resize_image;

                  resize_image=ResizeImage(source_image,resize_geometry.width,
                    resize_geometry.height,source_image->filter,_exception);
                  if (resize_image != (Image *) NULL)
                    {
                      source_image=DestroyImage(source_image);
                      source_image=resize_image;
                    }
                }
            }
          SetGeometry(source_image,&geometry);
          (void) ParseAbsoluteGeometry(source_image->geometry,&geometry);
          GravityAdjustGeometry(new_images->columns,new_images->rows,
            new_images->gravity, &geometry);
          mask_image=RemoveFirstImageFromList(&_images);
          if (mask_image == (Image *) NULL)
            status&=CompositeImage(new_images,source_image,compose,clip_to_self,
              geometry.x,geometry.y,_exception);
          else
            {
              Image
                *canvas_images;

              canvas_images=CloneImage(new_images,0,0,MagickTrue,_exception);
              if (canvas_images == (Image *) NULL)
                break;
              switch (compose)
              {
                case BlendCompositeOp:
                {
                  status&=CompositeImage(new_images,source_image,compose,
                    clip_to_self,geometry.x,geometry.y,_exception);
                  status&=CompositeImage(new_images,mask_image,
                    CopyAlphaCompositeOp,MagickTrue,0,0,_exception);
                  break;
                }
                case DisplaceCompositeOp:
                case DistortCompositeOp:
                {
                  status&=CompositeImage(source_image,mask_image,
                    CopyGreenCompositeOp,MagickTrue,0,0,_exception);
                  status&=CompositeImage(new_images,source_image,compose,
                    clip_to_self,geometry.x,geometry.y,_exception);
                  break;
                }
                case SaliencyBlendCompositeOp:
                case SeamlessBlendCompositeOp:
                {
                  status&=CompositeImage(source_image,mask_image,
                    CopyAlphaCompositeOp,MagickTrue,0,0,_exception);
                  status&=CompositeImage(new_images,source_image,compose,
                    clip_to_self,geometry.x,geometry.y,_exception);
                  break;
                }
                default:
                {
                  Image
                    *clone_image;

                  clone_image=CloneImage(new_images,0,0,MagickTrue,_exception);
                  if (clone_image == (Image *) NULL)
                    break;
                  status&=CompositeImage(new_images,source_image,compose,
                    clip_to_self,geometry.x,geometry.y,_exception);
                  status&=CompositeImage(new_images,mask_image,
                    CopyAlphaCompositeOp,MagickTrue,0,0,_exception);
                  status&=CompositeImage(clone_image,new_images,OverCompositeOp,
                    clip_to_self,0,0,_exception);
                  new_images=DestroyImageList(new_images);
                  new_images=clone_image;
                  break;
                }
              }
              status&=CompositeImage(canvas_images,new_images,OverCompositeOp,
                clip_to_self,0,0,_exception);
              new_images=DestroyImageList(new_images);
              new_images=canvas_images;
              mask_image=DestroyImage(mask_image);
            }
          source_image=DestroyImage(source_image);
          break;
        }
        if (LocaleCompare("copy",option+1) == 0)
          {
            Image
              *source_image;

            OffsetInfo
              offset;

            RectangleInfo
              geometry;

            /*
              Copy image pixels.
            */
            if (IsGeometry(arg1) == MagickFalse)
              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
            if (IsGeometry(arg2) == MagickFalse)
              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
            (void) ParsePageGeometry(_images,arg2,&geometry,_exception);
            offset.x=geometry.x;
            offset.y=geometry.y;
            source_image=_images;
            if (source_image->next != (Image *) NULL)
              source_image=source_image->next;
            (void) ParsePageGeometry(source_image,arg1,&geometry,_exception);
            (void) CopyImagePixels(_images,source_image,&geometry,&offset,
              _exception);
            break;
          }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'd':
    {
      if (LocaleCompare("deconstruct",option+1) == 0)
        {
          CLIWandWarnReplaced("-layers CompareAny");
          (void) CLIListOperatorImages(cli_wand,"-layers","CompareAny",NULL);
          break;
        }
      if (LocaleCompare("delete",option+1) == 0)
        {
          if (IfNormalOp)
            DeleteImages(&_images,arg1,_exception);
          else
            DeleteImages(&_images,"-1",_exception);
          break;
        }
      if (LocaleCompare("duplicate",option+1) == 0)
        {
          if (!IfNormalOp)
            new_images=DuplicateImages(_images,1,"-1",_exception);
          else
            {
              const char
                *p;

              size_t
                number_duplicates;

              if (IsGeometry(arg1) == MagickFalse)
                CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,
                  arg1);
              number_duplicates=(size_t) StringToLong(arg1);
              p=strchr(arg1,',');
              if (p == (const char *) NULL)
                new_images=DuplicateImages(_images,number_duplicates,"-1",
                  _exception);
              else
                new_images=DuplicateImages(_images,number_duplicates,p+1,
                  _exception);
            }
          AppendImageToList(&_images, new_images);
          new_images=(Image *) NULL;
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'e':
    {
      if (LocaleCompare("evaluate-sequence",option+1) == 0)
        {
          parse=ParseCommandOption(MagickEvaluateOptions,MagickFalse,arg1);
          if (parse < 0)
            CLIWandExceptArgBreak(OptionError,"UnrecognizedEvaluateOperator",
              option,arg1);
          new_images=EvaluateImages(_images,(MagickEvaluateOperator) parse,
            _exception);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'f':
    {
      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);
    }
    case 'h':
    {
      if (LocaleCompare("hald-clut",option+1) == 0)
        {
          /* FUTURE - make this a compose option (and thus layers compose )
             or perhaps compose last image over all other _images.
          */
          Image
            *hald_image;

          new_images=RemoveFirstImageFromList(&_images);
          hald_image=RemoveLastImageFromList(&_images);
          if (hald_image == (Image *) NULL)
            {
              (void) ThrowMagickException(_exception,GetMagickModule(),
                OptionError,"ImageSequenceRequired","`%s'",option);
              new_images=DestroyImage(new_images);
              status=MagickFalse;
              break;
            }
          (void) HaldClutImage(new_images,hald_image,_exception);
          hald_image=DestroyImage(hald_image);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'i':
    {
      if (LocaleCompare("ift",option+1) == 0)
        {
          Image
            *magnitude_image,
            *phase_image;

          magnitude_image=RemoveFirstImageFromList(&_images);
          phase_image=RemoveFirstImageFromList(&_images);
          if (phase_image == (Image *) NULL)
            {
              (void) ThrowMagickException(_exception,GetMagickModule(),
                OptionError,"ImageSequenceRequired","`%s'",option);
              magnitude_image=DestroyImage(magnitude_image);
              status=MagickFalse;
              break;
            }
          new_images=InverseFourierTransformImage(magnitude_image,phase_image,
            IsNormalOp,_exception);
          magnitude_image=DestroyImage(magnitude_image);
          phase_image=DestroyImage(phase_image);
          break;
        }
      if (LocaleCompare("insert",option+1) == 0)
        {
          Image
            *insert_image,
            *index_image;

          ssize_t
            index;

          if (IfNormalOp && (IsGeometry(arg1) == MagickFalse))
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          index=0;
          insert_image=RemoveLastImageFromList(&_images);
          if (IfNormalOp)
            index=(ssize_t) StringToLong(arg1);
          index_image=insert_image;
          if (index == 0)
            PrependImageToList(&_images,insert_image);
          else if (index == (ssize_t) GetImageListLength(_images))
            AppendImageToList(&_images,insert_image);
          else
            {
               index_image=GetImageFromList(_images,index-1);
               if (index_image == (Image *) NULL)
                 {
                   insert_image=DestroyImage(insert_image);
                   CLIWandExceptArgBreak(OptionError,"NoSuchImage",option,arg1);
                 }
              InsertImageInList(&index_image,insert_image);
            }
          _images=GetFirstImageInList(index_image);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'l':
    {
      if (LocaleCompare("layers",option+1) == 0)
        {
          parse=ParseCommandOption(MagickLayerOptions,MagickFalse,arg1);
          if ( parse < 0 )
            CLIWandExceptArgBreak(OptionError,"UnrecognizedLayerMethod",
                 option,arg1);
          switch ((LayerMethod) parse)
          {
            case CoalesceLayer:
            {
              new_images=CoalesceImages(_images,_exception);
              break;
            }
            case CompareAnyLayer:
            case CompareClearLayer:
            case CompareOverlayLayer:
            default:
            {
              new_images=CompareImagesLayers(_images,(LayerMethod) parse,
                   _exception);
              break;
            }
            case MergeLayer:
            case FlattenLayer:
            case MosaicLayer:
            case TrimBoundsLayer:
            {
              new_images=MergeImageLayers(_images,(LayerMethod) parse,
                _exception);
              break;
            }
            case DisposeLayer:
            {
              new_images=DisposeImages(_images,_exception);
              break;
            }
            case OptimizeImageLayer:
            {
              new_images=OptimizeImageLayers(_images,_exception);
              break;
            }
            case OptimizePlusLayer:
            {
              new_images=OptimizePlusImageLayers(_images,_exception);
              break;
            }
            case OptimizeTransLayer:
            {
              OptimizeImageTransparency(_images,_exception);
              break;
            }
            case RemoveDupsLayer:
            {
              RemoveDuplicateLayers(&_images,_exception);
              break;
            }
            case RemoveZeroLayer:
            {
              RemoveZeroDelayLayers(&_images,_exception);
              break;
            }
            case OptimizeLayer:
            { /* General Purpose, GIF Animation Optimizer.  */
              new_images=CoalesceImages(_images,_exception);
              if (new_images == (Image *) NULL)
                break;
              _images=DestroyImageList(_images);
              _images=OptimizeImageLayers(new_images,_exception);
              if (_images == (Image *) NULL)
                break;
              new_images=DestroyImageList(new_images);
              OptimizeImageTransparency(_images,_exception);
              (void) RemapImages(_quantize_info,_images,(Image *) NULL,
                _exception);
              break;
            }
            case CompositeLayer:
            {
              Image
                *source;

              RectangleInfo
                geometry;

              CompositeOperator
                compose;

              const char*
                value;

              value=GetImageOption(_image_info,"compose");
              compose=OverCompositeOp;  /* Default to Over */
              if (value != (const char *) NULL)
                compose=(CompositeOperator) ParseCommandOption(
                      MagickComposeOptions,MagickFalse,value);

              /* Split image sequence at the first 'NULL:' image. */
              source=_images;
              while (source != (Image *) NULL)
              {
                source=GetNextImageInList(source);
                if ((source != (Image *) NULL) &&
                    (LocaleCompare(source->magick,"NULL") == 0))
                  break;
              }
              if (source != (Image *) NULL)
                {
                  if ((GetPreviousImageInList(source) == (Image *) NULL) ||
                      (GetNextImageInList(source) == (Image *) NULL))
                    source=(Image *) NULL;
                  else
                    { /* Separate the two lists, junk the null: image.  */
                      source=SplitImageList(source->previous);
                      DeleteImageFromList(&source);
                    }
                }
              if (source == (Image *) NULL)
                {
                  (void) ThrowMagickException(_exception,GetMagickModule(),
                    OptionError,"MissingNullSeparator","layers Composite");
                  break;
                }
              /* Adjust offset with gravity and virtual canvas.  */
              SetGeometry(_images,&geometry);
              (void) ParseAbsoluteGeometry(_images->geometry,&geometry);
              geometry.width=source->page.width != 0 ?
                source->page.width : source->columns;
              geometry.height=source->page.height != 0 ?
               source->page.height : source->rows;
              GravityAdjustGeometry(_images->page.width != 0 ?
                _images->page.width : _images->columns,
                _images->page.height != 0 ? _images->page.height :
                _images->rows,_images->gravity,&geometry);

              /* Compose the two image sequences together */
              CompositeLayers(_images,compose,source,geometry.x,geometry.y,
                _exception);
              source=DestroyImageList(source);
              break;
            }
          }
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'm':
    {
      if (LocaleCompare("map",option+1) == 0)
        {
          CLIWandWarnReplaced("+remap");
          (void) RemapImages(_quantize_info,_images,(Image *) NULL,_exception);
          break;
        }
      if (LocaleCompare("metric",option+1) == 0)
        {
          (void) SetImageOption(_image_info,option+1,arg1);
          break;
        }
      if (LocaleCompare("morph",option+1) == 0)
        {
          Image
            *morph_image;

          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          morph_image=MorphImages(_images,StringToUnsignedLong(arg1),
            _exception);
          if (morph_image == (Image *) NULL)
            break;
          _images=DestroyImageList(_images);
          _images=morph_image;
          break;
        }
      if (LocaleCompare("mosaic",option+1) == 0)
        {
          /* REDIRECTED to use -layers mosaic instead */
          (void) CLIListOperatorImages(cli_wand,"-layers",option+1,NULL);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'p':
    {
      if (LocaleCompare("poly",option+1) == 0)
        {
          double
            *args;

          ssize_t
            count;

          /* convert argument string into an array of doubles */
          args = StringToArrayOfDoubles(arg1,&count,_exception);
          if (args == (double *) NULL )
            CLIWandExceptArgBreak(OptionError,"InvalidNumberList",option,arg1);
          new_images=PolynomialImage(_images,(size_t) (count >> 1),args,
           _exception);
          args=(double *) RelinquishMagickMemory(args);
          break;
        }
      if (LocaleCompare("process",option+1) == 0)
        {
          /* FUTURE: better parsing using ScriptToken() from string ??? */
          char
            **arguments;

          int
            j,
            number_arguments;

          arguments=StringToArgv(arg1,&number_arguments);
          if (arguments == (char **) NULL)
            break;
          if (strchr(arguments[1],'=') != (char *) NULL)
            {
              char
                breaker,
                quote,
                *token;

              const char
                *p;

              int
                next,
                tokenizer_status;

              size_t
                length;

              TokenInfo
                *token_info;

              /*
                Support old style syntax, filter="-option arg1".
              */
              assert(arg1 != (const char *) NULL);
              length=strlen(arg1);
              token=(char *) NULL;
              if (~length >= (MagickPathExtent-1))
                token=(char *) AcquireQuantumMemory(length+MagickPathExtent,
                  sizeof(*token));
              if (token == (char *) NULL)
                break;
              next=0;
              p=arg1;
              token_info=AcquireTokenInfo();
              tokenizer_status=Tokenizer(token_info,0,token,length,p,"","=",
                "\"",'\0',&breaker,&next,&quote);
              token_info=DestroyTokenInfo(token_info);
              if (tokenizer_status == 0)
                {
                  const char
                    *argv;

                  argv=(&(p[next]));
                  (void) InvokeDynamicImageFilter(token,&_images,1,&argv,
                    _exception);
                }
              token=DestroyString(token);
              break;
            }
          (void) SubstituteString(&arguments[1],"-","");
          (void) InvokeDynamicImageFilter(arguments[1],&_images,
            number_arguments-2,(const char **) arguments+2,_exception);
          for (j=0; j < number_arguments; j++)
            arguments[j]=DestroyString(arguments[j]);
          arguments=(char **) RelinquishMagickMemory(arguments);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 'r':
    {
      if (LocaleCompare("remap",option+1) == 0)
        {
          (void) RemapImages(_quantize_info,_images,(Image *) NULL,_exception);
          break;
        }
      if (LocaleCompare("reverse",option+1) == 0)
        {
          ReverseImageList(&_images);
          break;
        }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    case 's':
    {
      if (LocaleCompare("smush",option+1) == 0)
        {
          /* FUTURE: this option needs more work to make better */
          ssize_t
            offset;

          if (IsGeometry(arg1) == MagickFalse)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          offset=(ssize_t) StringToLong(arg1);
          new_images=SmushImages(_images,IsNormalOp,offset,_exception);
          break;
        }
      if (LocaleCompare("subimage",option+1) == 0)
        {
          Image
            *base_image,
            *compare_image;

          const char
            *value;

          MetricType
            metric;

          double
            similarity;

          RectangleInfo
            offset;

          base_image=GetImageFromList(_images,0);
          compare_image=GetImageFromList(_images,1);

          /* Comparision Metric */
          metric=UndefinedErrorMetric;
          value=GetImageOption(_image_info,"metric");
          if (value != (const char *) NULL)
            metric=(MetricType) ParseCommandOption(MagickMetricOptions,
              MagickFalse,value);

          new_images=SimilarityImage(base_image,compare_image,metric,0.0,
            &offset,&similarity,_exception);

          if (new_images != (Image *) NULL)
            {
              (void) FormatImageProperty(new_images,"subimage:similarity",
                "%.*g",GetMagickPrecision(),similarity);
              (void) FormatImageProperty(new_images,"subimage:x","%+ld",(long)
                offset.x);
              (void) FormatImageProperty(new_images,"subimage:y","%+ld",(long)
                offset.y);
              (void) FormatImageProperty(new_images,"subimage:offset",
                "%lux%lu%+ld%+ld",(unsigned long) offset.width,(unsigned long)
                offset.height,(long) offset.x,(long) offset.y);
            }
          break;
        }
      if (LocaleCompare("swap",option+1) == 0)
        {
        Image
          *p,
          *q,
          *swap;

        ssize_t
          index,
          swap_index;

        index=(-1);
        swap_index=(-2);
        if (IfNormalOp) {
          GeometryInfo
            geometry_info;

          MagickStatusType
            flags;

          swap_index=(-1);
          flags=ParseGeometry(arg1,&geometry_info);
          if ((flags & RhoValue) == 0)
            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
          index=(ssize_t) geometry_info.rho;
          if ((flags & SigmaValue) != 0)
            swap_index=(ssize_t) geometry_info.sigma;
        }
        p=GetImageFromList(_images,index);
        q=GetImageFromList(_images,swap_index);
        if ((p == (Image *) NULL) || (q == (Image *) NULL)) {
          if (IfNormalOp)
            CLIWandExceptArgBreak(OptionError,"InvalidImageIndex",option,arg1)
          else
            CLIWandExceptionBreak(OptionError,"TwoOrMoreImagesRequired",option);
        }
        if (p == q)
          CLIWandExceptArgBreak(OptionError,"InvalidImageIndex",option,arg1);
        swap=CloneImage(p,0,0,MagickTrue,_exception);
        if (swap == (Image *) NULL)
          CLIWandExceptArgBreak(ResourceLimitError,"MemoryAllocationFailed",
            option,GetExceptionMessage(errno));
        ReplaceImageInList(&p,CloneImage(q,0,0,MagickTrue,_exception));
        ReplaceImageInList(&q,swap);
        _images=GetFirstImageInList(q);
        break;
      }
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
    }
    default:
      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
  }

  /* clean up percent escape interpreted strings */
  if (arg1 != arg1n )
    arg1=DestroyString((char *)arg1);
  if (arg2 != arg2n )
    arg2=DestroyString((char *)arg2);

  /* if new image list generated, replace existing image list */
  if (new_images == (Image *) NULL)
    return(status == 0 ? MagickFalse : MagickTrue);
  _images=DestroyImageList(_images);
  _images=GetFirstImageInList(new_images);
  return(status == 0 ? MagickFalse : MagickTrue);

#undef _image_info
#undef _images
#undef _exception
#undef _draw_info
#undef _quantize_info
#undef IfNormalOp
#undef IfPlusOp
#undef IsNormalOp
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
+   C L I N o I m a g e O p e r a t i o n s                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  CLINoImageOperator() Applies operations that may not actually need images
%  in an image list.
%
%  The classic operators of this type is "-read", which actually creates
%  images even when no images are present.  Or image stack operators, which
%  can be applied (push or pop) to an empty image list.
%
%  Note that these operators may involve other special 'option' prefix
%  characters other  than '-' or '+', namely parenthesis and braces.
%
%  The format of the CLINoImageOption method is:
%
%      void CLINoImageOption(MagickCLI *cli_wand,const char *option,
%           const char *arg1, const char *arg2)
%
%  A description of each parameter follows:
%
%    o cli_wand: the main CLI Wand to use. (sometimes not required)
%
%    o option: The special option (with any switch char) to process
%
%    o arg1 & arg2: Argument for option, if required
%                   Currently arg2 is not used.
%
*/
WandPrivate void CLINoImageOperator(MagickCLI *cli_wand,
  const char *option,const char *arg1n,const char *arg2n)
{
  const char    /* percent escaped versions of the args */
    *arg1,
    *arg2;

#define _image_info     (cli_wand->wand.image_info)
#define _images         (cli_wand->wand.images)
#define _exception      (cli_wand->wand.exception)
#define _process_flags  (cli_wand->process_flags)
#define _option_type    ((CommandOptionFlags) cli_wand->command->flags)
#define IfNormalOp      (*option=='-')
#define IfPlusOp        (*option!='-')

  assert(cli_wand != (MagickCLI *) NULL);
  assert(cli_wand->signature == MagickWandSignature);
  assert(cli_wand->wand.signature == MagickWandSignature);

  if (cli_wand->wand.debug != MagickFalse)
    (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
      "- NoImage Operator: %s \"%s\" \"%s\"", option,
      arg1n != (char *) NULL ? arg1n : "",
      arg2n != (char *) NULL ? arg2n : "");

  arg1 = arg1n;
  arg2 = arg2n;

  /* Interpret Percent Escapes in Arguments - using first image */
  if ( (((_process_flags & ProcessInterpretProperities) != 0 )
        || ((_option_type & AlwaysInterpretArgsFlag) != 0)
       )  && ((_option_type & NeverInterpretArgsFlag) == 0) ) {
    /* Interpret Percent escapes in argument 1 */
    if (arg1n != (char *) NULL) {
      arg1=InterpretImageProperties(_image_info,_images,arg1n,_exception);
      if (arg1 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg1=arg1n;  /* use the given argument as is */
      }
    }
    if (arg2n != (char *) NULL) {
      arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
      if (arg2 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg2=arg2n;  /* use the given argument as is */
      }
    }
  }
#undef _process_flags
#undef _option_type

  do {  /* break to exit code */
    /*
      No-op options  (ignore these)
    */
    if (LocaleCompare("noop",option+1) == 0)   /* zero argument */
      break;
    if (LocaleCompare("sans",option+1) == 0)   /* one argument */
      break;
    if (LocaleCompare("sans0",option+1) == 0)  /* zero argument */
      break;
    if (LocaleCompare("sans1",option+1) == 0)  /* one argument */
      break;
    if (LocaleCompare("sans2",option+1) == 0)  /* two arguments */
      break;
    /*
      Image Reading
    */
    if ( ( LocaleCompare("read",option+1) == 0 ) ||
      ( LocaleCompare("--",option) == 0 ) ) {
      /* Do Glob filename Expansion for 'arg1' then read all images.
      *
      * Expansion handles '@', '~', '*', and '?' meta-characters while ignoring
      * (but attaching to the filenames in the generated argument list) any
      * [...] read modifiers that may be present.
      *
      * For example: It will expand '*.gif[20x20]' into a list such as
      * 'abc.gif[20x20]',  'foobar.gif[20x20]',  'xyzzy.gif[20x20]'
      *
      * NOTE: In IMv6 this was done globally across all images. This
      * meant you could include IM options in '@filename' lists, but you
      * could not include comments.   Doing it only for image read makes
      * it far more secure.
      *
      * Note: arguments do not have percent escapes expanded for security
      * reasons.
      */
      int      argc;
      char     **argv;
      ssize_t  i;

      argc = 1;
      argv = (char **) &arg1;

      /* Expand 'glob' expressions in the given filename.
        Expansion handles any 'coder:' prefix, or read modifiers attached
        to the filename, including them in the resulting expanded list.
      */
      if (ExpandFilenames(&argc,&argv) == MagickFalse)
        CLIWandExceptArgBreak(ResourceLimitError,"MemoryAllocationFailed",
            option,GetExceptionMessage(errno));

      /* loop over expanded filename list, and read then all in */
      for (i=0; i < (ssize_t) argc; i++) {
        Image *
          new_images;
        if (_image_info->ping != MagickFalse)
          new_images=PingImages(_image_info,argv[i],_exception);
        else
          new_images=ReadImages(_image_info,argv[i],_exception);
        AppendImageToList(&_images, new_images);
        argv[i]=DestroyString(argv[i]);
      }
      argv=(char **) RelinquishMagickMemory(argv);
      break;
    }
    /*
      Image Writing
      Note: Writing a empty image list is valid in specific cases
    */
    if (LocaleCompare("write",option+1) == 0) {
      /* Note: arguments do not have percent escapes expanded */
      char
        key[MagickPathExtent];

      Image
        *write_images;

      ImageInfo
        *write_info;

      /* Need images, unless a "null:" output coder is used */
      if ( _images == (Image *) NULL ) {
        if ( LocaleCompare(arg1,"null:") == 0 )
          break;
        CLIWandExceptArgBreak(OptionError,"NoImagesForWrite",option,arg1);
      }

      (void) FormatLocaleString(key,MagickPathExtent,"cache:%s",arg1);
      (void) DeleteImageRegistry(key);
      write_images=CloneImageList(_images,_exception);
      write_info=CloneImageInfo(_image_info);
      (void) WriteImages(write_info,write_images,arg1,_exception);
      write_info=DestroyImageInfo(write_info);
      write_images=DestroyImageList(write_images);
      break;
    }
    /*
      Parenthesis and Brace operations
    */
    if (LocaleCompare("(",option) == 0) {
      /* stack 'push' images */
      Stack
        *node;

      size_t
        size;

      size=0;
      node=cli_wand->image_list_stack;
      for ( ; node != (Stack *) NULL; node=node->next)
        size++;
      if ( size >= MAX_STACK_DEPTH )
        CLIWandExceptionBreak(OptionError,"ParenthesisNestedTooDeeply",option);
      node=(Stack *) AcquireMagickMemory(sizeof(*node));
      if (node == (Stack *) NULL)
        CLIWandExceptionBreak(ResourceLimitFatalError,
            "MemoryAllocationFailed",option);
      node->data = (void *)cli_wand->wand.images;
      node->next = cli_wand->image_list_stack;
      cli_wand->image_list_stack = node;
      cli_wand->wand.images = NewImageList();

      /* handle respect-parenthesis */
      if (IsStringTrue(GetImageOption(cli_wand->wand.image_info,
                    "respect-parenthesis")) != MagickFalse)
        option="{"; /* fall-thru so as to push image settings too */
      else
        break;
      /* fall thru to operation */
    }
    if (LocaleCompare("{",option) == 0) {
      /* stack 'push' of image_info settings */
      Stack
        *node;

      size_t
        size;

      size=0;
      node=cli_wand->image_info_stack;
      for ( ; node != (Stack *) NULL; node=node->next)
        size++;
      if ( size >= MAX_STACK_DEPTH )
        CLIWandExceptionBreak(OptionError,"CurlyBracesNestedTooDeeply",option);
      node=(Stack *) AcquireMagickMemory(sizeof(*node));
      if (node == (Stack *) NULL)
        CLIWandExceptionBreak(ResourceLimitFatalError,
            "MemoryAllocationFailed",option);

      node->data = (void *)cli_wand->wand.image_info;
      node->next = cli_wand->image_info_stack;

      cli_wand->image_info_stack = node;
      cli_wand->wand.image_info = CloneImageInfo(cli_wand->wand.image_info);
      if (cli_wand->wand.image_info == (ImageInfo *) NULL) {
        CLIWandException(ResourceLimitFatalError,"MemoryAllocationFailed",
            option);
        cli_wand->wand.image_info = (ImageInfo *)node->data;
        node = (Stack *)RelinquishMagickMemory(node);
        break;
      }

      break;
    }
    if (LocaleCompare(")",option) == 0) {
      /* pop images from stack */
      Stack
        *node;

      node = (Stack *)cli_wand->image_list_stack;
      if ( node == (Stack *) NULL)
        CLIWandExceptionBreak(OptionError,"UnbalancedParenthesis",option);
      cli_wand->image_list_stack = node->next;

      AppendImageToList((Image **)&node->data,cli_wand->wand.images);
      cli_wand->wand.images= (Image *)node->data;
      node = (Stack *)RelinquishMagickMemory(node);

      /* handle respect-parenthesis - of the previous 'pushed' settings */
      node = cli_wand->image_info_stack;
      if ( node != (Stack *) NULL)
        {
          if (IsStringTrue(GetImageOption(
                cli_wand->wand.image_info,"respect-parenthesis")) != MagickFalse)
            option="}"; /* fall-thru so as to pop image settings too */
          else
            break;
        }
      else
        break;
      /* fall thru to next if */
    }
    if (LocaleCompare("}",option) == 0) {
      /* pop image_info settings from stack */
      Stack
        *node;

      node = (Stack *)cli_wand->image_info_stack;
      if ( node == (Stack *) NULL)
        CLIWandExceptionBreak(OptionError,"UnbalancedCurlyBraces",option);
      cli_wand->image_info_stack = node->next;

      (void) DestroyImageInfo(cli_wand->wand.image_info);
      cli_wand->wand.image_info = (ImageInfo *)node->data;
      node = (Stack *)RelinquishMagickMemory(node);

      GetDrawInfo(cli_wand->wand.image_info, cli_wand->draw_info);
      cli_wand->quantize_info=DestroyQuantizeInfo(cli_wand->quantize_info);
      cli_wand->quantize_info=AcquireQuantizeInfo(cli_wand->wand.image_info);

      break;
    }
      if (LocaleCompare("print",option+1) == 0)
        {
          (void) FormatLocaleFile(stdout,"%s",arg1);
          break;
        }
    if (LocaleCompare("set",option+1) == 0)
      {
        /* Settings are applied to each image in memory in turn (if any).
           While a option: only need to be applied once globally.

           NOTE: rguments have not been automatically percent expaneded
        */

        /* escape the 'key' once only, using first image. */
        arg1=InterpretImageProperties(_image_info,_images,arg1n,_exception);
        if (arg1 == (char *) NULL)
          CLIWandExceptionBreak(OptionWarning,"InterpretPropertyFailure",
                option);

        if (LocaleNCompare(arg1,"registry:",9) == 0)
          {
            if (IfPlusOp)
              {
                (void) DeleteImageRegistry(arg1+9);
                arg1=DestroyString((char *)arg1);
                break;
              }
            arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
            if (arg2 == (char *) NULL) {
              arg1=DestroyString((char *)arg1);
              CLIWandExceptionBreak(OptionWarning,"InterpretPropertyFailure",
                    option);
            }
            (void) SetImageRegistry(StringRegistryType,arg1+9,arg2,_exception);
            arg1=DestroyString((char *)arg1);
            arg2=DestroyString((char *)arg2);
            break;
          }
        if (LocaleNCompare(arg1,"option:",7) == 0)
          {
            /* delete equivelent artifact from all images (if any) */
            if (_images != (Image *) NULL)
              {
                MagickResetIterator(&cli_wand->wand);
                while (MagickNextImage(&cli_wand->wand) != MagickFalse)
                  (void) DeleteImageArtifact(_images,arg1+7);
                MagickResetIterator(&cli_wand->wand);
              }
            /* now set/delete the global option as needed */
            /* FUTURE: make escapes in a global 'option:' delayed */
            arg2=(char *) NULL;
            if (IfNormalOp)
              {
                arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
                if (arg2 == (char *) NULL)
                  CLIWandExceptionBreak(OptionWarning,
                       "InterpretPropertyFailure",option);
              }
            (void) SetImageOption(_image_info,arg1+7,arg2);
            arg1=DestroyString((char *)arg1);
            arg2=DestroyString((char *)arg2);
            break;
          }
        /* Set Artifacts/Properties/Attributes all images (required) */
        if ( _images == (Image *) NULL )
          CLIWandExceptArgBreak(OptionWarning,"NoImageForProperty",option,arg1);

        MagickResetIterator(&cli_wand->wand);
        while (MagickNextImage(&cli_wand->wand) != MagickFalse)
          {
            arg2=(char *) NULL;
            if (IfNormalOp)
              {
                arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
                if (arg2 == (char *) NULL)
                  CLIWandExceptionBreak(OptionWarning,
                       "InterpretPropertyFailure",option);
              }
            if (LocaleNCompare(arg1,"artifact:",9) == 0)
              (void) SetImageArtifact(_images,arg1+9,arg2);
            else if (LocaleNCompare(arg1,"property:",9) == 0)
              (void) SetImageProperty(_images,arg1+9,arg2,_exception);
            else
              (void) SetImageProperty(_images,arg1,arg2,_exception);
            arg2=DestroyString((char *)arg2);
          }
        MagickResetIterator(&cli_wand->wand);
        arg1=DestroyString((char *)arg1);
        break;
     }
    if (LocaleCompare("clone",option+1) == 0) {
        Image
          *new_images;

        if (*option == '+')
          arg1=AcquireString("-1");
        if (IsSceneGeometry(arg1,MagickFalse) == MagickFalse)
          CLIWandExceptionBreak(OptionError,"InvalidArgument",option);
        if ( cli_wand->image_list_stack == (Stack *) NULL)
          CLIWandExceptionBreak(OptionError,"UnableToCloneImage",option);
        new_images = (Image *)cli_wand->image_list_stack->data;
        if (new_images == (Image *) NULL)
          CLIWandExceptionBreak(OptionError,"UnableToCloneImage",option);
        new_images=CloneImages(new_images,arg1,_exception);
        if (new_images == (Image *) NULL)
          CLIWandExceptionBreak(OptionError,"NoSuchImage",option);
        AppendImageToList(&_images,new_images);
        break;
      }
    /*
       Informational Operations.

       Note that these do not require either a cli-wand or images!
       Though currently a cli-wand much be provided regardless.
    */
    if (LocaleCompare("version",option+1) == 0)
      {
        ListMagickVersion(stdout);
        break;
      }
    if (LocaleCompare("list",option+1) == 0) {
      /*
         FUTURE: This 'switch' should really be part of MagickCore
      */
      ssize_t
        list;

      list=ParseCommandOption(MagickListOptions,MagickFalse,arg1);
      if ( list < 0 ) {
        CLIWandExceptionArg(OptionError,"UnrecognizedListType",option,arg1);
        break;
      }
      switch (list)
      {
        case MagickCoderOptions:
        {
          (void) ListCoderInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickColorOptions:
        {
          (void) ListColorInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickConfigureOptions:
        {
          (void) ListConfigureInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickDelegateOptions:
        {
          (void) ListDelegateInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickFontOptions:
        {
          (void) ListTypeInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickFormatOptions:
          (void) ListMagickInfo((FILE *) NULL,_exception);
          break;
        case MagickLocaleOptions:
          (void) ListLocaleInfo((FILE *) NULL,_exception);
          break;
        case MagickLogOptions:
          (void) ListLogInfo((FILE *) NULL,_exception);
          break;
        case MagickMagicOptions:
          (void) ListMagicInfo((FILE *) NULL,_exception);
          break;
        case MagickMimeOptions:
          (void) ListMimeInfo((FILE *) NULL,_exception);
          break;
        case MagickModuleOptions:
          (void) ListModuleInfo((FILE *) NULL,_exception);
          break;
        case MagickPolicyOptions:
          (void) ListPolicyInfo((FILE *) NULL,_exception);
          break;
        case MagickResourceOptions:
          (void) ListMagickResourceInfo((FILE *) NULL,_exception);
          break;
        case MagickThresholdOptions:
          (void) ListThresholdMaps((FILE *) NULL,_exception);
          break;
        default:
          (void) ListCommandOptions((FILE *) NULL,(CommandOption) list,
            _exception);
          break;
      }
      break;
    }

    CLIWandException(OptionError,"UnrecognizedOption",option);

DisableMSCWarning(4127)
  } while (0);  /* break to exit code. */
RestoreMSCWarning

  /* clean up percent escape interpreted strings */
  if (arg1 != arg1n )
    arg1=DestroyString((char *)arg1);
  if (arg2 != arg2n )
    arg2=DestroyString((char *)arg2);

#undef _image_info
#undef _images
#undef _exception
#undef IfNormalOp
#undef IfPlusOp
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
+   C L I O p t i o n                                                         %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  CLIOption() Processes the given option using the given CLI Magick Wand.
%  The option arguments can be variable in number, though at this time no more
%  that two is actually used by any option (this may change). Excess options
%  are simply ignored.
%
%  If the cli_wand->command pointer is non-null, then it is assumed that the
%  option has already been search for up from the CommandOptions[] table in
%  "MagickCore/options.c" using  GetCommandOptionInfo().  If not set this
%  routine will do the lookup instead. The pointer is reset afterward.
%
%  This action allows the caller to lookup and pre-handle any 'special'
%  options, (such as implicit reads) before calling this general option
%  handler to deal with 'standard' command line options.
%
%  The format of the CLIOption method is:
%
%       void CLIOption(MagickCLI *cli_wand,const char *option, ...)
%
%  A description of each parameter follows:
%
%     o cli_wand: the main CLI Wand to use.
%
%     o option: The special option (with any switch char) to process
%
%     o args: any required arguments for an option (variable number)
%
%  Example Usage...
%
%    CLIoption(cli_wand,"-read","rose:");
%    CLIoption(cli_wand,"-virtual-pixel","transparent");
%    CLIoption(cli_wand,"-distort","SRT:","30");
%    CLIoption(cli_wand,"-write","rotated_rose.png");
%
*/
WandExport void CLIOption(MagickCLI *cli_wand,const char *option,...)
{
  const char    /* extracted option args from args */
    *arg1,
    *arg2;

  CommandOptionFlags
    option_type;

  assert(cli_wand != (MagickCLI *) NULL);
  assert(cli_wand->signature == MagickWandSignature);
  assert(cli_wand->wand.signature == MagickWandSignature);

  do { /* Break Code Block for error handling */

    /* get information about option */
    if ( cli_wand->command == (const OptionInfo *) NULL )
      cli_wand->command = GetCommandOptionInfo(option);
#if 0
      (void) FormatLocaleFile(stderr, "CLIOption \"%s\" matched \"%s\"\n",
            option, cli_wand->command->mnemonic );
#endif
    option_type=(CommandOptionFlags) cli_wand->command->flags;

    if ( option_type == UndefinedOptionFlag )
      CLIWandExceptionReturn(OptionFatalError,"UnrecognizedOption",option);

    assert( LocaleCompare(cli_wand->command->mnemonic,option) == 0 );

    /* deprecated options */
    if ( (option_type & DeprecateOptionFlag) != 0 )
      CLIWandExceptionBreak(OptionError,"DeprecatedOptionNoCode",option);

    /* options that this module does not handle */
    if ((option_type & (SpecialOptionFlag|GenesisOptionFlag)) != 0 )
      CLIWandExceptionBreak(OptionFatalError,"InvalidUseOfOption",option);

    /* Get argument strings from VarArgs
      How can you determine if enough arguments was supplied?
      What happens if not enough arguments were supplied?
    */
    { size_t
        count = (size_t) cli_wand->command->type;

      va_list
        operands;

      va_start(operands,option);

      arg1=arg2=NULL;
      if ( count >= 1 )
        arg1=(const char *) va_arg(operands, const char *);
      if ( count >= 2 )
        arg2=(const char *) va_arg(operands, const char *);

      va_end(operands);
#if 0
      (void) FormatLocaleFile(stderr,
        "CLIOption: \"%s\"  Count: %ld  Flags: %04x  Args: \"%s\" \"%s\"\n",
            option,(long) count,option_type,arg1,arg2);
#endif
    }

    /*
      Call the appropriate option handler
    */

    /* FUTURE: this is temporary - get 'settings' to handle distribution of
      settings to images attributes,proprieties,artifacts */
    if ( cli_wand->wand.images != (Image *) NULL )
      (void) SyncImagesSettings(cli_wand->wand.image_info,cli_wand->wand.images,
        cli_wand->wand.exception);

    if ( (option_type & SettingOptionFlags) != 0 ) {
      CLISettingOptionInfo(cli_wand, option, arg1, arg2);
      /*
        FUTURE: Sync Specific Settings into Image Properities (not global)
      */
    }

    /* Operators that do not need images - read, write, stack, clone */
    if ((option_type & NoImageOperatorFlag) != 0)
      CLINoImageOperator(cli_wand, option, arg1, arg2);

    /* FUTURE: The not a setting part below is a temporary hack due to
    * some options being both a Setting and a Simple operator.
    * Specifically -monitor, -depth, and  -colorspace */
    if ( cli_wand->wand.images == (Image *) NULL )
      if ( ((option_type & (SimpleOperatorFlag|ListOperatorFlag)) != 0 ) &&
          ((option_type & SettingOptionFlags) == 0 ))  /* temp hack */
        CLIWandExceptionBreak(OptionError,"NoImagesFound",option);

    /* Operators which loop of individual images, simply */
    if ( (option_type & SimpleOperatorFlag) != 0 &&
         cli_wand->wand.images != (Image *) NULL) /* temp hack */
      {
        ExceptionInfo *exception=AcquireExceptionInfo();
        (void) CLISimpleOperatorImages(cli_wand, option, arg1, arg2,exception);
        exception=DestroyExceptionInfo(exception);
      }

    /* Operators that work on the image list as a whole */
    if ( (option_type & ListOperatorFlag) != 0 )
      (void) CLIListOperatorImages(cli_wand, option, arg1, arg2);

DisableMSCWarning(4127)
  } while (0);  /* end Break code block */
RestoreMSCWarning

  cli_wand->command = (const OptionInfo *) NULL; /* prevent re-use later */
}
