operation.c revision a12d8ba3a815842797e8f9d92f52b83c3d1f07a0
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%          OOO   PPPP   EEEE  RRRR    AA   TTTTT  III   OOO   N   N           %
7%         O   O  P   P  E     R   R  A  A    T     I   O   O  NN  N           %
8%         O   O  PPPP   EEE   RRRR   AAAA    T     I   O   O  N N N           %
9%         O   O  P      E     R R    A  A    T     I   O   O  N  NN           %
10%          OOO   P      EEEE  R  RR  A  A    T    III   OOO   N   N           %
11%                                                                             %
12%                                                                             %
13%                         CLI Magick Option Methods                           %
14%                                                                             %
15%                              Dragon Computing                               %
16%                              Anthony Thyssen                                %
17%                               September 2011                                %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36% Apply the given options (settings, and simple, or sequence operations) to
37% the given image(s) according to the current "image_info", "draw_info", and
38% "quantize_info" settings, stored in a special CLI Image Wand.
39%
40% The final goal is to allow the execution in a strict one option at a time
41% manner that is needed for 'pipelining and file scripting' of options in
42% IMv7.
43%
44% Anthony Thyssen, September 2011
45*/
46
47/*
48  Include declarations.
49*/
50#include "MagickWand/studio.h"
51#include "MagickWand/MagickWand.h"
52#include "MagickWand/magick-wand-private.h"
53#include "MagickWand/wand.h"
54#include "MagickWand/wandcli.h"
55#include "MagickWand/wandcli-private.h"
56#include "MagickWand/operation.h"
57#include "MagickCore/monitor-private.h"
58#include "MagickCore/thread-private.h"
59#include "MagickCore/string-private.h"
60
61/*
62  Define declarations.
63*/
64#define USE_WAND_METHODS  0
65#define MAX_STACK_DEPTH  32
66#define UNDEFINED_COMPRESSION_QUALITY  0UL
67
68/* FUTURE: why is this default so specific? */
69#define DEFAULT_DISSIMILARITY_THRESHOLD "0.31830988618379067154"
70
71/*
72  Constant declaration. (temporary exports)
73*/
74static const char
75  BackgroundColor[] = "#fff",  /* white */
76  BorderColor[] = "#dfdfdf",  /* sRGB gray */
77  MatteColor[] = "#bdbdbd";  /* slightly darker gray */
78
79/* For Debugging Geometry Input */
80#define ReportGeometry(flags,info) \
81  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n", \
82       flags, info.rho, info.sigma, info.xi, info.psi )
83
84/*
85** Function to report on the progress of image operations
86*/
87static MagickBooleanType MonitorProgress(const char *text,
88  const MagickOffsetType offset,const MagickSizeType extent,
89  void *wand_unused(cli_wandent_data))
90{
91  char
92    message[MaxTextExtent],
93    tag[MaxTextExtent];
94
95  const char
96    *locale_message;
97
98  register char
99    *p;
100
101  if (extent < 2)
102    return(MagickTrue);
103  (void) CopyMagickMemory(tag,text,MaxTextExtent);
104  p=strrchr(tag,'/');
105  if (p != (char *) NULL)
106    *p='\0';
107  (void) FormatLocaleString(message,MaxTextExtent,"Monitor/%s",tag);
108  locale_message=GetLocaleMessage(message);
109  if (locale_message == message)
110    locale_message=tag;
111  if (p == (char *) NULL)
112    (void) FormatLocaleFile(stderr,"%s: %ld of %lu, %02ld%% complete\r",
113      locale_message,(long) offset,(unsigned long) extent,(long)
114      (100L*offset/(extent-1)));
115  else
116    (void) FormatLocaleFile(stderr,"%s[%s]: %ld of %lu, %02ld%% complete\r",
117      locale_message,p+1,(long) offset,(unsigned long) extent,(long)
118      (100L*offset/(extent-1)));
119  if (offset == (MagickOffsetType) (extent-1))
120    (void) FormatLocaleFile(stderr,"\n");
121  (void) fflush(stderr);
122  return(MagickTrue);
123}
124
125/*
126** GetImageCache() will read an image into a image cache if not already
127** present then return the image that is in the cache under that filename.
128*/
129static inline Image *GetImageCache(const ImageInfo *image_info,const char *path,
130  ExceptionInfo *exception)
131{
132  char
133    key[MaxTextExtent];
134
135  ExceptionInfo
136    *sans_exception;
137
138  Image
139    *image;
140
141  ImageInfo
142    *read_info;
143
144  (void) FormatLocaleString(key,MaxTextExtent,"cache:%s",path);
145  sans_exception=AcquireExceptionInfo();
146  image=(Image *) GetImageRegistry(ImageRegistryType,key,sans_exception);
147  sans_exception=DestroyExceptionInfo(sans_exception);
148  if (image != (Image *) NULL)
149    return(image);
150  read_info=CloneImageInfo(image_info);
151  (void) CopyMagickString(read_info->filename,path,MaxTextExtent);
152  image=ReadImage(read_info,exception);
153  read_info=DestroyImageInfo(read_info);
154  if (image != (Image *) NULL)
155    (void) SetImageRegistry(ImageRegistryType,key,image,exception);
156  return(image);
157}
158
159/*
160  SparseColorOption() parse the complex -sparse-color argument into an
161  an array of floating point values than call SparseColorImage().
162  Argument is a complex mix of floating-point pixel coodinates, and color
163  specifications (or direct floating point numbers).  The number of floats
164  needed to represent a color varies depending on teh current channel
165  setting.
166
167  This really should be in MagickCore, so that other API's can make use of it.
168*/
169static Image *SparseColorOption(const Image *image,
170  const SparseColorMethod method,const char *arguments,
171  ExceptionInfo *exception)
172{
173  char
174    token[MaxTextExtent];
175
176  const char
177    *p;
178
179  double
180    *sparse_arguments;
181
182  Image
183    *sparse_image;
184
185  PixelInfo
186    color;
187
188  MagickBooleanType
189    error;
190
191  register size_t
192    x;
193
194  size_t
195    number_arguments,
196    number_colors;
197
198  assert(image != (Image *) NULL);
199  assert(image->signature == MagickSignature);
200  if (IfMagickTrue(image->debug))
201    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
202  assert(exception != (ExceptionInfo *) NULL);
203  assert(exception->signature == MagickSignature);
204  /*
205    Limit channels according to image
206    add up number of values needed per color.
207  */
208  number_colors=0;
209  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
210    number_colors++;
211  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
212    number_colors++;
213  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
214    number_colors++;
215  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
216      (image->colorspace == CMYKColorspace))
217    number_colors++;
218  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
219      IfMagickTrue(image->matte))
220    number_colors++;
221
222  /*
223    Read string, to determine number of arguments needed,
224  */
225  p=arguments;
226  x=0;
227  while( *p != '\0' )
228  {
229    GetMagickToken(p,&p,token);
230    if ( token[0] == ',' ) continue;
231    if ( isalpha((int) token[0]) || token[0] == '#' )
232      x += number_colors;  /* color argument found */
233    else
234      x++;   /* floating point argument */
235  }
236  /* control points and color values */
237  error = IsMagickTrue( x % (2+number_colors) );
238  number_arguments=x;
239  if ( IfMagickTrue(error) ) {
240    (void) ThrowMagickException(exception,GetMagickModule(),
241               OptionError, "InvalidArgument", "'%s': %s", "sparse-color",
242               "Invalid number of Arguments");
243    return( (Image *)NULL);
244  }
245
246  /* Allocate and fill in the floating point arguments */
247  sparse_arguments=(double *) AcquireQuantumMemory(number_arguments,
248    sizeof(*sparse_arguments));
249  if (sparse_arguments == (double *) NULL) {
250    (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
251      "MemoryAllocationFailed","%s","SparseColorOption");
252    return( (Image *)NULL);
253  }
254  (void) ResetMagickMemory(sparse_arguments,0,number_arguments*
255    sizeof(*sparse_arguments));
256  p=arguments;
257  x=0;
258  while( *p != '\0' && x < number_arguments ) {
259    /* X coordinate */
260    token[0]=','; while ( token[0] == ',' ) GetMagickToken(p,&p,token);
261    if ( token[0] == '\0' ) break;
262    if ( isalpha((int) token[0]) || token[0] == '#' ) {
263      (void) ThrowMagickException(exception,GetMagickModule(),
264            OptionError, "InvalidArgument", "'%s': %s", "sparse-color",
265            "Color found, instead of X-coord");
266      error = MagickTrue;
267      break;
268    }
269    sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
270    /* Y coordinate */
271    token[0]=','; while ( token[0] == ',' ) GetMagickToken(p,&p,token);
272    if ( token[0] == '\0' ) break;
273    if ( isalpha((int) token[0]) || token[0] == '#' ) {
274      (void) ThrowMagickException(exception,GetMagickModule(),
275            OptionError, "InvalidArgument", "'%s': %s", "sparse-color",
276            "Color found, instead of Y-coord");
277      error = MagickTrue;
278      break;
279    }
280    sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
281    /* color name or function given in string argument */
282    token[0]=','; while ( token[0] == ',' ) GetMagickToken(p,&p,token);
283    if ( token[0] == '\0' ) break;
284    if ( isalpha((int) token[0]) || token[0] == '#' ) {
285      /* Color string given */
286      (void) QueryColorCompliance(token,AllCompliance,&color,
287                exception);
288      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
289        sparse_arguments[x++] = QuantumScale*color.red;
290      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
291        sparse_arguments[x++] = QuantumScale*color.green;
292      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
293        sparse_arguments[x++] = QuantumScale*color.blue;
294      if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
295          (image->colorspace == CMYKColorspace))
296        sparse_arguments[x++] = QuantumScale*color.black;
297      if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
298          IfMagickTrue(image->matte))
299        sparse_arguments[x++] = QuantumScale*color.alpha;
300    }
301    else {
302      /* Colors given as a set of floating point values - experimental */
303      /* NB: token contains the first floating point value to use! */
304      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
305        {
306        while ( token[0] == ',' ) GetMagickToken(p,&p,token);
307        if ( token[0] == '\0' || isalpha((int)token[0]) || token[0] == '#' )
308          break;
309        sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
310        token[0] = ','; /* used this token - get another */
311      }
312      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
313        {
314        while ( token[0] == ',' ) GetMagickToken(p,&p,token);
315        if ( token[0] == '\0' || isalpha((int)token[0]) || token[0] == '#' )
316          break;
317        sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
318        token[0] = ','; /* used this token - get another */
319      }
320      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
321        {
322        while ( token[0] == ',' ) GetMagickToken(p,&p,token);
323        if ( token[0] == '\0' || isalpha((int)token[0]) || token[0] == '#' )
324          break;
325        sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
326        token[0] = ','; /* used this token - get another */
327      }
328      if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
329          (image->colorspace == CMYKColorspace))
330        {
331        while ( token[0] == ',' ) GetMagickToken(p,&p,token);
332        if ( token[0] == '\0' || isalpha((int)token[0]) || token[0] == '#' )
333          break;
334        sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
335        token[0] = ','; /* used this token - get another */
336      }
337      if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
338          IfMagickTrue(image->matte))
339        {
340        while ( token[0] == ',' ) GetMagickToken(p,&p,token);
341        if ( token[0] == '\0' || isalpha((int)token[0]) || token[0] == '#' )
342          break;
343        sparse_arguments[x++]=StringToDouble(token,(char **) NULL);
344        token[0] = ','; /* used this token - get another */
345      }
346    }
347  }
348  if ( number_arguments != x && !error ) {
349    (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
350      "InvalidArgument","'%s': %s","sparse-color","Argument Parsing Error");
351    sparse_arguments=(double *) RelinquishMagickMemory(sparse_arguments);
352    return( (Image *)NULL);
353  }
354  if ( error )
355    return( (Image *)NULL);
356
357  /* Call the Sparse Color Interpolation function with the parsed arguments */
358  sparse_image=SparseColorImage(image,method,number_arguments,sparse_arguments,
359    exception);
360  sparse_arguments=(double *) RelinquishMagickMemory(sparse_arguments);
361  return( sparse_image );
362}
363
364/*
365%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
366%                                                                             %
367%                                                                             %
368%                                                                             %
369%   C L I S e t t i n g O p t i o n I n f o                                   %
370%                                                                             %
371%                                                                             %
372%                                                                             %
373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
374%
375%  CLISettingOptionInfo() applies a single settings option into a CLI wand
376%  holding the image_info, draw_info, quantize_info structures that will be
377%  used when processing the images.
378%
379%  These options do no require images to be present in the CLI wand for them
380%  to be able to be set, in which case they will generally be applied to image
381%  that are read in later
382%
383%  Options handled by this function are listed in CommandOptions[] of
384%  "option.c" that is one of "SettingOptionFlags" option flags.
385%
386%  The format of the CLISettingOptionInfo method is:
387%
388%    void CLISettingOptionInfo(MagickCLI *cli_wand,
389%               const char *option, const char *arg1, const char *arg2)
390%
391%  A description of each parameter follows:
392%
393%    o cli_wand: structure holding settings to be applied
394%
395%    o option: The option string to be set
396%
397%    o arg1, arg2: optional argument strings to the operation
398%        arg2 is currently only used by "-limit"
399%
400*/
401WandExport void CLISettingOptionInfo(MagickCLI *cli_wand,
402     const char *option,const char *arg1, const char *arg2)
403{
404  ssize_t
405    parse;     /* option argument parsing (string to value table lookup) */
406
407  assert(cli_wand != (MagickCLI *) NULL);
408  assert(cli_wand->signature == WandSignature);
409  assert(cli_wand->wand.signature == WandSignature);
410  if (IfMagickTrue(cli_wand->wand.debug))
411    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
412
413#define _image_info       (cli_wand->wand.image_info)
414#define _exception        (cli_wand->wand.exception)
415#define _draw_info        (cli_wand->draw_info)
416#define _quantize_info    (cli_wand->quantize_info)
417#define IfSetOption       (*option=='-')
418#define ArgBoolean        IsMagickTrue(IfSetOption)
419#define ArgBooleanNot     IsMagickFalse(IfSetOption)
420#define ArgBooleanString  (IfSetOption?"true":"false")
421#define ArgOption(def)    (IfSetOption?arg1:(const char *)(def))
422
423  switch (*(option+1))
424  {
425    case 'a':
426    {
427      if (LocaleCompare("adjoin",option+1) == 0)
428        {
429          _image_info->adjoin = ArgBoolean;
430          break;
431        }
432      if (LocaleCompare("affine",option+1) == 0)
433        {
434          CLIWandWarnReplaced("-draw 'affine ...'");
435          if (IfSetOption)
436            (void) ParseAffineGeometry(arg1,&_draw_info->affine,_exception);
437          else
438            GetAffineMatrix(&_draw_info->affine);
439          break;
440        }
441      if (LocaleCompare("antialias",option+1) == 0)
442        {
443          _image_info->antialias =
444            _draw_info->stroke_antialias =
445              _draw_info->text_antialias = ArgBoolean;
446          break;
447        }
448      if (LocaleCompare("attenuate",option+1) == 0)
449        {
450          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
451            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
452          (void) SetImageOption(_image_info,option+1,ArgOption("1.0"));
453          break;
454        }
455      if (LocaleCompare("authenticate",option+1) == 0)
456        {
457          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
458          break;
459        }
460      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
461    }
462    case 'b':
463    {
464      if (LocaleCompare("background",option+1) == 0)
465        {
466          /* FUTURE: both _image_info attribute & ImageOption in use!
467             _image_info only used directly for generating new images.
468             SyncImageSettings() used to set per-image attribute.
469
470             FUTURE: if _image_info->background_color is not set then
471             we should fall back to per-image background_color
472
473             At this time -background will 'wipe out' the per-image
474             background color!
475
476             Better error handling of QueryColorCompliance() needed.
477          */
478          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
479          (void) QueryColorCompliance(ArgOption(BackgroundColor),AllCompliance,
480             &_image_info->background_color,_exception);
481          break;
482        }
483      if (LocaleCompare("bias",option+1) == 0)
484        {
485          /* FUTURE: bias OBSOLETED, replaced by Artifact "convolve:bias"
486             as it is actually rarely used except in direct convolve operations
487             Usage outside a direct convolve operation is actally non-sensible!
488
489             SyncImageSettings() used to set per-image attribute.
490          */
491          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
492            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
493          (void) SetImageOption(_image_info,"convolve:bias",ArgOption(NULL));
494          break;
495        }
496      if (LocaleCompare("black-point-compensation",option+1) == 0)
497        {
498          /* Used as a image chromaticity setting
499             SyncImageSettings() used to set per-image attribute.
500          */
501          (void) SetImageOption(_image_info,option+1,ArgBooleanString);
502          break;
503        }
504      if (LocaleCompare("blue-primary",option+1) == 0)
505        {
506          /* Image chromaticity X,Y  NB: Y=X if Y not defined
507             Used by many coders including PNG
508             SyncImageSettings() used to set per-image attribute.
509          */
510          arg1=ArgOption("0.0");
511          if (IfMagickFalse(IsGeometry(arg1)))
512            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
513          (void) SetImageOption(_image_info,option+1,arg1);
514          break;
515        }
516      if (LocaleCompare("bordercolor",option+1) == 0)
517        {
518          /* FUTURE: both _image_info attribute & ImageOption in use!
519             SyncImageSettings() used to set per-image attribute.
520             Better error checking of QueryColorCompliance().
521          */
522          if (IfSetOption)
523            {
524              (void) SetImageOption(_image_info,option+1,arg1);
525              (void) QueryColorCompliance(arg1,AllCompliance,
526                  &_image_info->border_color,_exception);
527              (void) QueryColorCompliance(arg1,AllCompliance,
528                  &_draw_info->border_color,_exception);
529              break;
530            }
531          (void) DeleteImageOption(_image_info,option+1);
532          (void) QueryColorCompliance(BorderColor,AllCompliance,
533            &_image_info->border_color,_exception);
534          (void) QueryColorCompliance(BorderColor,AllCompliance,
535            &_draw_info->border_color,_exception);
536          break;
537        }
538      if (LocaleCompare("box",option+1) == 0)
539        {
540          CLIWandWarnReplaced("-undercolor");
541          CLISettingOptionInfo(cli_wand,"-undercolor",arg1, arg2);
542          break;
543        }
544      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
545    }
546    case 'c':
547    {
548      if (LocaleCompare("cache",option+1) == 0)
549        {
550          MagickSizeType
551            limit;
552
553          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
554            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
555          limit=MagickResourceInfinity;
556          if (LocaleCompare("unlimited",arg1) != 0)
557            limit=(MagickSizeType) SiPrefixToDoubleInterval(arg1,100.0);
558          (void) SetMagickResourceLimit(MemoryResource,limit);
559          (void) SetMagickResourceLimit(MapResource,2*limit);
560          break;
561        }
562      if (LocaleCompare("caption",option+1) == 0)
563        {
564          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
565          break;
566        }
567      if (LocaleCompare("channel",option+1) == 0)
568        {
569          arg1=ArgOption("default");
570          parse=ParseChannelOption(arg1);
571          if (parse < 0)
572            CLIWandExceptArgBreak(OptionError,"UnrecognizedChannelType",
573                 option,arg1);
574          _image_info->channel=(ChannelType) parse;
575          (void) SetImageOption(_image_info,option+1,arg1);
576          break;
577        }
578      if (LocaleCompare("colorspace",option+1) == 0)
579        {
580          /* Setting used for new images via AquireImage()
581             But also used as a SimpleImageOperator
582             Undefined colorspace means don't modify images on
583             read or as a operation */
584          parse = ParseCommandOption(MagickColorspaceOptions,MagickFalse,
585                        ArgOption("undefined"));
586          if (parse < 0)
587            CLIWandExceptArgBreak(OptionError,"UnrecognizedColorspace",
588                                    option,arg1);
589          _image_info->colorspace=(ColorspaceType) parse;
590          break;
591        }
592      if (LocaleCompare("comment",option+1) == 0)
593        {
594          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
595          break;
596        }
597      if (LocaleCompare("compose",option+1) == 0)
598        {
599          /* FUTURE: _image_info should be used,
600             SyncImageSettings() used to set per-image attribute. - REMOVE
601
602             This setting should NOT be used to set image 'compose'
603             "-layer" operators shoud use _image_info if defined otherwise
604             they should use a per-image compose setting.
605          */
606          parse = ParseCommandOption(MagickComposeOptions,MagickFalse,
607                          ArgOption("undefined"));
608          if (parse < 0)
609            CLIWandExceptArgBreak(OptionError,"UnrecognizedComposeOperator",
610                                      option,arg1);
611          _image_info->compose=(CompositeOperator) parse;
612          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
613          break;
614        }
615      if (LocaleCompare("compress",option+1) == 0)
616        {
617          /* FUTURE: What should be used?  _image_info  or ImageOption ???
618             The former is more efficent, but Crisy prefers the latter!
619             SyncImageSettings() used to set per-image attribute.
620
621             The coders appears to use _image_info, not Image_Option
622             however the image attribute (for save) is set from the
623             ImageOption!
624
625             Note that "undefined" is a different setting to "none".
626          */
627          parse = ParseCommandOption(MagickCompressOptions,MagickFalse,
628                     ArgOption("undefined"));
629          if (parse < 0)
630            CLIWandExceptArgBreak(OptionError,"UnrecognizedImageCompression",
631                                      option,arg1);
632          _image_info->compression=(CompressionType) parse;
633          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
634          break;
635        }
636      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
637    }
638    case 'd':
639    {
640      if (LocaleCompare("debug",option+1) == 0)
641        {
642          /* SyncImageSettings() used to set per-image attribute. */
643          arg1=ArgOption("none");
644          parse = ParseCommandOption(MagickLogEventOptions,MagickFalse,arg1);
645          if (parse < 0)
646            CLIWandExceptArgBreak(OptionError,"UnrecognizedEventType",
647                                      option,arg1);
648          (void) SetLogEventMask(arg1);
649          _image_info->debug=IsEventLogging();   /* extract logging*/
650          cli_wand->wand.debug=IsEventLogging();
651          break;
652        }
653      if (LocaleCompare("define",option+1) == 0)
654        {
655          if (LocaleNCompare(arg1,"registry:",9) == 0)
656            {
657              if (IfSetOption)
658                (void) DefineImageRegistry(StringRegistryType,arg1+9,_exception);
659              else
660                (void) DeleteImageRegistry(arg1+9);
661              break;
662            }
663          /* DefineImageOption() equals SetImageOption() but with '=' */
664          if (IfSetOption)
665            (void) DefineImageOption(_image_info,arg1);
666          else if (IsMagickFalse(DeleteImageOption(_image_info,arg1)))
667            CLIWandExceptArgBreak(OptionError,"NoSuchOption",option,arg1);
668          break;
669        }
670      if (LocaleCompare("delay",option+1) == 0)
671        {
672          /* Only used for new images via AcquireImage()
673             FUTURE: Option should also be used for "-morph" (color morphing)
674          */
675          arg1=ArgOption("0");
676          if (IfMagickFalse(IsGeometry(arg1)))
677            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
678          (void) SetImageOption(_image_info,option+1,arg1);
679          break;
680        }
681      if (LocaleCompare("density",option+1) == 0)
682        {
683          /* FUTURE: strings used in _image_info attr and _draw_info!
684             Basically as density can be in a XxY form!
685
686             SyncImageSettings() used to set per-image attribute.
687          */
688          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
689            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
690          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
691          (void) CloneString(&_image_info->density,ArgOption(NULL));
692          (void) CloneString(&_draw_info->density,_image_info->density);
693          break;
694        }
695      if (LocaleCompare("depth",option+1) == 0)
696        {
697          /* This is also a SimpleImageOperator! for 8->16 vaule trunc !!!!
698             SyncImageSettings() used to set per-image attribute.
699          */
700          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
701            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
702          _image_info->depth=IfSetOption?StringToUnsignedLong(arg1)
703                                       :MAGICKCORE_QUANTUM_DEPTH;
704          break;
705        }
706      if (LocaleCompare("direction",option+1) == 0)
707        {
708          /* Image Option is only used to set _draw_info */
709          arg1=ArgOption("undefined");
710          parse = ParseCommandOption(MagickDirectionOptions,MagickFalse,arg1);
711          if (parse < 0)
712            CLIWandExceptArgBreak(OptionError,"UnrecognizedDirectionType",
713                                      option,arg1);
714          _draw_info->direction=(DirectionType) parse;
715          (void) SetImageOption(_image_info,option+1,arg1);
716          break;
717        }
718      if (LocaleCompare("display",option+1) == 0)
719        {
720          (void) CloneString(&_image_info->server_name,ArgOption(NULL));
721          (void) CloneString(&_draw_info->server_name,_image_info->server_name);
722          break;
723        }
724      if (LocaleCompare("dispose",option+1) == 0)
725        {
726          /* only used in setting new images */
727          arg1=ArgOption("undefined");
728          parse = ParseCommandOption(MagickDisposeOptions,MagickFalse,arg1);
729          if (parse < 0)
730            CLIWandExceptArgBreak(OptionError,"UnrecognizedDisposeMethod",
731                                      option,arg1);
732          (void) SetImageOption(_image_info,option+1,ArgOption("undefined"));
733          break;
734        }
735      if (LocaleCompare("dissimilarity-threshold",option+1) == 0)
736        {
737          /* FUTURE: this is only used by CompareImages() which is used
738             only by the "compare" CLI program at this time.  */
739          arg1=ArgOption(DEFAULT_DISSIMILARITY_THRESHOLD);
740          if (IfMagickFalse(IsGeometry(arg1)))
741            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
742          (void) SetImageOption(_image_info,option+1,arg1);
743          break;
744        }
745      if (LocaleCompare("dither",option+1) == 0)
746        {
747          /* _image_info attr (on/off), _quantize_info attr (on/off)
748             but also ImageInfo and _quantize_info method!
749             FUTURE: merge the duality of the dithering options
750          */
751          _image_info->dither = _quantize_info->dither = ArgBoolean;
752          (void) SetImageOption(_image_info,option+1,ArgOption("none"));
753          _quantize_info->dither_method=(DitherMethod) ParseCommandOption(
754                    MagickDitherOptions,MagickFalse,ArgOption("none"));
755          if (_quantize_info->dither_method == NoDitherMethod)
756            _image_info->dither = _quantize_info->dither = MagickFalse;
757          break;
758        }
759      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
760    }
761    case 'e':
762    {
763      if (LocaleCompare("encoding",option+1) == 0)
764        {
765          (void) CloneString(&_draw_info->encoding,ArgOption("undefined"));
766          (void) SetImageOption(_image_info,option+1,_draw_info->encoding);
767          break;
768        }
769      if (LocaleCompare("endian",option+1) == 0)
770        {
771          /* Both _image_info attr and ImageInfo */
772          arg1 = ArgOption("undefined");
773          parse = ParseCommandOption(MagickEndianOptions,MagickFalse,arg1);
774          if (parse < 0)
775            CLIWandExceptArgBreak(OptionError,"UnrecognizedEndianType",
776                                      option,arg1);
777          /* FUTURE: check alloc/free of endian string!  - remove? */
778          _image_info->endian=(EndianType) (*arg1);
779          (void) SetImageOption(_image_info,option+1,arg1);
780          break;
781        }
782      if (LocaleCompare("extract",option+1) == 0)
783        {
784          (void) CloneString(&_image_info->extract,ArgOption(NULL));
785          break;
786        }
787      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
788    }
789    case 'f':
790    {
791      if (LocaleCompare("family",option+1) == 0)
792        {
793          (void) CloneString(&_draw_info->family,ArgOption(NULL));
794          break;
795        }
796      if (LocaleCompare("fill",option+1) == 0)
797        {
798          /* Set "fill" OR "fill-pattern" in _draw_info
799             The original fill color is preserved if a fill-pattern is given.
800             That way it does not effect other operations that directly using
801             the fill color and, can be retored using "+tile".
802          */
803          MagickBooleanType
804            status;
805
806          ExceptionInfo
807            *sans;
808
809          PixelInfo
810            color;
811
812          arg1 = ArgOption("none");  /* +fill turns it off! */
813          (void) SetImageOption(_image_info,option+1,arg1);
814          if (_draw_info->fill_pattern != (Image *) NULL)
815            _draw_info->fill_pattern=DestroyImage(_draw_info->fill_pattern);
816
817          /* is it a color or a image? -- ignore exceptions */
818          sans=AcquireExceptionInfo();
819          status=QueryColorCompliance(arg1,AllCompliance,&color,sans);
820          sans=DestroyExceptionInfo(sans);
821
822          if (IfMagickFalse(status))
823            _draw_info->fill_pattern=GetImageCache(_image_info,arg1,_exception);
824          else
825            _draw_info->fill=color;
826          break;
827        }
828      if (LocaleCompare("filter",option+1) == 0)
829        {
830          /* SyncImageSettings() used to set per-image attribute. */
831          arg1 = ArgOption("undefined");
832          parse = ParseCommandOption(MagickFilterOptions,MagickFalse,arg1);
833          if (parse < 0)
834            CLIWandExceptArgBreak(OptionError,"UnrecognizedImageFilter",
835                                      option,arg1);
836          (void) SetImageOption(_image_info,option+1,arg1);
837          break;
838        }
839      if (LocaleCompare("font",option+1) == 0)
840        {
841          (void) CloneString(&_draw_info->font,ArgOption(NULL));
842          (void) CloneString(&_image_info->font,_draw_info->font);
843          break;
844        }
845      if (LocaleCompare("format",option+1) == 0)
846        {
847          /* FUTURE: why the ping test, you could set ping after this! */
848          /*
849          register const char
850            *q;
851
852          for (q=strchr(arg1,'%'); q != (char *) NULL; q=strchr(q+1,'%'))
853            if (strchr("Agkrz@[#",*(q+1)) != (char *) NULL)
854              _image_info->ping=MagickFalse;
855          */
856          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
857          break;
858        }
859      if (LocaleCompare("fuzz",option+1) == 0)
860        {
861          /* Option used to set image fuzz! unless blank canvas (from color)
862             Image attribute used for color compare operations
863             SyncImageSettings() used to set per-image attribute.
864
865             FUTURE: Can't find anything else using _image_info->fuzz directly!
866                     remove direct sttribute from image_info
867          */
868          arg1=ArgOption("0");
869          if (IfMagickFalse(IsGeometry(arg1)))
870            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
871          _image_info->fuzz=StringToDoubleInterval(arg1,(double)
872                QuantumRange+1.0);
873          (void) SetImageOption(_image_info,option+1,arg1);
874          break;
875        }
876      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
877    }
878    case 'g':
879    {
880      if (LocaleCompare("gravity",option+1) == 0)
881        {
882          /* SyncImageSettings() used to set per-image attribute. */
883          arg1 = ArgOption("none");
884          parse = ParseCommandOption(MagickGravityOptions,MagickFalse,arg1);
885          if (parse < 0)
886            CLIWandExceptArgBreak(OptionError,"UnrecognizedGravityType",
887                                      option,arg1);
888          _draw_info->gravity=(GravityType) parse;
889          (void) SetImageOption(_image_info,option+1,arg1);
890          break;
891        }
892      if (LocaleCompare("green-primary",option+1) == 0)
893        {
894          /* Image chromaticity X,Y  NB: Y=X if Y not defined
895             SyncImageSettings() used to set per-image attribute.
896             Used directly by many coders
897          */
898          arg1=ArgOption("0.0");
899          if (IfMagickFalse(IsGeometry(arg1)))
900            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
901          (void) SetImageOption(_image_info,option+1,arg1);
902          break;
903        }
904      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
905    }
906    case 'h':
907    {
908      if (LocaleCompare("highlight-color",option+1) == 0)
909        {
910          /* FUTURE: this is only used by CompareImages() which is used
911             only by the "compare" CLI program at this time.  */
912          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
913          break;
914        }
915      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
916    }
917    case 'i':
918    {
919      if (LocaleCompare("intent",option+1) == 0)
920        {
921          /* Only used by coders: MIFF, MPC, BMP, PNG
922             and for image profile call to AcquireTransformThreadSet()
923             SyncImageSettings() used to set per-image attribute.
924          */
925          arg1 = ArgOption("indefined");
926          parse = ParseCommandOption(MagickIntentOptions,MagickFalse,arg1);
927          if (parse < 0)
928            CLIWandExceptArgBreak(OptionError,"UnrecognizedIntentType",
929                                      option,arg1);
930          (void) SetImageOption(_image_info,option+1,arg1);
931          break;
932        }
933      if (LocaleCompare("interlace",option+1) == 0)
934        {
935          /* _image_info is directly used by coders (so why an image setting?)
936             SyncImageSettings() used to set per-image attribute.
937          */
938          arg1 = ArgOption("undefined");
939          parse = ParseCommandOption(MagickInterlaceOptions,MagickFalse,arg1);
940          if (parse < 0)
941            CLIWandExceptArgBreak(OptionError,"UnrecognizedInterlaceType",
942                                      option,arg1);
943          _image_info->interlace=(InterlaceType) parse;
944          (void) SetImageOption(_image_info,option+1,arg1);
945          break;
946        }
947      if (LocaleCompare("interline-spacing",option+1) == 0)
948        {
949          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
950            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
951          (void) SetImageOption(_image_info,option+1, ArgOption(NULL));
952          _draw_info->interline_spacing=StringToDouble(ArgOption("0"),
953               (char **) NULL);
954          break;
955        }
956      if (LocaleCompare("interpolate",option+1) == 0)
957        {
958          /* SyncImageSettings() used to set per-image attribute. */
959          arg1 = ArgOption("undefined");
960          parse = ParseCommandOption(MagickInterpolateOptions,MagickFalse,arg1);
961          if (parse < 0)
962            CLIWandExceptArgBreak(OptionError,"UnrecognizedInterpolateMethod",
963                                      option,arg1);
964          (void) SetImageOption(_image_info,option+1,arg1);
965          break;
966        }
967      if (LocaleCompare("interword-spacing",option+1) == 0)
968        {
969          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
970            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
971          (void) SetImageOption(_image_info,option+1, ArgOption(NULL));
972          _draw_info->interword_spacing=StringToDouble(ArgOption("0"),(char **) NULL);
973          break;
974        }
975      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
976    }
977    case 'k':
978    {
979      if (LocaleCompare("kerning",option+1) == 0)
980        {
981          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
982            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
983          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
984          _draw_info->kerning=StringToDouble(ArgOption("0"),(char **) NULL);
985          break;
986        }
987      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
988    }
989    case 'l':
990    {
991      if (LocaleCompare("label",option+1) == 0)
992        {
993          /* only used for new images - not in SyncImageOptions() */
994          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
995          break;
996        }
997      if (LocaleCompare("limit",option+1) == 0)
998        {
999          MagickSizeType
1000            limit;
1001
1002          limit=MagickResourceInfinity;
1003          parse= ParseCommandOption(MagickResourceOptions,MagickFalse,arg1);
1004          if ( parse < 0 )
1005            CLIWandExceptArgBreak(OptionError,"UnrecognizedResourceType",
1006                option,arg1);
1007          if (LocaleCompare("unlimited",arg2) != 0)
1008            limit=(MagickSizeType) SiPrefixToDoubleInterval(arg2,100.0);
1009          (void) SetMagickResourceLimit((ResourceType)parse,limit);
1010          break;
1011        }
1012      if (LocaleCompare("log",option+1) == 0)
1013        {
1014          if (IfSetOption) {
1015            if ((strchr(arg1,'%') == (char *) NULL))
1016              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1017            (void) SetLogFormat(arg1);
1018          }
1019          break;
1020        }
1021      if (LocaleCompare("lowlight-color",option+1) == 0)
1022        {
1023          /* FUTURE: this is only used by CompareImages() which is used
1024             only by the "compare" CLI program at this time.  */
1025          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1026          break;
1027        }
1028      if (LocaleCompare("loop",option+1) == 0)
1029        {
1030          /* SyncImageSettings() used to set per-image attribute. */
1031          arg1=ArgOption("0");
1032          if (IfMagickFalse(IsGeometry(arg1)))
1033            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1034          (void) SetImageOption(_image_info,option+1,arg1);
1035          break;
1036        }
1037      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1038    }
1039    case 'm':
1040    {
1041      if (LocaleCompare("mattecolor",option+1) == 0)
1042        {
1043          /* SyncImageSettings() used to set per-image attribute. */
1044          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1045          (void) QueryColorCompliance(ArgOption(MatteColor),AllCompliance,
1046             &_image_info->matte_color,_exception);
1047          break;
1048        }
1049      if (LocaleCompare("metric",option+1) == 0)
1050        {
1051          /* FUTURE: this is only used by CompareImages() which is used
1052             only by the "compare" CLI program at this time.  */
1053          parse=ParseCommandOption(MagickMetricOptions,MagickFalse,arg1);
1054          if ( parse < 0 )
1055            CLIWandExceptArgBreak(OptionError,"UnrecognizedMetricType",
1056                option,arg1);
1057          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1058          break;
1059        }
1060      if (LocaleCompare("monitor",option+1) == 0)
1061        {
1062          (void) SetImageInfoProgressMonitor(_image_info, IfSetOption?
1063                MonitorProgress: (MagickProgressMonitor) NULL, (void *) NULL);
1064          break;
1065        }
1066      if (LocaleCompare("monochrome",option+1) == 0)
1067        {
1068          /* Setting (used by some input coders!) -- why?
1069             Warning: This is also Special '-type' SimpleOperator
1070          */
1071          _image_info->monochrome= ArgBoolean;
1072          break;
1073        }
1074      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1075    }
1076    case 'o':
1077    {
1078      if (LocaleCompare("orient",option+1) == 0)
1079        {
1080          /* Is not used when defining for new images.
1081             This makes it more of a 'operation' than a setting
1082             FUTURE: make set meta-data operator instead.
1083             SyncImageSettings() used to set per-image attribute.
1084          */
1085          parse=ParseCommandOption(MagickOrientationOptions,MagickFalse,
1086               ArgOption("undefined"));
1087          if (parse < 0)
1088            CLIWandExceptArgBreak(OptionError,"UnrecognizedImageOrientation",
1089                                      option,arg1);
1090          _image_info->orientation=(OrientationType)parse;
1091          (void) SetImageOption(_image_info,option+1, ArgOption(NULL));
1092          break;
1093        }
1094      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1095    }
1096    case 'p':
1097    {
1098      if (LocaleCompare("page",option+1) == 0)
1099        {
1100          /* Only used for new images and image generators.
1101             SyncImageSettings() used to set per-image attribute. ?????
1102             That last is WRONG!!!!
1103             FUTURE: adjust named 'page' sizes according density
1104          */
1105          char
1106            *canonical_page,
1107            page[MaxTextExtent];
1108
1109          const char
1110            *image_option;
1111
1112          MagickStatusType
1113            flags;
1114
1115          RectangleInfo
1116            geometry;
1117
1118          if (!IfSetOption)
1119            {
1120              (void) DeleteImageOption(_image_info,option+1);
1121              (void) CloneString(&_image_info->page,(char *) NULL);
1122              break;
1123            }
1124          (void) ResetMagickMemory(&geometry,0,sizeof(geometry));
1125          image_option=GetImageOption(_image_info,"page");
1126          if (image_option != (const char *) NULL)
1127            flags=ParseAbsoluteGeometry(image_option,&geometry);
1128          canonical_page=GetPageGeometry(arg1);
1129          flags=ParseAbsoluteGeometry(canonical_page,&geometry);
1130          canonical_page=DestroyString(canonical_page);
1131          (void) FormatLocaleString(page,MaxTextExtent,"%lux%lu",
1132            (unsigned long) geometry.width,(unsigned long) geometry.height);
1133          if (((flags & XValue) != 0) || ((flags & YValue) != 0))
1134            (void) FormatLocaleString(page,MaxTextExtent,"%lux%lu%+ld%+ld",
1135              (unsigned long) geometry.width,(unsigned long) geometry.height,
1136              (long) geometry.x,(long) geometry.y);
1137          (void) SetImageOption(_image_info,option+1,page);
1138          (void) CloneString(&_image_info->page,page);
1139          break;
1140        }
1141      if (LocaleCompare("ping",option+1) == 0)
1142        {
1143          _image_info->ping = ArgBoolean;
1144          break;
1145        }
1146      if (LocaleCompare("pointsize",option+1) == 0)
1147        {
1148          if (IfSetOption) {
1149            if (IfMagickFalse(IsGeometry(arg1)))
1150              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1151            _image_info->pointsize =
1152            _draw_info->pointsize =
1153              StringToDouble(arg1,(char **) NULL);
1154          }
1155          else {
1156            _image_info->pointsize=0.0; /* unset pointsize */
1157            _draw_info->pointsize=12.0;
1158          }
1159          break;
1160        }
1161      if (LocaleCompare("precision",option+1) == 0)
1162        {
1163          arg1=ArgOption("-1");
1164          if (IfMagickFalse(IsGeometry(arg1)))
1165            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1166          (void) SetMagickPrecision(StringToInteger(arg1));
1167          break;
1168        }
1169      /* FUTURE: Only the 'preview' coder appears to use this
1170       * DEPRECIATE the coder?  Leaving only the 'preview' operator.
1171      if (LocaleCompare("preview",option+1) == 0)
1172        {
1173          _image_info->preview_type=UndefinedPreview;
1174          if (IfSetOption)
1175            _image_info->preview_type=(PreviewType) ParseCommandOption(
1176                MagickPreviewOptions,MagickFalse,arg1);
1177          break;
1178        }
1179      */
1180      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1181    }
1182    case 'q':
1183    {
1184      if (LocaleCompare("quality",option+1) == 0)
1185        {
1186          if (IfMagickFalse(IsGeometry(arg1)))
1187            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1188          _image_info->quality= IfSetOption ? StringToUnsignedLong(arg1)
1189                                            : UNDEFINED_COMPRESSION_QUALITY;
1190          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1191          break;
1192        }
1193      if (LocaleCompare("quantize",option+1) == 0)
1194        {
1195          /* Just a set direct in _quantize_info */
1196          arg1=ArgOption("undefined");
1197          parse=ParseCommandOption(MagickColorspaceOptions,MagickFalse,arg1);
1198          if (parse < 0)
1199            CLIWandExceptArgBreak(OptionError,"UnrecognizedColorspace",
1200                 option,arg1);
1201          _quantize_info->colorspace=(ColorspaceType)parse;
1202          break;
1203        }
1204      if (LocaleCompare("quiet",option+1) == 0)
1205        {
1206          /* FUTURE: if two -quiet is performed you can not do +quiet!
1207             This needs to be checked over thoughly.
1208          */
1209          static WarningHandler
1210            warning_handler = (WarningHandler) NULL;
1211
1212          WarningHandler
1213            tmp = SetWarningHandler((WarningHandler) NULL);
1214
1215          if ( tmp != (WarningHandler) NULL)
1216            warning_handler = tmp; /* remember the old handler */
1217          if (!IfSetOption)        /* set the old handler */
1218            warning_handler=SetWarningHandler(warning_handler);
1219          break;
1220        }
1221      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1222    }
1223    case 'r':
1224    {
1225      if (LocaleCompare("red-primary",option+1) == 0)
1226        {
1227          /* Image chromaticity X,Y  NB: Y=X if Y not defined
1228             Used by many coders
1229             SyncImageSettings() used to set per-image attribute.
1230          */
1231          arg1=ArgOption("0.0");
1232          if (IfMagickFalse(IsGeometry(arg1)))
1233            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1234          (void) SetImageOption(_image_info,option+1,arg1);
1235          break;
1236        }
1237      if (LocaleCompare("regard-warnings",option+1) == 0)
1238        /* FUTURE: to be replaced by a 'fatal-level' type setting */
1239        break;
1240      if (LocaleCompare("render",option+1) == 0)
1241        {
1242          /* _draw_info only setting */
1243          _draw_info->render= ArgBooleanNot;
1244          break;
1245        }
1246      if (LocaleCompare("respect-parenthesis",option+1) == 0)
1247        {
1248          /* link image and setting stacks - option is itself saved on stack! */
1249          (void) SetImageOption(_image_info,option+1,ArgBooleanString);
1250          break;
1251        }
1252      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1253    }
1254    case 's':
1255    {
1256      if (LocaleCompare("sampling-factor",option+1) == 0)
1257        {
1258          /* FUTURE: should be converted to jpeg:sampling_factor */
1259          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
1260            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1261          (void) CloneString(&_image_info->sampling_factor,ArgOption(NULL));
1262          break;
1263        }
1264      if (LocaleCompare("scene",option+1) == 0)
1265        {
1266          /* SyncImageSettings() used to set this as a per-image attribute.
1267             What ??? Why ????
1268          */
1269          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
1270            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1271          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1272          _image_info->scene=StringToUnsignedLong(ArgOption("0"));
1273          break;
1274        }
1275      if (LocaleCompare("seed",option+1) == 0)
1276        {
1277          if (IfMagickFalse(IsGeometry(arg1)))
1278            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1279          SetRandomSecretKey(
1280               IfSetOption ? (size_t) StringToUnsignedLong(arg1)
1281                           : (size_t) time((time_t *) NULL) );
1282          break;
1283        }
1284      if (LocaleCompare("size",option+1) == 0)
1285        {
1286          /* FUTURE: string in _image_info -- convert to Option ???
1287             Look at the special handling for "size" in SetImageOption()
1288           */
1289          (void) CloneString(&_image_info->size,ArgOption(NULL));
1290          break;
1291        }
1292      if (LocaleCompare("stretch",option+1) == 0)
1293        {
1294          arg1=ArgOption("undefined");
1295          parse = ParseCommandOption(MagickStretchOptions,MagickFalse,arg1);
1296          if (parse < 0)
1297            CLIWandExceptArgBreak(OptionError,"UnrecognizedStretchType",
1298                 option,arg1);
1299          _draw_info->stretch=(StretchType) parse;
1300          break;
1301        }
1302      if (LocaleCompare("stroke",option+1) == 0)
1303        {
1304          /* set stroke color OR stroke-pattern
1305             UPDATE: ensure stroke color is not destroyed is a pattern
1306             is given. Just in case the color is also used for other purposes.
1307           */
1308          MagickBooleanType
1309            status;
1310
1311          ExceptionInfo
1312            *sans;
1313
1314          PixelInfo
1315            color;
1316
1317          arg1 = ArgOption("none");  /* +fill turns it off! */
1318          (void) SetImageOption(_image_info,option+1,arg1);
1319          if (_draw_info->stroke_pattern != (Image *) NULL)
1320            _draw_info->stroke_pattern=DestroyImage(_draw_info->stroke_pattern);
1321
1322          /* is it a color or a image? -- ignore exceptions */
1323          sans=AcquireExceptionInfo();
1324          status=QueryColorCompliance(arg1,AllCompliance,&color,sans);
1325          sans=DestroyExceptionInfo(sans);
1326
1327          if (IfMagickFalse(status))
1328            _draw_info->stroke_pattern=GetImageCache(_image_info,arg1,_exception);
1329          else
1330            _draw_info->stroke=color;
1331          break;
1332        }
1333      if (LocaleCompare("strokewidth",option+1) == 0)
1334        {
1335          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
1336            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1337          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1338          _draw_info->stroke_width=StringToDouble(ArgOption("1.0"),
1339               (char **) NULL);
1340          break;
1341        }
1342      if (LocaleCompare("style",option+1) == 0)
1343        {
1344          arg1=ArgOption("undefined");
1345          parse = ParseCommandOption(MagickStyleOptions,MagickFalse,arg1);
1346          if (parse < 0)
1347            CLIWandExceptArgBreak(OptionError,"UnrecognizedStyleType",
1348                 option,arg1);
1349          _draw_info->style=(StyleType) parse;
1350          break;
1351        }
1352#if 0
1353      if (LocaleCompare("subimage-search",option+1) == 0)
1354        {
1355        /* FUTURE: this is only used by CompareImages() which is used
1356            only by the "compare" CLI program at this time.  */
1357          (void) SetImageOption(_image_info,option+1,ArgBooleanString);
1358          break;
1359        }
1360#endif
1361      if (LocaleCompare("synchronize",option+1) == 0)
1362        {
1363          /* FUTURE: syncronize to storage - but what does that mean? */
1364          _image_info->synchronize = ArgBoolean;
1365          break;
1366        }
1367      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1368    }
1369    case 't':
1370    {
1371      if (LocaleCompare("taint",option+1) == 0)
1372        {
1373          /* SyncImageSettings() used to set per-image attribute. */
1374          (void) SetImageOption(_image_info,option+1,ArgBooleanString);
1375          break;
1376        }
1377      if (LocaleCompare("texture",option+1) == 0)
1378        {
1379          /* FUTURE: move _image_info string to option splay-tree
1380             Other than "montage" what uses "texture" ????
1381          */
1382          (void) CloneString(&_image_info->texture,ArgOption(NULL));
1383          break;
1384        }
1385      if (LocaleCompare("tile",option+1) == 0)
1386        {
1387          _draw_info->fill_pattern=IfSetOption
1388                                 ?GetImageCache(_image_info,arg1,_exception)
1389                                 :DestroyImage(_draw_info->fill_pattern);
1390          break;
1391        }
1392      if (LocaleCompare("tile-offset",option+1) == 0)
1393        {
1394          /* SyncImageSettings() used to set per-image attribute. ??? */
1395          arg1=ArgOption("0");
1396          if (IfMagickFalse(IsGeometry(arg1)))
1397            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1398          (void) SetImageOption(_image_info,option+1,arg1);
1399          break;
1400        }
1401      if (LocaleCompare("transparent-color",option+1) == 0)
1402        {
1403          /* FUTURE: both _image_info attribute & ImageOption in use!
1404             _image_info only used for generating new images.
1405             SyncImageSettings() used to set per-image attribute.
1406
1407             Note that +transparent-color, means fall-back to image
1408             attribute so ImageOption is deleted, not set to a default.
1409          */
1410          if (IfSetOption && IfMagickFalse(IsGeometry(arg1)))
1411            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1412          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1413          (void) QueryColorCompliance(ArgOption("none"),AllCompliance,
1414              &_image_info->transparent_color,_exception);
1415          break;
1416        }
1417      if (LocaleCompare("treedepth",option+1) == 0)
1418        {
1419          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1420          _quantize_info->tree_depth=StringToUnsignedLong(ArgOption("0"));
1421          break;
1422        }
1423      if (LocaleCompare("type",option+1) == 0)
1424        {
1425          /* SyncImageSettings() used to set per-image attribute. */
1426          parse=ParseCommandOption(MagickTypeOptions,MagickFalse,
1427               ArgOption("undefined"));
1428          if (parse < 0)
1429            CLIWandExceptArgBreak(OptionError,"UnrecognizedImageType",
1430                 option,arg1);
1431          _image_info->type=(ImageType) parse;
1432          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1433          break;
1434        }
1435      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1436    }
1437    case 'u':
1438    {
1439      if (LocaleCompare("undercolor",option+1) == 0)
1440        {
1441          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1442          (void) QueryColorCompliance(ArgOption("none"),AllCompliance,
1443               &_draw_info->undercolor,_exception);
1444          break;
1445        }
1446      if (LocaleCompare("units",option+1) == 0)
1447        {
1448          /* SyncImageSettings() used to set per-image attribute.
1449             Should this effect _draw_info X and Y resolution?
1450             FUTURE: this probably should be part of the density setting
1451          */
1452          parse=ParseCommandOption(MagickResolutionOptions,MagickFalse,
1453               ArgOption("undefined"));
1454          if (parse < 0)
1455            CLIWandExceptArgBreak(OptionError,"UnrecognizedUnitsType",
1456                 option,arg1);
1457          _image_info->units=(ResolutionType) parse;
1458          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1459          break;
1460        }
1461      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1462    }
1463    case 'v':
1464    {
1465      if (LocaleCompare("verbose",option+1) == 0)
1466        {
1467          /* FUTURE: Remember all options become image artifacts
1468             _image_info->verbose is only used by coders.
1469          */
1470          (void) SetImageOption(_image_info,option+1,ArgBooleanString);
1471          _image_info->verbose= ArgBoolean;
1472          _image_info->ping=MagickFalse; /* verbose can't be a ping */
1473          break;
1474        }
1475      if (LocaleCompare("view",option+1) == 0)
1476        {
1477          /* FUTURE: Convert from _image_info to ImageOption
1478             Only used by coder FPX
1479             And it only tests existance, not its content!
1480          */
1481          (void) CloneString(&_image_info->view,ArgOption(NULL));
1482          break;
1483        }
1484      if (LocaleCompare("virtual-pixel",option+1) == 0)
1485        {
1486          /* SyncImageSettings() used to set per-image attribute.
1487             This is VERY deep in the image caching structure.
1488          */
1489          parse=ParseCommandOption(MagickVirtualPixelOptions,MagickFalse,
1490               ArgOption("undefined"));
1491          if (parse < 0)
1492            CLIWandExceptArgBreak(OptionError,"UnrecognizedVirtualPixelMethod",
1493                 option,arg1);
1494          (void) SetImageOption(_image_info,option+1,ArgOption(NULL));
1495          break;
1496        }
1497      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1498    }
1499    case 'w':
1500    {
1501      if (LocaleCompare("weight",option+1) == 0)
1502        {
1503          /* Just what does using a font 'weight' do ???
1504             There is no "-list weight" output (reference manual says there is)
1505          */
1506          arg1=ArgOption("all");
1507          _draw_info->weight=StringToUnsignedLong(arg1);
1508          if (LocaleCompare(arg1,"all") == 0)
1509            _draw_info->weight=0;
1510          if (LocaleCompare(arg1,"bold") == 0)
1511            _draw_info->weight=700;
1512          if (LocaleCompare(arg1,"bolder") == 0)
1513            if (_draw_info->weight <= 800)
1514              _draw_info->weight+=100;
1515          if (LocaleCompare(arg1,"lighter") == 0)
1516            if (_draw_info->weight >= 100)
1517              _draw_info->weight-=100;
1518          if (LocaleCompare(arg1,"normal") == 0)
1519            _draw_info->weight=400;
1520          break;
1521        }
1522      if (LocaleCompare("white-point",option+1) == 0)
1523        {
1524          /* Used as a image chromaticity setting
1525             SyncImageSettings() used to set per-image attribute.
1526          */
1527          arg1=ArgOption("0.0");
1528          if (IfMagickFalse(IsGeometry(arg1)))
1529            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1530          (void) SetImageOption(_image_info,option+1,arg1);
1531          break;
1532        }
1533      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1534    }
1535    default:
1536      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1537  }
1538
1539#undef _image_info
1540#undef _exception
1541#undef _draw_info
1542#undef _quantize_info
1543#undef IfSetOption
1544#undef ArgBoolean
1545#undef ArgBooleanNot
1546#undef ArgBooleanString
1547#undef ArgOption
1548
1549  return;
1550}
1551
1552/*
1553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1554%                                                                             %
1555%                                                                             %
1556%                                                                             %
1557+     C L I S i m p l e O p e r a t o r I m a g e s                           %
1558%                                                                             %
1559%                                                                             %
1560%                                                                             %
1561%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1562%
1563%  WandSimpleOperatorImages() applys one simple image operation given to all
1564%  the images in the CLI wand,  with the settings that was previously saved in
1565%  the CLI wand.
1566%
1567%  It is assumed that any per-image settings are up-to-date with respect to
1568%  extra settings that were already saved in the wand.
1569%
1570%  The format of the WandSimpleOperatorImage method is:
1571%
1572%    void CLISimpleOperatorImages(MagickCLI *cli_wand,
1573%        const char *option, const char *arg1, const char *arg2)
1574%
1575%  A description of each parameter follows:
1576%
1577%    o cli_wand: structure holding settings and images to be operated on
1578%
1579%    o option:  The option string for the operation
1580%
1581%    o arg1, arg2: optional argument strings to the operation
1582%
1583*/
1584
1585/*
1586  Internal subrountine to apply one simple image operation to the current
1587  image pointed to by the CLI wand.
1588
1589  The image in the list may be modified in three different ways...
1590    * directly modified (EG: -negate, -gamma, -level, -annotate, -draw),
1591    * replaced by a new image (EG: -spread, -resize, -rotate, -morphology)
1592    * one image replace by a list of images (-separate and -crop only!)
1593
1594  In each case the result replaces the single original image in the list, as
1595  well as the pointer to the modified image (last image added if replaced by a
1596  list of images) is returned.
1597
1598  As the image pointed to may be replaced, the first image in the list may
1599  also change.  GetFirstImageInList() should be used by caller if they wish
1600  return the Image pointer to the first image in list.
1601*/
1602static void CLISimpleOperatorImage(MagickCLI *cli_wand,
1603  const char *option, const char *arg1, const char *arg2)
1604{
1605  Image *
1606    new_image;
1607
1608  GeometryInfo
1609    geometry_info;
1610
1611  RectangleInfo
1612    geometry;
1613
1614  MagickStatusType
1615    flags;
1616
1617  ssize_t
1618    parse;
1619
1620#define _image_info     (cli_wand->wand.image_info)
1621#define _image          (cli_wand->wand.images)
1622#define _exception      (cli_wand->wand.exception)
1623#define _draw_info      (cli_wand->draw_info)
1624#define _quantize_info  (cli_wand->quantize_info)
1625#define IfNormalOp      (*option=='-')
1626#define IfPlusOp        (*option!='-')
1627#define normal_op       IsMagickTrue(IfNormalOp)
1628#define plus_alt_op     IsMagickFalse(IfNormalOp)
1629
1630  assert(cli_wand != (MagickCLI *) NULL);
1631  assert(cli_wand->signature == WandSignature);
1632  assert(cli_wand->wand.signature == WandSignature);
1633  assert(_image != (Image *) NULL);             /* an image must be present */
1634  if (IfMagickTrue(cli_wand->wand.debug))
1635    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
1636
1637  SetGeometryInfo(&geometry_info);
1638
1639  new_image = (Image *)NULL; /* the replacement image, if not null at end */
1640
1641  /* FUTURE: We may need somthing a little more optimized than this!
1642     Perhaps, do the 'sync' if 'settings tainted' before next operator.
1643  */
1644  switch (*(option+1))
1645  {
1646    case 'a':
1647    {
1648      if (LocaleCompare("adaptive-blur",option+1) == 0)
1649        {
1650          flags=ParseGeometry(arg1,&geometry_info);
1651          if ((flags & (RhoValue|SigmaValue)) == 0)
1652            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1653          if ((flags & SigmaValue) == 0)
1654            geometry_info.sigma=1.0;
1655          new_image=AdaptiveBlurImage(_image,geometry_info.rho,
1656            geometry_info.sigma,_exception);
1657          break;
1658        }
1659      if (LocaleCompare("adaptive-resize",option+1) == 0)
1660        {
1661          /* FUTURE: Roll into a resize special operator */
1662          if (IfMagickFalse(IsGeometry(arg1)))
1663            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1664          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
1665          new_image=AdaptiveResizeImage(_image,geometry.width,geometry.height,
1666            _exception);
1667          break;
1668        }
1669      if (LocaleCompare("adaptive-sharpen",option+1) == 0)
1670        {
1671          flags=ParseGeometry(arg1,&geometry_info);
1672          if ((flags & (RhoValue|SigmaValue)) == 0)
1673            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1674          if ((flags & SigmaValue) == 0)
1675            geometry_info.sigma=1.0;
1676          new_image=AdaptiveSharpenImage(_image,geometry_info.rho,
1677            geometry_info.sigma,_exception);
1678          break;
1679        }
1680      if (LocaleCompare("alpha",option+1) == 0)
1681        {
1682          parse=ParseCommandOption(MagickAlphaOptions,MagickFalse,arg1);
1683          if (parse < 0)
1684            CLIWandExceptArgBreak(OptionError,"UnrecognizedAlphaChannelType",
1685                 option,arg1);
1686          (void) SetImageAlphaChannel(_image,(AlphaChannelType)parse,
1687               _exception);
1688          break;
1689        }
1690      if (LocaleCompare("annotate",option+1) == 0)
1691        {
1692          char
1693            *text,
1694            geometry[MaxTextExtent];
1695
1696          SetGeometryInfo(&geometry_info);
1697          flags=ParseGeometry(arg1,&geometry_info);
1698          if ((flags & RhoValue) == 0)
1699            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1700          if ((flags & SigmaValue) == 0)
1701            geometry_info.sigma=geometry_info.rho;
1702          text=InterpretImageProperties(_image_info,_image,arg2,
1703            _exception);
1704          if (text == (char *) NULL)
1705            break;
1706          (void) CloneString(&_draw_info->text,text);
1707          text=DestroyString(text);
1708          (void) FormatLocaleString(geometry,MaxTextExtent,"%+f%+f",
1709            geometry_info.xi,geometry_info.psi);
1710          (void) CloneString(&_draw_info->geometry,geometry);
1711          _draw_info->affine.sx=cos(DegreesToRadians(
1712            fmod(geometry_info.rho,360.0)));
1713          _draw_info->affine.rx=sin(DegreesToRadians(
1714            fmod(geometry_info.rho,360.0)));
1715          _draw_info->affine.ry=(-sin(DegreesToRadians(
1716            fmod(geometry_info.sigma,360.0))));
1717          _draw_info->affine.sy=cos(DegreesToRadians(
1718            fmod(geometry_info.sigma,360.0)));
1719          (void) AnnotateImage(_image,_draw_info,_exception);
1720          GetAffineMatrix(&_draw_info->affine);
1721          break;
1722        }
1723      if (LocaleCompare("auto-gamma",option+1) == 0)
1724        {
1725          (void) AutoGammaImage(_image,_exception);
1726          break;
1727        }
1728      if (LocaleCompare("auto-level",option+1) == 0)
1729        {
1730          (void) AutoLevelImage(_image,_exception);
1731          break;
1732        }
1733      if (LocaleCompare("auto-orient",option+1) == 0)
1734        {
1735          /* This should probably be a MagickCore function */
1736          switch (_image->orientation)
1737          {
1738            case TopRightOrientation:
1739            {
1740              new_image=FlopImage(_image,_exception);
1741              break;
1742            }
1743            case BottomRightOrientation:
1744            {
1745              new_image=RotateImage(_image,180.0,_exception);
1746              break;
1747            }
1748            case BottomLeftOrientation:
1749            {
1750              new_image=FlipImage(_image,_exception);
1751              break;
1752            }
1753            case LeftTopOrientation:
1754            {
1755              new_image=TransposeImage(_image,_exception);
1756              break;
1757            }
1758            case RightTopOrientation:
1759            {
1760              new_image=RotateImage(_image,90.0,_exception);
1761              break;
1762            }
1763            case RightBottomOrientation:
1764            {
1765              new_image=TransverseImage(_image,_exception);
1766              break;
1767            }
1768            case LeftBottomOrientation:
1769            {
1770              new_image=RotateImage(_image,270.0,_exception);
1771              break;
1772            }
1773            default:
1774              break;
1775          }
1776          if (new_image != (Image *) NULL)
1777            new_image->orientation=TopLeftOrientation;
1778          break;
1779        }
1780      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1781    }
1782    case 'b':
1783    {
1784      if (LocaleCompare("black-threshold",option+1) == 0)
1785        {
1786          if (IfMagickFalse(IsGeometry(arg1)))
1787            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1788          (void) BlackThresholdImage(_image,arg1,_exception);
1789          break;
1790        }
1791      if (LocaleCompare("blue-shift",option+1) == 0)
1792        {
1793          geometry_info.rho=1.5;
1794          if (IfNormalOp) {
1795            flags=ParseGeometry(arg1,&geometry_info);
1796            if ((flags & RhoValue) == 0)
1797              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1798          }
1799          new_image=BlueShiftImage(_image,geometry_info.rho,_exception);
1800          break;
1801        }
1802      if (LocaleCompare("blur",option+1) == 0)
1803        {
1804          flags=ParseGeometry(arg1,&geometry_info);
1805          if ((flags & (RhoValue|SigmaValue)) == 0)
1806            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1807          if ((flags & SigmaValue) == 0)
1808            geometry_info.sigma=1.0;
1809          new_image=BlurImage(_image,geometry_info.rho,geometry_info.sigma,
1810           _exception);
1811          break;
1812        }
1813      if (LocaleCompare("border",option+1) == 0)
1814        {
1815          CompositeOperator
1816            compose;
1817
1818          const char*
1819            value;
1820
1821          flags=ParsePageGeometry(_image,arg1,&geometry,_exception);
1822          if ((flags & RhoValue) == 0)
1823            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1824          if ((flags & SigmaValue) == 0)
1825            geometry.height=geometry.width;
1826
1827          value=GetImageOption(_image_info,"compose");
1828          if (value != (const char *) NULL)
1829            compose=(CompositeOperator) ParseCommandOption(
1830                 MagickComposeOptions,MagickFalse,value);
1831          else
1832            compose=OverCompositeOp;  /* use Over not _image->compose */
1833
1834          new_image=BorderImage(_image,&geometry,compose,_exception);
1835          break;
1836        }
1837      if (LocaleCompare("brightness-contrast",option+1) == 0)
1838        {
1839          double
1840            brightness,
1841            contrast;
1842
1843          GeometryInfo
1844            geometry_info;
1845
1846          MagickStatusType
1847            flags;
1848
1849          flags=ParseGeometry(arg1,&geometry_info);
1850          if ((flags & RhoValue) == 0)
1851            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1852          brightness=geometry_info.rho;
1853          contrast=0.0;
1854          if ((flags & SigmaValue) != 0)
1855            contrast=geometry_info.sigma;
1856          (void) BrightnessContrastImage(_image,brightness,contrast,
1857            _exception);
1858          break;
1859        }
1860      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
1861    }
1862    case 'c':
1863    {
1864      if (LocaleCompare("cdl",option+1) == 0)
1865        {
1866          char
1867            *color_correction_collection;
1868
1869          /*
1870            Color correct with a color decision list.
1871          */
1872          color_correction_collection=FileToString(arg1,~0,_exception);
1873          if (color_correction_collection == (char *) NULL)
1874            break;
1875          (void) ColorDecisionListImage(_image,color_correction_collection,
1876            _exception);
1877          break;
1878        }
1879      if (LocaleCompare("charcoal",option+1) == 0)
1880        {
1881          flags=ParseGeometry(arg1,&geometry_info);
1882          if ((flags & (RhoValue|SigmaValue)) == 0)
1883            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1884          if ((flags & SigmaValue) == 0)
1885            geometry_info.sigma=1.0;
1886          if ((flags & XiValue) == 0)
1887            geometry_info.xi=1.0;
1888          new_image=CharcoalImage(_image,geometry_info.rho,
1889            geometry_info.sigma,_exception);
1890          break;
1891        }
1892      if (LocaleCompare("chop",option+1) == 0)
1893        {
1894          if (IfMagickFalse(IsGeometry(arg1)))
1895            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1896          (void) ParseGravityGeometry(_image,arg1,&geometry,_exception);
1897          new_image=ChopImage(_image,&geometry,_exception);
1898          break;
1899        }
1900      if (LocaleCompare("clamp",option+1) == 0)
1901        {
1902          (void) ClampImage(_image,_exception);
1903          break;
1904        }
1905      if (LocaleCompare("clip",option+1) == 0)
1906        {
1907          if (IfNormalOp)
1908            (void) ClipImage(_image,_exception);
1909          else /* "+mask" remove the write mask */
1910            (void) SetImageMask(_image,(Image *) NULL,_exception);
1911          break;
1912        }
1913      if (LocaleCompare("clip-mask",option+1) == 0)
1914        {
1915          CacheView
1916            *mask_view;
1917
1918          Image
1919            *mask_image;
1920
1921          register Quantum
1922            *restrict q;
1923
1924          register ssize_t
1925            x;
1926
1927          ssize_t
1928            y;
1929
1930          if (IfPlusOp) {
1931            /* "+clip-mask" Remove the write mask */
1932            (void) SetImageMask(_image,(Image *) NULL,_exception);
1933            break;
1934          }
1935          mask_image=GetImageCache(_image_info,arg1,_exception);
1936          if (mask_image == (Image *) NULL)
1937            break;
1938          if (IfMagickFalse(SetImageStorageClass(mask_image,DirectClass,_exception)))
1939            break;
1940          /* Create a write mask from cli_wand mask image */
1941          /* FUTURE: use Alpha operations instead and create a Grey Image */
1942          mask_view=AcquireAuthenticCacheView(mask_image,_exception);
1943          for (y=0; y < (ssize_t) mask_image->rows; y++)
1944          {
1945            q=GetCacheViewAuthenticPixels(mask_view,0,y,mask_image->columns,1,
1946              _exception);
1947            if (q == (Quantum *) NULL)
1948              break;
1949            for (x=0; x < (ssize_t) mask_image->columns; x++)
1950            {
1951              if (IfMagickFalse(mask_image->matte))
1952                SetPixelAlpha(mask_image,GetPixelIntensity(mask_image,q),q);
1953              SetPixelRed(mask_image,GetPixelAlpha(mask_image,q),q);
1954              SetPixelGreen(mask_image,GetPixelAlpha(mask_image,q),q);
1955              SetPixelBlue(mask_image,GetPixelAlpha(mask_image,q),q);
1956              q+=GetPixelChannels(mask_image);
1957            }
1958            if (IfMagickFalse(SyncCacheViewAuthenticPixels(mask_view,_exception)))
1959              break;
1960          }
1961          /* clean up and set the write mask */
1962          mask_view=DestroyCacheView(mask_view);
1963          mask_image->matte=MagickTrue;
1964          (void) SetImageMask(_image,mask_image,_exception);
1965          mask_image=DestroyImage(mask_image);
1966          break;
1967        }
1968      if (LocaleCompare("clip-path",option+1) == 0)
1969        {
1970          (void) ClipImagePath(_image,arg1,normal_op,_exception);
1971          break;
1972        }
1973      if (LocaleCompare("colorize",option+1) == 0)
1974        {
1975          if (IfMagickFalse(IsGeometry(arg1)))
1976            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1977          new_image=ColorizeImage(_image,arg1,&_draw_info->fill,_exception);
1978          break;
1979        }
1980      if (LocaleCompare("color-matrix",option+1) == 0)
1981        {
1982          KernelInfo
1983            *kernel;
1984
1985          kernel=AcquireKernelInfo(arg1);
1986          if (kernel == (KernelInfo *) NULL)
1987            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
1988          new_image=ColorMatrixImage(_image,kernel,_exception);
1989          kernel=DestroyKernelInfo(kernel);
1990          break;
1991        }
1992      if (LocaleCompare("colors",option+1) == 0)
1993        {
1994          /* Reduce the number of colors in the image.
1995             FUTURE: also provide 'plus version with image 'color counts'
1996          */
1997          _quantize_info->number_colors=StringToUnsignedLong(arg1);
1998          if (_quantize_info->number_colors == 0)
1999            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2000          if ((_image->storage_class == DirectClass) ||
2001              _image->colors > _quantize_info->number_colors)
2002            (void) QuantizeImage(_quantize_info,_image,_exception);
2003          else
2004            (void) CompressImageColormap(_image,_exception);
2005          break;
2006        }
2007      if (LocaleCompare("colorspace",option+1) == 0)
2008        {
2009          /* WARNING: this is both a image_info setting (already done)
2010                      and a operator to change image colorspace.
2011
2012             FUTURE: default colorspace should be sRGB!
2013             Unless some type of 'linear colorspace' mode is set.
2014
2015             Note that +colorspace sets "undefined" or no effect on
2016             new images, but forces images already in memory back to RGB!
2017             That seems to be a little strange!
2018          */
2019          (void) TransformImageColorspace(_image,
2020                    IfNormalOp ? _image_info->colorspace : RGBColorspace,
2021                    _exception);
2022          break;
2023        }
2024      if (LocaleCompare("contrast",option+1) == 0)
2025        {
2026          CLIWandWarnReplaced(normal_op?"-level":"+level");
2027          (void) ContrastImage(_image,normal_op,_exception);
2028          break;
2029        }
2030      if (LocaleCompare("contrast-stretch",option+1) == 0)
2031        {
2032          double
2033            black_point,
2034            white_point;
2035
2036          MagickStatusType
2037            flags;
2038
2039          flags=ParseGeometry(arg1,&geometry_info);
2040          if ((flags & RhoValue) == 0)
2041            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2042          black_point=geometry_info.rho;
2043          white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma :
2044            black_point;
2045          if ((flags & PercentValue) != 0) {
2046              black_point*=(double) _image->columns*_image->rows/100.0;
2047              white_point*=(double) _image->columns*_image->rows/100.0;
2048            }
2049          white_point=(MagickRealType) _image->columns*_image->rows-
2050            white_point;
2051          (void) ContrastStretchImage(_image,black_point,white_point,
2052            _exception);
2053          break;
2054        }
2055      if (LocaleCompare("convolve",option+1) == 0)
2056        {
2057          KernelInfo
2058            *kernel_info;
2059
2060          kernel_info=AcquireKernelInfo(arg1);
2061          if (kernel_info == (KernelInfo *) NULL)
2062            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2063          new_image=MorphologyImage(_image,CorrelateMorphology,1,kernel_info,
2064            _exception);
2065          kernel_info=DestroyKernelInfo(kernel_info);
2066          break;
2067        }
2068      if (LocaleCompare("crop",option+1) == 0)
2069        {
2070          /* WARNING: This can generate multiple images! */
2071          if (IfMagickFalse(IsGeometry(arg1)))
2072            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2073          new_image=CropImageToTiles(_image,arg1,_exception);
2074          break;
2075        }
2076      if (LocaleCompare("cycle",option+1) == 0)
2077        {
2078          if (IfMagickFalse(IsGeometry(arg1)))
2079            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2080          (void) CycleColormapImage(_image,(ssize_t) StringToLong(arg1),
2081            _exception);
2082          break;
2083        }
2084      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2085    }
2086    case 'd':
2087    {
2088      if (LocaleCompare("decipher",option+1) == 0)
2089        {
2090          StringInfo
2091            *passkey;
2092
2093          passkey=FileToStringInfo(arg1,~0,_exception);
2094          if (passkey == (StringInfo *) NULL)
2095            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2096
2097          (void) PasskeyDecipherImage(_image,passkey,_exception);
2098          passkey=DestroyStringInfo(passkey);
2099          break;
2100        }
2101      if (LocaleCompare("depth",option+1) == 0)
2102        {
2103          /* The _image_info->depth setting has already been set
2104             We just need to apply it to all images in current sequence
2105
2106             WARNING: Depth from 8 to 16 causes 'quantum rounding to images!
2107             That is it really is an operation, not a setting! Arrgghhh
2108
2109             FUTURE: this should not be an operator!!!
2110          */
2111          (void) SetImageDepth(_image,_image_info->depth,_exception);
2112          break;
2113        }
2114      if (LocaleCompare("deskew",option+1) == 0)
2115        {
2116          double
2117            threshold;
2118
2119          if (IfNormalOp) {
2120            if (IfMagickFalse(IsGeometry(arg1)))
2121              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2122            threshold=StringToDoubleInterval(arg1,(double) QuantumRange+1.0);
2123          }
2124          else
2125            threshold=40.0*QuantumRange/100.0;
2126          new_image=DeskewImage(_image,threshold,_exception);
2127          break;
2128        }
2129      if (LocaleCompare("despeckle",option+1) == 0)
2130        {
2131          new_image=DespeckleImage(_image,_exception);
2132          break;
2133        }
2134      if (LocaleCompare("distort",option+1) == 0)
2135        {
2136          char
2137            *arg;
2138
2139          double
2140            *args;
2141
2142          ssize_t
2143            count;
2144
2145          parse = ParseCommandOption(MagickDistortOptions,MagickFalse,arg1);
2146          if ( parse < 0 )
2147             CLIWandExceptArgBreak(OptionError,"UnrecognizedDistortMethod",
2148                                      option,arg1);
2149          if ((DistortImageMethod) parse == ResizeDistortion)
2150            {
2151               double
2152                 resize_args[2];
2153               /* Special Case - Argument is actually a resize geometry!
2154               ** Convert that to an appropriate distortion argument array.
2155               ** FUTURE: make a separate special resize operator
2156                    Roll into a resize special operator */
2157               if (IfMagickFalse(IsGeometry(arg2)))
2158                 CLIWandExceptArgBreak(OptionError,"InvalidGeometry",
2159                                           option,arg2);
2160               (void) ParseRegionGeometry(_image,arg2,&geometry,_exception);
2161               resize_args[0]=(double) geometry.width;
2162               resize_args[1]=(double) geometry.height;
2163               new_image=DistortImage(_image,(DistortImageMethod) parse,
2164                    (size_t)2,resize_args,MagickTrue,_exception);
2165               break;
2166            }
2167          /* allow percent escapes in argument string */
2168          arg=InterpretImageProperties(_image_info,_image,arg2,_exception);
2169          if (arg == (char *) NULL)
2170            break;
2171          /* convert argument string into an array of doubles */
2172          args = StringToArrayOfDoubles(arg,&count,_exception);
2173          arg=DestroyString(arg);
2174          if (args == (double *)NULL )
2175            CLIWandExceptArgBreak(OptionError,"InvalidNumberList",option,arg2);
2176
2177          new_image=DistortImage(_image,(DistortImageMethod) parse,count,args,
2178               plus_alt_op,_exception);
2179          args=(double *) RelinquishMagickMemory(args);
2180          break;
2181        }
2182      if (LocaleCompare("draw",option+1) == 0)
2183        {
2184          (void) CloneString(&_draw_info->primitive,arg1);
2185          (void) DrawImage(_image,_draw_info,_exception);
2186          (void) CloneString(&_draw_info->primitive,(char *)NULL);
2187          break;
2188        }
2189      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2190    }
2191    case 'e':
2192    {
2193      if (LocaleCompare("edge",option+1) == 0)
2194        {
2195          flags=ParseGeometry(arg1,&geometry_info);
2196          if ((flags & (RhoValue|SigmaValue)) == 0)
2197            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2198          if ((flags & SigmaValue) == 0)
2199            geometry_info.sigma=1.0;
2200          new_image=EdgeImage(_image,geometry_info.rho,geometry_info.sigma,
2201               _exception);
2202          break;
2203        }
2204      if (LocaleCompare("emboss",option+1) == 0)
2205        {
2206          flags=ParseGeometry(arg1,&geometry_info);
2207          if ((flags & (RhoValue|SigmaValue)) == 0)
2208            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2209          if ((flags & SigmaValue) == 0)
2210            geometry_info.sigma=1.0;
2211          new_image=EmbossImage(_image,geometry_info.rho,
2212            geometry_info.sigma,_exception);
2213          break;
2214        }
2215      if (LocaleCompare("encipher",option+1) == 0)
2216        {
2217          StringInfo
2218            *passkey;
2219
2220          passkey=FileToStringInfo(arg1,~0,_exception);
2221          if (passkey != (StringInfo *) NULL)
2222            {
2223              (void) PasskeyEncipherImage(_image,passkey,_exception);
2224              passkey=DestroyStringInfo(passkey);
2225            }
2226          break;
2227        }
2228      if (LocaleCompare("enhance",option+1) == 0)
2229        {
2230          new_image=EnhanceImage(_image,_exception);
2231          break;
2232        }
2233      if (LocaleCompare("equalize",option+1) == 0)
2234        {
2235          (void) EqualizeImage(_image,_exception);
2236          break;
2237        }
2238      if (LocaleCompare("evaluate",option+1) == 0)
2239        {
2240          double
2241            constant;
2242
2243          parse = ParseCommandOption(MagickEvaluateOptions,MagickFalse,arg1);
2244          if ( parse < 0 )
2245            CLIWandExceptArgBreak(OptionError,"UnrecognizedEvaluateOperator",
2246                 option,arg1);
2247          if (IfMagickFalse(IsGeometry(arg2)))
2248            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg2);
2249          constant=StringToDoubleInterval(arg2,(double) QuantumRange+1.0);
2250          (void) EvaluateImage(_image,(MagickEvaluateOperator)parse,constant,
2251               _exception);
2252          break;
2253        }
2254      if (LocaleCompare("extent",option+1) == 0)
2255        {
2256          if (IfMagickFalse(IsGeometry(arg1)))
2257            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2258          flags=ParseGravityGeometry(_image,arg1,&geometry,_exception);
2259          if (geometry.width == 0)
2260            geometry.width=_image->columns;
2261          if (geometry.height == 0)
2262            geometry.height=_image->rows;
2263          new_image=ExtentImage(_image,&geometry,_exception);
2264          break;
2265        }
2266      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2267    }
2268    case 'f':
2269    {
2270      if (LocaleCompare("features",option+1) == 0)
2271        {
2272          /* FUTURE: move to SyncImageSettings() and AcqireImage()??? */
2273          if (IfPlusOp) {
2274              (void) DeleteImageArtifact(_image,"identify:features");
2275              break;
2276            }
2277          (void) SetImageArtifact(_image,"identify:features","true");
2278          (void) SetImageArtifact(_image,"verbose","true");
2279          break;
2280        }
2281      if (LocaleCompare("flip",option+1) == 0)
2282        {
2283          new_image=FlipImage(_image,_exception);
2284          break;
2285        }
2286      if (LocaleCompare("flop",option+1) == 0)
2287        {
2288          new_image=FlopImage(_image,_exception);
2289          break;
2290        }
2291      if (LocaleCompare("floodfill",option+1) == 0)
2292        {
2293          PixelInfo
2294            target;
2295
2296          if (IfMagickFalse(IsGeometry(arg1)))
2297            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2298          (void) ParsePageGeometry(_image,arg1,&geometry,_exception);
2299          (void) QueryColorCompliance(arg2,AllCompliance,&target,_exception);
2300          (void) FloodfillPaintImage(_image,_draw_info,&target,geometry.x,
2301                    geometry.y,plus_alt_op,_exception);
2302          break;
2303        }
2304      if (LocaleCompare("frame",option+1) == 0)
2305        {
2306          FrameInfo
2307            frame_info;
2308
2309          CompositeOperator
2310            compose;
2311
2312          const char*
2313            value;
2314
2315          value=GetImageOption(_image_info,"compose");
2316          if (value != (const char *) NULL)
2317            compose=(CompositeOperator) ParseCommandOption(
2318                 MagickComposeOptions,MagickFalse,value);
2319          else
2320            compose=OverCompositeOp;  /* use Over not _image->compose */
2321
2322          if (IfMagickFalse(IsGeometry(arg1)))
2323            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2324          flags=ParsePageGeometry(_image,arg1,&geometry,_exception);
2325          frame_info.width=geometry.width;
2326          frame_info.height=geometry.height;
2327          if ((flags & HeightValue) == 0)
2328            frame_info.height=geometry.width;
2329          frame_info.outer_bevel=geometry.x;
2330          frame_info.inner_bevel=geometry.y;
2331          frame_info.x=(ssize_t) frame_info.width;
2332          frame_info.y=(ssize_t) frame_info.height;
2333          frame_info.width=_image->columns+2*frame_info.width;
2334          frame_info.height=_image->rows+2*frame_info.height;
2335          new_image=FrameImage(_image,&frame_info,compose,_exception);
2336          break;
2337        }
2338      if (LocaleCompare("function",option+1) == 0)
2339        {
2340          char
2341            *arg;
2342
2343          double
2344            *args;
2345
2346          ssize_t
2347            count;
2348
2349          parse=ParseCommandOption(MagickFunctionOptions,MagickFalse,arg1);
2350          if ( parse < 0 )
2351            CLIWandExceptArgBreak(OptionError,"UnrecognizedFunction",
2352                 option,arg1);
2353          /* allow percent escapes in argument string */
2354          arg=InterpretImageProperties(_image_info,_image,arg2,_exception);
2355          if (arg == (char *) NULL)
2356            break;
2357          /* convert argument string into an array of doubles */
2358          args = StringToArrayOfDoubles(arg,&count,_exception);
2359          arg=DestroyString(arg);
2360          if (args == (double *)NULL )
2361            CLIWandExceptArgBreak(OptionError,"InvalidNumberList",option,arg2);
2362
2363          (void) FunctionImage(_image,(MagickFunction)parse,count,args,
2364               _exception);
2365          args=(double *) RelinquishMagickMemory(args);
2366          break;
2367        }
2368      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2369    }
2370    case 'g':
2371    {
2372      if (LocaleCompare("gamma",option+1) == 0)
2373        {
2374          if (IfMagickFalse(IsGeometry(arg1)))
2375            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2376          if (IfNormalOp)
2377            (void) GammaImage(_image,StringToDouble(arg1,(char **) NULL),
2378                 _exception);
2379          else
2380            _image->gamma=StringToDouble(arg1,(char **) NULL);
2381          break;
2382        }
2383      if (LocaleCompare("gaussian-blur",option+1) == 0)
2384        {
2385          flags=ParseGeometry(arg1,&geometry_info);
2386          if ((flags & (RhoValue|SigmaValue)) == 0)
2387            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2388          if ((flags & SigmaValue) == 0)
2389            geometry_info.sigma=1.0;
2390          new_image=GaussianBlurImage(_image,geometry_info.rho,
2391            geometry_info.sigma,_exception);
2392          break;
2393        }
2394      if (LocaleCompare("gaussian",option+1) == 0)
2395        {
2396          CLIWandWarnReplaced("-gaussian-blur");
2397          CLISimpleOperatorImage(cli_wand,"-gaussian-blur",arg1,NULL);
2398        }
2399      if (LocaleCompare("geometry",option+1) == 0)
2400        {
2401          /*
2402            Record Image offset for composition. (A Setting)
2403            Resize last _image. (ListOperator)  -- DEPRECIATE
2404            FUTURE: Why if no 'offset' does this resize ALL images?
2405            Also why is the setting recorded in the IMAGE non-sense!
2406          */
2407          if (IfPlusOp)
2408            { /* remove the previous composition geometry offset! */
2409              if (_image->geometry != (char *) NULL)
2410                _image->geometry=DestroyString(_image->geometry);
2411              break;
2412            }
2413          if (IfMagickFalse(IsGeometry(arg1)))
2414            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2415          flags=ParseRegionGeometry(_image,arg1,&geometry,_exception);
2416          if (((flags & XValue) != 0) || ((flags & YValue) != 0))
2417            (void) CloneString(&_image->geometry,arg1);
2418          else
2419            new_image=ResizeImage(_image,geometry.width,geometry.height,
2420              _image->filter,_exception);
2421          break;
2422        }
2423      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2424    }
2425    case 'i':
2426    {
2427      if (LocaleCompare("identify",option+1) == 0)
2428        {
2429          const char
2430            *format,
2431            *text;
2432
2433          format=GetImageOption(_image_info,"format");
2434          if (format == (char *) NULL)
2435            {
2436              (void) IdentifyImage(_image,stdout,_image_info->verbose,
2437                _exception);
2438              break;
2439            }
2440          text=InterpretImageProperties(_image_info,_image,format,_exception);
2441          if (text == (char *) NULL)
2442            break;
2443          (void) fputs(text,stdout);
2444          (void) fputc('\n',stdout);
2445          text=DestroyString((char *)text);
2446          break;
2447        }
2448      if (LocaleCompare("implode",option+1) == 0)
2449        {
2450          flags=ParseGeometry(arg1,&geometry_info);
2451          if ((flags & RhoValue) == 0)
2452            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2453          new_image=ImplodeImage(_image,geometry_info.rho,_image->interpolate,
2454               _exception);
2455          break;
2456        }
2457      if (LocaleCompare("interpolative-resize",option+1) == 0)
2458        {
2459          /* FUTURE: New to IMv7
2460               Roll into a resize special operator */
2461          if (IfMagickFalse(IsGeometry(arg1)))
2462            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2463          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
2464          new_image=InterpolativeResizeImage(_image,geometry.width,
2465               geometry.height,_image->interpolate,_exception);
2466          break;
2467        }
2468      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2469    }
2470    case 'l':
2471    {
2472      if (LocaleCompare("lat",option+1) == 0)
2473        {
2474          flags=ParseGeometry(arg1,&geometry_info);
2475          if ((flags & (RhoValue|SigmaValue)) == 0)
2476            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2477          if ((flags & PercentValue) != 0)
2478            geometry_info.xi=(double) QuantumRange*geometry_info.xi/100.0;
2479          new_image=AdaptiveThresholdImage(_image,(size_t) geometry_info.rho,
2480               (size_t) geometry_info.sigma,(double) geometry_info.xi,
2481               _exception);
2482          break;
2483        }
2484      if (LocaleCompare("level",option+1) == 0)
2485        {
2486          MagickRealType
2487            black_point,
2488            gamma,
2489            white_point;
2490
2491          MagickStatusType
2492            flags;
2493
2494          flags=ParseGeometry(arg1,&geometry_info);
2495          if ((flags & RhoValue) == 0)
2496            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2497          black_point=geometry_info.rho;
2498          white_point=(MagickRealType) QuantumRange;
2499          if ((flags & SigmaValue) != 0)
2500            white_point=geometry_info.sigma;
2501          gamma=1.0;
2502          if ((flags & XiValue) != 0)
2503            gamma=geometry_info.xi;
2504          if ((flags & PercentValue) != 0)
2505            {
2506              black_point*=(MagickRealType) (QuantumRange/100.0);
2507              white_point*=(MagickRealType) (QuantumRange/100.0);
2508            }
2509          if ((flags & SigmaValue) == 0)
2510            white_point=(MagickRealType) QuantumRange-black_point;
2511          if (IfPlusOp || ((flags & AspectValue) != 0))
2512            (void) LevelizeImage(_image,black_point,white_point,gamma,_exception);
2513          else
2514            (void) LevelImage(_image,black_point,white_point,gamma,_exception);
2515          break;
2516        }
2517      if (LocaleCompare("level-colors",option+1) == 0)
2518        {
2519          char
2520            token[MaxTextExtent];
2521
2522          const char
2523            *p;
2524
2525          PixelInfo
2526            black_point,
2527            white_point;
2528
2529          p=(const char *) arg1;
2530          GetMagickToken(p,&p,token);  /* get black point color */
2531          if ((isalpha((int) *token) != 0) || ((*token == '#') != 0))
2532            (void) QueryColorCompliance(token,AllCompliance,
2533                      &black_point,_exception);
2534          else
2535            (void) QueryColorCompliance("#000000",AllCompliance,
2536                      &black_point,_exception);
2537          if (isalpha((int) token[0]) || (token[0] == '#'))
2538            GetMagickToken(p,&p,token);
2539          if (*token == '\0')
2540            white_point=black_point; /* set everything to that color */
2541          else
2542            {
2543              if ((isalpha((int) *token) == 0) && ((*token == '#') == 0))
2544                GetMagickToken(p,&p,token); /* Get white point color. */
2545              if ((isalpha((int) *token) != 0) || ((*token == '#') != 0))
2546                (void) QueryColorCompliance(token,AllCompliance,
2547                           &white_point,_exception);
2548              else
2549                (void) QueryColorCompliance("#ffffff",AllCompliance,
2550                           &white_point,_exception);
2551            }
2552          (void) LevelImageColors(_image,&black_point,&white_point,
2553                     plus_alt_op,_exception);
2554          break;
2555        }
2556      if (LocaleCompare("linear-stretch",option+1) == 0)
2557        {
2558          double
2559            black_point,
2560            white_point;
2561
2562          MagickStatusType
2563            flags;
2564
2565          flags=ParseGeometry(arg1,&geometry_info);
2566          if ((flags & RhoValue) == 0)
2567            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2568          black_point=geometry_info.rho;
2569          white_point=(MagickRealType) _image->columns*_image->rows;
2570          if ((flags & SigmaValue) != 0)
2571            white_point=geometry_info.sigma;
2572          if ((flags & PercentValue) != 0)
2573            {
2574              black_point*=(double) _image->columns*_image->rows/100.0;
2575              white_point*=(double) _image->columns*_image->rows/100.0;
2576            }
2577          if ((flags & SigmaValue) == 0)
2578            white_point=(MagickRealType) _image->columns*_image->rows-
2579              black_point;
2580          (void) LinearStretchImage(_image,black_point,white_point,_exception);
2581          break;
2582        }
2583      if (LocaleCompare("liquid-rescale",option+1) == 0)
2584        {
2585          /* FUTURE: Roll into a resize special operator */
2586          if (IfMagickFalse(IsGeometry(arg1)))
2587            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2588          flags=ParseRegionGeometry(_image,arg1,&geometry,_exception);
2589          if ((flags & XValue) == 0)
2590            geometry.x=1;
2591          if ((flags & YValue) == 0)
2592            geometry.y=0;
2593          new_image=LiquidRescaleImage(_image,geometry.width,
2594            geometry.height,1.0*geometry.x,1.0*geometry.y,_exception);
2595          break;
2596        }
2597      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2598    }
2599    case 'm':
2600    {
2601      if (LocaleCompare("map",option+1) == 0)
2602        {
2603          CLIWandWarnReplaced("-remap");
2604          CLISimpleOperatorImage(cli_wand,"-remap",NULL,NULL);
2605          break;
2606        }
2607      if (LocaleCompare("mask",option+1) == 0)
2608        {
2609          Image
2610            *mask;
2611
2612          if (IfPlusOp)
2613            { /* Remove a mask. */
2614              (void) SetImageMask(_image,(Image *) NULL,_exception);
2615              break;
2616            }
2617          /* Set the image mask. */
2618          mask=GetImageCache(_image_info,arg1,_exception);
2619          if (mask == (Image *) NULL)
2620            break;
2621          (void) SetImageMask(_image,mask,_exception);
2622          mask=DestroyImage(mask);
2623          break;
2624        }
2625      if (LocaleCompare("matte",option+1) == 0)
2626        {
2627          CLIWandWarnReplaced(IfNormalOp?"-alpha Set":"-alpha Off");
2628          (void) SetImageAlphaChannel(_image,IfNormalOp ? SetAlphaChannel :
2629                         DeactivateAlphaChannel, _exception);
2630          break;
2631        }
2632      if (LocaleCompare("median",option+1) == 0)
2633        {
2634          CLIWandWarnReplaced("-statistic Median");
2635          CLISimpleOperatorImage(cli_wand,"-statistic","Median",arg1);
2636          break;
2637        }
2638      if (LocaleCompare("mode",option+1) == 0)
2639        {
2640          /* FUTURE: note this is also a special "montage" option */
2641          CLIWandWarnReplaced("-statistic Mode");
2642          CLISimpleOperatorImage(cli_wand,"-statistic","Mode",arg1);
2643          break;
2644        }
2645      if (LocaleCompare("modulate",option+1) == 0)
2646        {
2647          if (IfMagickFalse(IsGeometry(arg1)))
2648            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2649          (void) ModulateImage(_image,arg1,_exception);
2650          break;
2651        }
2652      if (LocaleCompare("monitor",option+1) == 0)
2653        {
2654          (void) SetImageProgressMonitor(_image, IfNormalOp ? MonitorProgress :
2655                (MagickProgressMonitor) NULL,(void *) NULL);
2656          break;
2657        }
2658      if (LocaleCompare("monochrome",option+1) == 0)
2659        {
2660          (void) SetImageType(_image,BilevelType,_exception);
2661          break;
2662        }
2663      if (LocaleCompare("morphology",option+1) == 0)
2664        {
2665          char
2666            token[MaxTextExtent];
2667
2668          const char
2669            *p;
2670
2671          KernelInfo
2672            *kernel;
2673
2674          ssize_t
2675            iterations;
2676
2677          p=arg1;
2678          GetMagickToken(p,&p,token);
2679          parse=ParseCommandOption(MagickMorphologyOptions,MagickFalse,token);
2680          if ( parse < 0 )
2681            CLIWandExceptArgBreak(OptionError,"UnrecognizedFunction",
2682                 option,arg1);
2683          iterations=1L;
2684          GetMagickToken(p,&p,token);
2685          if ((*p == ':') || (*p == ','))
2686            GetMagickToken(p,&p,token);
2687          if ((*p != '\0'))
2688            iterations=(ssize_t) StringToLong(p);
2689          kernel=AcquireKernelInfo(arg2);
2690          if (kernel == (KernelInfo *) NULL)
2691            CLIWandExceptArgBreak(OptionError,"UnabletoParseKernel",
2692                 option,arg2);
2693          new_image=MorphologyImage(_image,(MorphologyMethod)parse,
2694               iterations,kernel,_exception);
2695          kernel=DestroyKernelInfo(kernel);
2696          break;
2697        }
2698      if (LocaleCompare("motion-blur",option+1) == 0)
2699        {
2700          flags=ParseGeometry(arg1,&geometry_info);
2701          if ((flags & (RhoValue|SigmaValue)) == 0)
2702            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2703          if ((flags & SigmaValue) == 0)
2704            geometry_info.sigma=1.0;
2705          new_image=MotionBlurImage(_image,geometry_info.rho,
2706            geometry_info.sigma,geometry_info.xi,_exception);
2707          break;
2708        }
2709      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2710    }
2711    case 'n':
2712    {
2713      if (LocaleCompare("negate",option+1) == 0)
2714        {
2715          (void) NegateImage(_image, plus_alt_op, _exception);
2716          break;
2717        }
2718      if (LocaleCompare("noise",option+1) == 0)
2719        {
2720          double
2721            attenuate;
2722
2723          const char*
2724            value;
2725
2726          if (IfNormalOp)
2727            {
2728              CLIWandWarnReplaced("-statistic NonPeak");
2729              CLISimpleOperatorImage(cli_wand,"-statistic","NonPeak",arg1);
2730              break;
2731            }
2732          parse=ParseCommandOption(MagickNoiseOptions,MagickFalse,arg1);
2733          if ( parse < 0 )
2734            CLIWandExceptArgBreak(OptionError,"UnrecognizedNoiseType",
2735                option,arg1);
2736          attenuate=1.0;
2737          value=GetImageOption(_image_info,"attenuate");
2738          if  (value != (const char *) NULL)
2739            attenuate=StringToDouble(value,(char **) NULL);
2740          new_image=AddNoiseImage(_image,(NoiseType)parse,attenuate,
2741               _exception);
2742          break;
2743        }
2744      if (LocaleCompare("normalize",option+1) == 0)
2745        {
2746          (void) NormalizeImage(_image,_exception);
2747          break;
2748        }
2749      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2750    }
2751    case 'o':
2752    {
2753      if (LocaleCompare("opaque",option+1) == 0)
2754        {
2755          PixelInfo
2756            target;
2757
2758          (void) QueryColorCompliance(arg1,AllCompliance,&target,_exception);
2759          (void) OpaquePaintImage(_image,&target,&_draw_info->fill,plus_alt_op,
2760               _exception);
2761          break;
2762        }
2763      if (LocaleCompare("ordered-dither",option+1) == 0)
2764        {
2765          (void) OrderedPosterizeImage(_image,arg1,_exception);
2766          break;
2767        }
2768      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2769    }
2770    case 'p':
2771    {
2772      if (LocaleCompare("paint",option+1) == 0)
2773        {
2774          flags=ParseGeometry(arg1,&geometry_info);
2775          if ((flags & (RhoValue|SigmaValue)) == 0)
2776            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2777          new_image=OilPaintImage(_image,geometry_info.rho,geometry_info.sigma,
2778               _exception);
2779          break;
2780        }
2781      if (LocaleCompare("polaroid",option+1) == 0)
2782        {
2783          const char
2784            *caption;
2785
2786          double
2787            angle;
2788
2789          if (IfPlusOp) {
2790            RandomInfo
2791            *random_info;
2792
2793            random_info=AcquireRandomInfo();
2794            angle=22.5*(GetPseudoRandomValue(random_info)-0.5);
2795            random_info=DestroyRandomInfo(random_info);
2796          }
2797          else {
2798            flags=ParseGeometry(arg1,&geometry_info);
2799            if ((flags & RhoValue) == 0)
2800              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2801            angle=geometry_info.rho;
2802          }
2803          caption=GetImageProperty(_image,"caption",_exception);
2804          new_image=PolaroidImage(_image,_draw_info,caption,angle,
2805            _image->interpolate,_exception);
2806          break;
2807        }
2808      if (LocaleCompare("posterize",option+1) == 0)
2809        {
2810          flags=ParseGeometry(arg1,&geometry_info);
2811          if ((flags & RhoValue) == 0)
2812            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2813          (void) PosterizeImage(_image,(size_t) geometry_info.rho,
2814               _quantize_info->dither,_exception);
2815          break;
2816        }
2817      if (LocaleCompare("preview",option+1) == 0)
2818        {
2819          /* FUTURE: should be a 'Genesis' option?
2820             Option however is also in WandSettingOptionInfo()
2821             Why???
2822          */
2823          parse=ParseCommandOption(MagickPreviewOptions, MagickFalse,arg1);
2824          if ( parse < 0 )
2825            CLIWandExceptArgBreak(OptionError,"UnrecognizedPreviewType",
2826                option,arg1);
2827          new_image=PreviewImage(_image,(PreviewType)parse,_exception);
2828          break;
2829        }
2830      if (LocaleCompare("profile",option+1) == 0)
2831        {
2832          const char
2833            *name;
2834
2835          const StringInfo
2836            *profile;
2837
2838          Image
2839            *profile_image;
2840
2841          ImageInfo
2842            *profile_info;
2843
2844          if (IfPlusOp)
2845            { /* Remove a profile from the _image.  */
2846              (void) ProfileImage(_image,arg1,(const unsigned char *)
2847                NULL,0,_exception);
2848              break;
2849            }
2850          /* Associate a profile with the _image.  */
2851          profile_info=CloneImageInfo(_image_info);
2852          profile=GetImageProfile(_image,"iptc");
2853          if (profile != (StringInfo *) NULL)
2854            profile_info->profile=(void *) CloneStringInfo(profile);
2855          profile_image=GetImageCache(profile_info,arg1,_exception);
2856          profile_info=DestroyImageInfo(profile_info);
2857          if (profile_image == (Image *) NULL)
2858            {
2859              StringInfo
2860                *profile;
2861
2862              profile_info=CloneImageInfo(_image_info);
2863              (void) CopyMagickString(profile_info->filename,arg1,
2864                MaxTextExtent);
2865              profile=FileToStringInfo(profile_info->filename,~0UL,_exception);
2866              if (profile != (StringInfo *) NULL)
2867                {
2868                  (void) ProfileImage(_image,profile_info->magick,
2869                    GetStringInfoDatum(profile),(size_t)
2870                    GetStringInfoLength(profile),_exception);
2871                  profile=DestroyStringInfo(profile);
2872                }
2873              profile_info=DestroyImageInfo(profile_info);
2874              break;
2875            }
2876          ResetImageProfileIterator(profile_image);
2877          name=GetNextImageProfile(profile_image);
2878          while (name != (const char *) NULL)
2879          {
2880            profile=GetImageProfile(profile_image,name);
2881            if (profile != (StringInfo *) NULL)
2882              (void) ProfileImage(_image,name,GetStringInfoDatum(profile),
2883                (size_t) GetStringInfoLength(profile),_exception);
2884            name=GetNextImageProfile(profile_image);
2885          }
2886          profile_image=DestroyImage(profile_image);
2887          break;
2888        }
2889      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2890    }
2891    case 'r':
2892    {
2893      if (LocaleCompare("radial-blur",option+1) == 0)
2894        {
2895          flags=ParseGeometry(arg1,&geometry_info);
2896          if ((flags & RhoValue) == 0)
2897            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2898          new_image=RadialBlurImage(_image,geometry_info.rho,_exception);
2899          break;
2900        }
2901      if (LocaleCompare("raise",option+1) == 0)
2902        {
2903          if (IfMagickFalse(IsGeometry(arg1)))
2904            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2905          flags=ParsePageGeometry(_image,arg1,&geometry,_exception);
2906          if ((flags & SigmaValue) == 0)
2907            geometry.height=geometry.width;
2908          (void) RaiseImage(_image,&geometry,normal_op,_exception);
2909          break;
2910        }
2911      if (LocaleCompare("random-threshold",option+1) == 0)
2912        {
2913          if (IfMagickFalse(IsGeometry(arg1)))
2914            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2915          (void) RandomThresholdImage(_image,arg1,_exception);
2916          break;
2917        }
2918      if (LocaleCompare("recolor",option+1) == 0)
2919        {
2920          CLIWandWarnReplaced("-color-matrix");
2921          CLISimpleOperatorImage(cli_wand,"-color-matrix",arg1,NULL);
2922        }
2923      if (LocaleCompare("remap",option+1) == 0)
2924        {
2925          Image
2926            *remap_image;
2927
2928          remap_image=GetImageCache(_image_info,arg1,_exception);
2929          if (remap_image == (Image *) NULL)
2930            break;
2931          (void) RemapImage(_quantize_info,_image,remap_image,_exception);
2932          remap_image=DestroyImage(remap_image);
2933          break;
2934        }
2935      if (LocaleCompare("repage",option+1) == 0)
2936        {
2937          if (IfNormalOp)
2938            {
2939              if (IfMagickFalse(IsGeometry(arg1)))
2940                CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,
2941                  arg1);
2942              (void) ResetImagePage(_image,arg1);
2943            }
2944          else
2945            (void) ParseAbsoluteGeometry("0x0+0+0",&_image->page);
2946          break;
2947        }
2948      if (LocaleCompare("resample",option+1) == 0)
2949        {
2950          /* FUTURE: Roll into a resize special operation */
2951          flags=ParseGeometry(arg1,&geometry_info);
2952          if ((flags & (RhoValue|SigmaValue)) == 0)
2953            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2954          if ((flags & SigmaValue) == 0)
2955            geometry_info.sigma=geometry_info.rho;
2956          new_image=ResampleImage(_image,geometry_info.rho,
2957            geometry_info.sigma,_image->filter,_exception);
2958          break;
2959        }
2960      if (LocaleCompare("resize",option+1) == 0)
2961        {
2962          if (IfMagickFalse(IsGeometry(arg1)))
2963            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2964          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
2965          new_image=ResizeImage(_image,geometry.width,geometry.height,
2966            _image->filter,_exception);
2967          break;
2968        }
2969      if (LocaleCompare("roll",option+1) == 0)
2970        {
2971          if (IfMagickFalse(IsGeometry(arg1)))
2972            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2973          (void) ParsePageGeometry(_image,arg1,&geometry,_exception);
2974          new_image=RollImage(_image,geometry.x,geometry.y,_exception);
2975          break;
2976        }
2977      if (LocaleCompare("rotate",option+1) == 0)
2978        {
2979          flags=ParseGeometry(arg1,&geometry_info);
2980          if ((flags & RhoValue) == 0)
2981            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2982          if ((flags & GreaterValue) != 0 && (_image->columns <= _image->rows))
2983            break;
2984          if ((flags & LessValue) != 0 && (_image->columns >= _image->rows))
2985            break;
2986          new_image=RotateImage(_image,geometry_info.rho,_exception);
2987          break;
2988        }
2989      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
2990    }
2991    case 's':
2992    {
2993      if (LocaleCompare("sample",option+1) == 0)
2994        {
2995          /* FUTURE: Roll into a resize special operator */
2996          if (IfMagickFalse(IsGeometry(arg1)))
2997            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
2998          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
2999          new_image=SampleImage(_image,geometry.width,geometry.height,
3000            _exception);
3001          break;
3002        }
3003      if (LocaleCompare("scale",option+1) == 0)
3004        {
3005          /* FUTURE: Roll into a resize special operator */
3006          if (IfMagickFalse(IsGeometry(arg1)))
3007            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3008          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
3009          new_image=ScaleImage(_image,geometry.width,geometry.height,
3010            _exception);
3011          break;
3012        }
3013      if (LocaleCompare("segment",option+1) == 0)
3014        {
3015          flags=ParseGeometry(arg1,&geometry_info);
3016          if ((flags & (RhoValue|SigmaValue)) == 0)
3017            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3018          if ((flags & SigmaValue) == 0)
3019            geometry_info.sigma=1.0;
3020          (void) SegmentImage(_image,_image->colorspace,
3021            _image_info->verbose,geometry_info.rho,geometry_info.sigma,
3022            _exception);
3023          break;
3024        }
3025      if (LocaleCompare("selective-blur",option+1) == 0)
3026        {
3027          flags=ParseGeometry(arg1,&geometry_info);
3028          if ((flags & (RhoValue|SigmaValue)) == 0)
3029            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3030          if ((flags & SigmaValue) == 0)
3031            geometry_info.sigma=1.0;
3032          if ((flags & PercentValue) != 0)
3033            geometry_info.xi=(double) QuantumRange*geometry_info.xi/100.0;
3034          new_image=SelectiveBlurImage(_image,geometry_info.rho,
3035            geometry_info.sigma,geometry_info.xi,_exception);
3036          break;
3037        }
3038      if (LocaleCompare("separate",option+1) == 0)
3039        {
3040          /* WARNING: This can generate multiple images! */
3041          /* FUTURE - this may be replaced by a "-channel" method */
3042          new_image=SeparateImages(_image,_exception);
3043          break;
3044        }
3045      if (LocaleCompare("sepia-tone",option+1) == 0)
3046        {
3047          if (IfMagickFalse(IsGeometry(arg1)))
3048            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3049          new_image=SepiaToneImage(_image,StringToDoubleInterval(arg1,
3050                 (double) QuantumRange+1.0),_exception);
3051          break;
3052        }
3053      if (LocaleCompare("set",option+1) == 0)
3054        {
3055          char
3056            *value;
3057
3058          if (IfPlusOp) {
3059              if (LocaleNCompare(arg1,"registry:",9) == 0)
3060                (void) DeleteImageRegistry(arg1+9);
3061              else
3062                if (LocaleNCompare(arg1,"option:",7) == 0)
3063                  {
3064                    (void) DeleteImageOption(_image_info,arg1+7);
3065                    (void) DeleteImageArtifact(_image,arg1+7);
3066                  }
3067                else
3068                  (void) DeleteImageProperty(_image,arg1);
3069              break;
3070            }
3071          value=InterpretImageProperties(_image_info,_image,arg2,_exception);
3072          if (value == (char *) NULL)
3073            break;
3074          if (LocaleNCompare(arg1,"registry:",9) == 0)
3075            (void) SetImageRegistry(StringRegistryType,arg1+9,value,_exception);
3076          else
3077            if (LocaleNCompare(arg1,"option:",7) == 0)
3078              {
3079                (void) SetImageOption(_image_info,arg1+7,value);
3080                (void) SetImageArtifact(_image,arg1+7,value);
3081              }
3082            else
3083              (void) SetImageProperty(_image,arg1,value,_exception);
3084          value=DestroyString(value);
3085          break;
3086        }
3087      if (LocaleCompare("shade",option+1) == 0)
3088        {
3089          flags=ParseGeometry(arg1,&geometry_info);
3090          if (((flags & RhoValue) == 0) || ((flags & SigmaValue) == 0))
3091            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3092          new_image=ShadeImage(_image,normal_op,geometry_info.rho,
3093               geometry_info.sigma,_exception);
3094          break;
3095        }
3096      if (LocaleCompare("shadow",option+1) == 0)
3097        {
3098          flags=ParseGeometry(arg1,&geometry_info);
3099          if ((flags & (RhoValue|SigmaValue)) == 0)
3100            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3101          if ((flags & SigmaValue) == 0)
3102            geometry_info.sigma=1.0;
3103          if ((flags & XiValue) == 0)
3104            geometry_info.xi=4.0;
3105          if ((flags & PsiValue) == 0)
3106            geometry_info.psi=4.0;
3107          new_image=ShadowImage(_image,geometry_info.rho,geometry_info.sigma,
3108            (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t)
3109            ceil(geometry_info.psi-0.5),_exception);
3110          break;
3111        }
3112      if (LocaleCompare("sharpen",option+1) == 0)
3113        {
3114          flags=ParseGeometry(arg1,&geometry_info);
3115          if ((flags & (RhoValue|SigmaValue)) == 0)
3116            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3117          if ((flags & SigmaValue) == 0)
3118            geometry_info.sigma=1.0;
3119          if ((flags & XiValue) == 0)
3120            geometry_info.xi=0.0;
3121          new_image=SharpenImage(_image,geometry_info.rho,geometry_info.sigma,
3122           _exception);
3123          break;
3124        }
3125      if (LocaleCompare("shave",option+1) == 0)
3126        {
3127          if (IfMagickFalse(IsGeometry(arg1)))
3128            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3129          flags=ParsePageGeometry(_image,arg1,&geometry,_exception);
3130          new_image=ShaveImage(_image,&geometry,_exception);
3131          break;
3132        }
3133      if (LocaleCompare("shear",option+1) == 0)
3134        {
3135          flags=ParseGeometry(arg1,&geometry_info);
3136          if ((flags & RhoValue) == 0)
3137            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3138          if ((flags & SigmaValue) == 0)
3139            geometry_info.sigma=geometry_info.rho;
3140          new_image=ShearImage(_image,geometry_info.rho,
3141            geometry_info.sigma,_exception);
3142          break;
3143        }
3144      if (LocaleCompare("sigmoidal-contrast",option+1) == 0)
3145        {
3146          flags=ParseGeometry(arg1,&geometry_info);
3147          if ((flags & RhoValue) == 0)
3148            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3149          if ((flags & SigmaValue) == 0)
3150            geometry_info.sigma=(double) QuantumRange/2.0;
3151          if ((flags & PercentValue) != 0)
3152            geometry_info.sigma=(double) QuantumRange*geometry_info.sigma/
3153              100.0;
3154          (void) SigmoidalContrastImage(_image,normal_op,geometry_info.rho,
3155               geometry_info.sigma,_exception);
3156          break;
3157        }
3158      if (LocaleCompare("sketch",option+1) == 0)
3159        {
3160          flags=ParseGeometry(arg1,&geometry_info);
3161          if ((flags & (RhoValue|SigmaValue)) == 0)
3162            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3163          if ((flags & SigmaValue) == 0)
3164            geometry_info.sigma=1.0;
3165          new_image=SketchImage(_image,geometry_info.rho,
3166            geometry_info.sigma,geometry_info.xi,_exception);
3167          break;
3168        }
3169      if (LocaleCompare("solarize",option+1) == 0)
3170        {
3171          if (IfMagickFalse(IsGeometry(arg1)))
3172            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3173          (void) SolarizeImage(_image,StringToDoubleInterval(arg1,(double)
3174                 QuantumRange+1.0),_exception);
3175          break;
3176        }
3177      if (LocaleCompare("sparse-color",option+1) == 0)
3178        {
3179          char
3180            *arguments;
3181
3182          parse= ParseCommandOption(MagickSparseColorOptions,MagickFalse,arg1);
3183          if ( parse < 0 )
3184            CLIWandExceptArgBreak(OptionError,"UnrecognizedSparseColorMethod",
3185                option,arg1);
3186          arguments=InterpretImageProperties(_image_info,_image,arg2,_exception);
3187          if (arguments == (char *) NULL)
3188            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg2);
3189          new_image=SparseColorOption(_image,(SparseColorMethod)parse,
3190               arguments,_exception);
3191          arguments=DestroyString(arguments);
3192          break;
3193        }
3194      if (LocaleCompare("splice",option+1) == 0)
3195        {
3196          if (IfMagickFalse(IsGeometry(arg1)))
3197            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3198          flags=ParseGravityGeometry(_image,arg1,&geometry,_exception);
3199          new_image=SpliceImage(_image,&geometry,_exception);
3200          break;
3201        }
3202      if (LocaleCompare("spread",option+1) == 0)
3203        {
3204          flags=ParseGeometry(arg1,&geometry_info);
3205          if ((flags & RhoValue) == 0)
3206            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg2);
3207          new_image=SpreadImage(_image,geometry_info.rho,_image->interpolate,
3208               _exception);
3209          break;
3210        }
3211      if (LocaleCompare("statistic",option+1) == 0)
3212        {
3213          parse=ParseCommandOption(MagickStatisticOptions,MagickFalse,arg1);
3214          if ( parse < 0 )
3215            CLIWandExceptArgBreak(OptionError,"UnrecognizedStatisticType",
3216                 option,arg1);
3217          flags=ParseGeometry(arg2,&geometry_info);
3218          if ((flags & RhoValue) == 0)
3219            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg2);
3220          if ((flags & SigmaValue) == 0)
3221            geometry_info.sigma=geometry_info.rho;
3222          new_image=StatisticImage(_image,(StatisticType)parse,
3223               (size_t) geometry_info.rho,(size_t) geometry_info.sigma,
3224               _exception);
3225          break;
3226        }
3227      if (LocaleCompare("strip",option+1) == 0)
3228        {
3229          (void) StripImage(_image,_exception);
3230          break;
3231        }
3232      if (LocaleCompare("swirl",option+1) == 0)
3233        {
3234          flags=ParseGeometry(arg2,&geometry_info);
3235          if ((flags & RhoValue) == 0)
3236            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg2);
3237          new_image=SwirlImage(_image,geometry_info.rho,
3238            _image->interpolate,_exception);
3239          break;
3240        }
3241      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3242    }
3243    case 't':
3244    {
3245      if (LocaleCompare("threshold",option+1) == 0)
3246        {
3247          double
3248            threshold;
3249
3250          threshold=(double) QuantumRange/2;
3251          if (normal_op) {
3252            if (IfMagickFalse(IsGeometry(arg1)))
3253              CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3254            threshold=StringToDoubleInterval(arg1,(double) QuantumRange+1.0);
3255          }
3256          (void) BilevelImage(_image,threshold,_exception);
3257          break;
3258        }
3259      if (LocaleCompare("thumbnail",option+1) == 0)
3260        {
3261          if (IfMagickFalse(IsGeometry(arg1)))
3262            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3263          (void) ParseRegionGeometry(_image,arg1,&geometry,_exception);
3264          new_image=ThumbnailImage(_image,geometry.width,geometry.height,
3265            _exception);
3266          break;
3267        }
3268      if (LocaleCompare("tint",option+1) == 0)
3269        {
3270          if (IfMagickFalse(IsGeometry(arg1)))
3271            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3272          new_image=TintImage(_image,arg1,&_draw_info->fill,_exception);
3273          break;
3274        }
3275      if (LocaleCompare("transform",option+1) == 0)
3276        {
3277          CLIWandWarnReplaced("+distort AffineProjection");
3278          new_image=AffineTransformImage(_image,&_draw_info->affine,_exception);
3279          break;
3280        }
3281      if (LocaleCompare("transparent",option+1) == 0)
3282        {
3283          PixelInfo
3284            target;
3285
3286          (void) QueryColorCompliance(arg1,AllCompliance,&target,_exception);
3287          (void) TransparentPaintImage(_image,&target,(Quantum)
3288            TransparentAlpha,plus_alt_op,_exception);
3289          break;
3290        }
3291      if (LocaleCompare("transpose",option+1) == 0)
3292        {
3293          new_image=TransposeImage(_image,_exception);
3294          break;
3295        }
3296      if (LocaleCompare("transverse",option+1) == 0)
3297        {
3298          new_image=TransverseImage(_image,_exception);
3299          break;
3300        }
3301      if (LocaleCompare("trim",option+1) == 0)
3302        {
3303          new_image=TrimImage(_image,_exception);
3304          break;
3305        }
3306      if (LocaleCompare("type",option+1) == 0)
3307        {
3308          /* Note that "type" setting should have already been defined */
3309          (void) SetImageType(_image,_image_info->type,_exception);
3310          break;
3311        }
3312      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3313    }
3314    case 'u':
3315    {
3316      if (LocaleCompare("unique",option+1) == 0)
3317        {
3318          /* FUTURE: move to SyncImageSettings() and AcqireImage()???
3319             Option is not documented, bt appears to be for "identify".
3320             We may need a identify specific verbose!
3321          */
3322          if (plus_alt_op) {
3323              (void) DeleteImageArtifact(_image,"identify:unique-colors");
3324              break;
3325            }
3326          (void) SetImageArtifact(_image,"identify:unique-colors","true");
3327          (void) SetImageArtifact(_image,"verbose","true");
3328          break;
3329        }
3330      if (LocaleCompare("unique-colors",option+1) == 0)
3331        {
3332          new_image=UniqueImageColors(_image,_exception);
3333          break;
3334        }
3335      if (LocaleCompare("unsharp",option+1) == 0)
3336        {
3337          flags=ParseGeometry(arg1,&geometry_info);
3338          if ((flags & (RhoValue|SigmaValue)) == 0)
3339            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3340          if ((flags & SigmaValue) == 0)
3341            geometry_info.sigma=1.0;
3342          if ((flags & XiValue) == 0)
3343            geometry_info.xi=1.0;
3344          if ((flags & PsiValue) == 0)
3345            geometry_info.psi=0.05;
3346          new_image=UnsharpMaskImage(_image,geometry_info.rho,
3347            geometry_info.sigma,geometry_info.xi,geometry_info.psi,_exception);
3348          break;
3349        }
3350      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3351    }
3352    case 'v':
3353    {
3354      if (LocaleCompare("verbose",option+1) == 0)
3355        {
3356          /* FUTURE: move to SyncImageSettings() and AcquireImage()???
3357             three places!   ImageArtifact   ImageOption  _image_info->verbose
3358             Some how new images also get this artifact!
3359          */
3360          (void) SetImageArtifact(_image,option+1,
3361                           IfNormalOp ? "true" : "false" );
3362          break;
3363        }
3364      if (LocaleCompare("vignette",option+1) == 0)
3365        {
3366          flags=ParseGeometry(arg1,&geometry_info);
3367          if ((flags & (RhoValue|SigmaValue)) == 0)
3368            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3369          if ((flags & SigmaValue) == 0)
3370            geometry_info.sigma=1.0;
3371          if ((flags & XiValue) == 0)
3372            geometry_info.xi=0.1*_image->columns;
3373          if ((flags & PsiValue) == 0)
3374            geometry_info.psi=0.1*_image->rows;
3375          new_image=VignetteImage(_image,geometry_info.rho,geometry_info.sigma,
3376            (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t)
3377            ceil(geometry_info.psi-0.5),_exception);
3378          break;
3379        }
3380      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3381    }
3382    case 'w':
3383    {
3384      if (LocaleCompare("wave",option+1) == 0)
3385        {
3386          flags=ParseGeometry(arg1,&geometry_info);
3387          if ((flags & (RhoValue|SigmaValue)) == 0)
3388            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3389          if ((flags & SigmaValue) == 0)
3390            geometry_info.sigma=1.0;
3391          new_image=WaveImage(_image,geometry_info.rho,geometry_info.sigma,
3392               _image->interpolate,_exception);
3393          break;
3394        }
3395      if (LocaleCompare("white-threshold",option+1) == 0)
3396        {
3397          if (IfMagickFalse(IsGeometry(arg1)))
3398            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3399          (void) WhiteThresholdImage(_image,arg1,_exception);
3400          break;
3401        }
3402      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3403    }
3404    default:
3405      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3406  }
3407  /*
3408     Replace current image with any image that was generated
3409     and set image point to last image (so image->next is correct)
3410  */
3411  if (new_image != (Image *) NULL)
3412    ReplaceImageInListReturnLast(&_image,new_image);
3413
3414  return;
3415#undef _image_info
3416#undef _draw_info
3417#undef _quantize_info
3418#undef _image
3419#undef _exception
3420#undef IfNormalOp
3421#undef IfPlusOp
3422#undef normal_op
3423#undef plus_alt_op
3424}
3425
3426WandExport void CLISimpleOperatorImages(MagickCLI *cli_wand,
3427  const char *option, const char *arg1, const char *arg2)
3428{
3429  size_t
3430    n,
3431    i;
3432
3433  assert(cli_wand != (MagickCLI *) NULL);
3434  assert(cli_wand->signature == WandSignature);
3435  assert(cli_wand->wand.signature == WandSignature);
3436  assert(cli_wand->wand.images != (Image *) NULL); /* images must be present */
3437  if (IfMagickTrue(cli_wand->wand.debug))
3438    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
3439
3440#if !USE_WAND_METHODS
3441  /* FUTURE add appropriate tracing */
3442  i=0;
3443  n=GetImageListLength(cli_wand->wand.images);
3444  cli_wand->wand.images=GetFirstImageInList(cli_wand->wand.images);
3445  while (1) {
3446    i++;
3447    CLISimpleOperatorImage(cli_wand, option, arg1, arg2);
3448    if ( cli_wand->wand.images->next == (Image *) NULL )
3449      break;
3450    cli_wand->wand.images=cli_wand->wand.images->next;
3451  }
3452  assert( i == n );
3453  cli_wand->wand.images=GetFirstImageInList(cli_wand->wand.images);
3454#else
3455  MagickResetIterator(&cli_wand->wand);
3456  while ( IfMagickTrue(MagickNextImage(&cli_wand->wand)) )
3457    CLISimpleOperatorImage(cli_wand, option, arg1, arg2);
3458  MagickResetIterator(&cli_wand->wand);
3459#endif
3460  return;
3461}
3462
3463/*
3464%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3465%                                                                             %
3466%                                                                             %
3467%                                                                             %
3468+     C L I L i s t O p e r a t o r I m a g e s                               %
3469%                                                                             %
3470%                                                                             %
3471%                                                                             %
3472%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3473%
3474%  CLIListOperatorImages() applies a single operation that is apply to the
3475%  entire image list as a whole. The result is often a complete replacment
3476%  of the image list with a completely new list, or just a single image.
3477%
3478%  The format of the MogrifyImage method is:
3479%
3480%    void CLIListOperatorImages(MagickCLI *cli_wand,
3481%        const char *option, const char *arg1, const char *arg2)
3482%
3483%  A description of each parameter follows:
3484%
3485%    o cli_wand: structure holding settings to be applied
3486%
3487%    o option:  The option string for the operation
3488%
3489%    o arg1, arg2: optional argument strings to the operation
3490%        arg2 is currently not used
3491%
3492*/
3493WandExport void CLIListOperatorImages(MagickCLI *cli_wand,
3494     const char *option,const char *arg1, const char *magick_unused(arg2))
3495{
3496  ssize_t
3497    parse;
3498
3499  Image
3500    *new_images;
3501
3502#define _image_info     (cli_wand->wand.image_info)
3503#define _images         (cli_wand->wand.images)
3504#define _exception      (cli_wand->wand.exception)
3505#define _draw_info      (cli_wand->draw_info)
3506#define _quantize_info  (cli_wand->quantize_info)
3507#define IfNormalOp      (*option=='-')
3508#define IfPlusOp        (*option!='-')
3509#define normal_op       IsMagickTrue(IfNormalOp)
3510
3511  assert(cli_wand != (MagickCLI *) NULL);
3512  assert(cli_wand->signature == WandSignature);
3513  assert(cli_wand->wand.signature == WandSignature);
3514  assert(_images != (Image *) NULL);             /* _images must be present */
3515  if (IfMagickTrue(cli_wand->wand.debug))
3516    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
3517
3518  new_images=NewImageList();
3519
3520  switch (*(option+1))
3521  {
3522    case 'a':
3523    {
3524      if (LocaleCompare("append",option+1) == 0)
3525        {
3526          new_images=AppendImages(_images,normal_op,_exception);
3527          break;
3528        }
3529      if (LocaleCompare("average",option+1) == 0)
3530        {
3531          CLIWandWarnReplaced("-evaluate-sequence Mean");
3532          CLIListOperatorImages(cli_wand,"-evaluate-sequence","Mean",NULL);
3533          break;
3534        }
3535      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3536    }
3537    case 'c':
3538    {
3539      if (LocaleCompare("channel-fx",option+1) == 0)
3540        {
3541          new_images=ChannelFxImage(_images,arg1,_exception);
3542          break;
3543        }
3544      if (LocaleCompare("clut",option+1) == 0)
3545        {
3546          Image
3547            *clut_image;
3548
3549          /* FUTURE - make this a compose option, and thus can be used
3550             with layers compose or even compose last image over all other
3551             _images.
3552          */
3553          new_images=RemoveFirstImageFromList(&_images);
3554          clut_image=RemoveLastImageFromList(&_images);
3555          /* FUTURE - produce Exception, rather than silent fail */
3556          if (clut_image == (Image *) NULL)
3557            break;
3558          (void) ClutImage(new_images,clut_image,new_images->interpolate,_exception);
3559          clut_image=DestroyImage(clut_image);
3560          break;
3561        }
3562      if (LocaleCompare("coalesce",option+1) == 0)
3563        {
3564          new_images=CoalesceImages(_images,_exception);
3565          break;
3566        }
3567      if (LocaleCompare("combine",option+1) == 0)
3568        {
3569          /* FUTURE - this may be replaced by a 'channel' method */
3570          new_images=CombineImages(_images,_exception);
3571          break;
3572        }
3573      if (LocaleCompare("composite",option+1) == 0)
3574        {
3575          CompositeOperator
3576            compose;
3577
3578          const char*
3579            value;
3580
3581          MagickBooleanType
3582            clip_to_self;
3583
3584          Image
3585            *mask_image,
3586            *source_image;
3587
3588          RectangleInfo
3589            geometry;
3590
3591          /* Compose value from "-compose" option only */
3592          value=GetImageOption(_image_info,"compose");
3593          if (value == (const char *) NULL)
3594            compose=OverCompositeOp;  /* use Over not source_image->compose */
3595          else
3596            compose=(CompositeOperator) ParseCommandOption(MagickComposeOptions,
3597              MagickFalse,value);
3598
3599          /* Get "clip-to-self" expert setting (false is normal) */
3600          value=GetImageOption(_image_info,"compose:clip-to-self");
3601          if (value == (const char *) NULL)
3602            clip_to_self=MagickTrue;
3603          else
3604            clip_to_self=IsStringTrue(GetImageOption(_image_info,
3605              "compose:clip-to-self")); /* if this is true */
3606          value=GetImageOption(_image_info,"compose:outside-overlay");
3607          if (value != (const char *) NULL) {   /* or this false */
3608            /* FUTURE: depreciate warning for "compose:outside-overlay"*/
3609            clip_to_self= IsMagickFalse(IsStringNotFalse(value));
3610          }
3611
3612          new_images=RemoveFirstImageFromList(&_images);
3613          source_image=RemoveFirstImageFromList(&_images);
3614          if (source_image == (Image *) NULL)
3615            break; /* FUTURE - produce Exception, rather than silent fail */
3616
3617          /* FUTURE - this should not be here! - should be part of -geometry */
3618          (void) TransformImage(&source_image,(char *) NULL,
3619            source_image->geometry,_exception);
3620
3621          SetGeometry(source_image,&geometry);
3622          (void) ParseAbsoluteGeometry(source_image->geometry,&geometry);
3623          GravityAdjustGeometry(new_images->columns,new_images->rows,
3624               new_images->gravity, &geometry);
3625
3626          mask_image=RemoveFirstImageFromList(&_images);
3627          if (mask_image != (Image *) NULL)
3628            { /* handle a third write mask image */
3629              if ((compose == DisplaceCompositeOp) ||
3630                  (compose == DistortCompositeOp)) {
3631                /* Merge Y displacement into X displace/distort map. */
3632                (void) CompositeImage(source_image,mask_image,
3633                  CopyGreenCompositeOp,MagickTrue,0,0,_exception);
3634                mask_image=DestroyImage(mask_image);
3635              }
3636              else {
3637                /* Set a blending mask for the composition.  */
3638                (void) NegateImage(mask_image,MagickFalse,_exception);
3639                (void) SetImageMask(new_images,mask_image,_exception);
3640                mask_image=DestroyImage(mask_image);
3641              }
3642            }
3643          (void) CompositeImage(new_images,source_image,compose,clip_to_self,
3644            geometry.x,geometry.y,_exception);
3645          (void) SetImageMask(new_images,(Image *) NULL,_exception);
3646          source_image=DestroyImage(source_image);
3647          break;
3648        }
3649      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3650    }
3651    case 'd':
3652    {
3653      if (LocaleCompare("deconstruct",option+1) == 0)
3654        {
3655          CLIWandWarnReplaced("-layer CompareAny");
3656          CLIListOperatorImages(cli_wand,"-layer","CompareAny",NULL);
3657          break;
3658        }
3659      if (LocaleCompare("delete",option+1) == 0)
3660        {
3661          if (IfNormalOp)
3662            DeleteImages(&_images,arg1,_exception);
3663          else
3664            DeleteImages(&_images,"-1",_exception);
3665          break;
3666        }
3667      if (LocaleCompare("duplicate",option+1) == 0)
3668        {
3669          if (IfNormalOp)
3670            {
3671              const char
3672                *p;
3673
3674              size_t
3675                number_duplicates;
3676
3677              if (IfMagickFalse(IsGeometry(arg1)))
3678                CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,
3679                      arg1);
3680              number_duplicates=(size_t) StringToLong(arg1);
3681              p=strchr(arg1,',');
3682              if (p == (const char *) NULL)
3683                new_images=DuplicateImages(_images,number_duplicates,"-1",
3684                  _exception);
3685              else
3686                new_images=DuplicateImages(_images,number_duplicates,p,
3687                  _exception);
3688            }
3689          else
3690            new_images=DuplicateImages(_images,1,"-1",_exception);
3691          AppendImageToList(&_images, new_images);
3692          new_images=(Image *)NULL;
3693          break;
3694        }
3695      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3696    }
3697    case 'e':
3698    {
3699      if (LocaleCompare("evaluate-sequence",option+1) == 0)
3700        {
3701          parse = ParseCommandOption(MagickEvaluateOptions,MagickFalse,arg1);
3702          if ( parse < 0 )
3703            CLIWandExceptArgBreak(OptionError,"UnrecognizedEvaluateOperator",
3704                 option,arg1);
3705          new_images=EvaluateImages(_images,(MagickEvaluateOperator)parse,
3706               _exception);
3707          break;
3708        }
3709      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3710    }
3711    case 'f':
3712    {
3713      if (LocaleCompare("fft",option+1) == 0)
3714        {
3715          new_images=ForwardFourierTransformImage(_images,normal_op,_exception);
3716          break;
3717        }
3718      if (LocaleCompare("flatten",option+1) == 0)
3719        {
3720          /* REDIRECTED to use -layers flatten instead */
3721          CLIListOperatorImages(cli_wand,"-layers",option+1,NULL);
3722          break;
3723        }
3724      if (LocaleCompare("fx",option+1) == 0)
3725        {
3726          new_images=FxImage(_images,arg1,_exception);
3727          break;
3728        }
3729      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3730    }
3731    case 'h':
3732    {
3733      if (LocaleCompare("hald-clut",option+1) == 0)
3734        {
3735          /* FUTURE - make this a compose option (and thus layers compose )
3736             or perhaps compose last image over all other _images.
3737          */
3738          Image
3739            *hald_image;
3740
3741          new_images=RemoveFirstImageFromList(&_images);
3742          hald_image=RemoveLastImageFromList(&_images);
3743          if (hald_image == (Image *) NULL)
3744            break;
3745          (void) HaldClutImage(new_images,hald_image,_exception);
3746          hald_image=DestroyImage(hald_image);
3747          break;
3748        }
3749      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3750    }
3751    case 'i':
3752    {
3753      if (LocaleCompare("ift",option+1) == 0)
3754        {
3755          Image
3756            *magnitude_image,
3757            *phase_image;
3758
3759           magnitude_image=RemoveFirstImageFromList(&_images);
3760           phase_image=RemoveFirstImageFromList(&_images);
3761          /* FUTURE - produce Exception, rather than silent fail */
3762           if (phase_image == (Image *) NULL)
3763             break;
3764           new_images=InverseFourierTransformImage(magnitude_image,phase_image,
3765                   normal_op,_exception);
3766           magnitude_image=DestroyImage(magnitude_image);
3767           phase_image=DestroyImage(phase_image);
3768          break;
3769        }
3770      if (LocaleCompare("insert",option+1) == 0)
3771        {
3772          Image
3773            *insert_image,
3774            *index_image;
3775
3776          ssize_t
3777            index;
3778
3779          if (IfNormalOp && IfMagickFalse(IsGeometry(arg1)))
3780            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3781          index=0;
3782          insert_image=RemoveLastImageFromList(&_images);
3783          if (IfNormalOp)
3784            index=(ssize_t) StringToLong(arg1);
3785          index_image=insert_image;
3786          if (index == 0)
3787            PrependImageToList(&_images,insert_image);
3788          else if (index == (ssize_t) GetImageListLength(_images))
3789            AppendImageToList(&_images,insert_image);
3790          else
3791            {
3792               index_image=GetImageFromList(_images,index-1);
3793               if (index_image == (Image *) NULL)
3794                 CLIWandExceptArgBreak(OptionError,"NoSuchImage",option,arg1);
3795              InsertImageInList(&index_image,insert_image);
3796            }
3797          _images=GetFirstImageInList(index_image);
3798          break;
3799        }
3800      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3801    }
3802    case 'l':
3803    {
3804      if (LocaleCompare("layers",option+1) == 0)
3805        {
3806          parse=ParseCommandOption(MagickLayerOptions,MagickFalse,arg1);
3807          if ( parse < 0 )
3808            CLIWandExceptArgBreak(OptionError,"UnrecognizedLayerMethod",
3809                 option,arg1);
3810          switch ((ImageLayerMethod) parse)
3811          {
3812            case CoalesceLayer:
3813            {
3814              new_images=CoalesceImages(_images,_exception);
3815              break;
3816            }
3817            case CompareAnyLayer:
3818            case CompareClearLayer:
3819            case CompareOverlayLayer:
3820            default:
3821            {
3822              new_images=CompareImagesLayers(_images,(ImageLayerMethod) parse,
3823                   _exception);
3824              break;
3825            }
3826            case MergeLayer:
3827            case FlattenLayer:
3828            case MosaicLayer:
3829            case TrimBoundsLayer:
3830            {
3831              new_images=MergeImageLayers(_images,(ImageLayerMethod) parse,
3832                   _exception);
3833              break;
3834            }
3835            case DisposeLayer:
3836            {
3837              new_images=DisposeImages(_images,_exception);
3838              break;
3839            }
3840            case OptimizeImageLayer:
3841            {
3842              new_images=OptimizeImageLayers(_images,_exception);
3843              break;
3844            }
3845            case OptimizePlusLayer:
3846            {
3847              new_images=OptimizePlusImageLayers(_images,_exception);
3848              break;
3849            }
3850            case OptimizeTransLayer:
3851            {
3852              OptimizeImageTransparency(_images,_exception);
3853              break;
3854            }
3855            case RemoveDupsLayer:
3856            {
3857              RemoveDuplicateLayers(&_images,_exception);
3858              break;
3859            }
3860            case RemoveZeroLayer:
3861            {
3862              RemoveZeroDelayLayers(&_images,_exception);
3863              break;
3864            }
3865            case OptimizeLayer:
3866            { /* General Purpose, GIF Animation Optimizer.  */
3867              new_images=CoalesceImages(_images,_exception);
3868              if (new_images == (Image *) NULL)
3869                break;
3870              _images=DestroyImageList(_images);
3871              _images=OptimizeImageLayers(new_images,_exception);
3872              if (_images == (Image *) NULL)
3873                break;
3874              new_images=DestroyImageList(new_images);
3875              OptimizeImageTransparency(_images,_exception);
3876              (void) RemapImages(_quantize_info,_images,(Image *) NULL,
3877                _exception);
3878              break;
3879            }
3880            case CompositeLayer:
3881            {
3882              Image
3883                *source;
3884
3885              RectangleInfo
3886                geometry;
3887
3888              CompositeOperator
3889                compose;
3890
3891              const char*
3892                value;
3893
3894              value=GetImageOption(_image_info,"compose");
3895              compose=OverCompositeOp;  /* Default to Over */
3896              if (value != (const char *) NULL)
3897                compose=(CompositeOperator) ParseCommandOption(
3898                      MagickComposeOptions,MagickFalse,value);
3899
3900              /* Split image sequence at the first 'NULL:' image. */
3901              source=_images;
3902              while (source != (Image *) NULL)
3903              {
3904                source=GetNextImageInList(source);
3905                if ((source != (Image *) NULL) &&
3906                    (LocaleCompare(source->magick,"NULL") == 0))
3907                  break;
3908              }
3909              if (source != (Image *) NULL)
3910                {
3911                  if ((GetPreviousImageInList(source) == (Image *) NULL) ||
3912                      (GetNextImageInList(source) == (Image *) NULL))
3913                    source=(Image *) NULL;
3914                  else
3915                    { /* Separate the two lists, junk the null: image.  */
3916                      source=SplitImageList(source->previous);
3917                      DeleteImageFromList(&source);
3918                    }
3919                }
3920              if (source == (Image *) NULL)
3921                {
3922                  (void) ThrowMagickException(_exception,GetMagickModule(),
3923                    OptionError,"MissingNullSeparator","layers Composite");
3924                  break;
3925                }
3926              /* Adjust offset with gravity and virtual canvas.  */
3927              SetGeometry(_images,&geometry);
3928              (void) ParseAbsoluteGeometry(_images->geometry,&geometry);
3929              geometry.width=source->page.width != 0 ?
3930                source->page.width : source->columns;
3931              geometry.height=source->page.height != 0 ?
3932               source->page.height : source->rows;
3933              GravityAdjustGeometry(_images->page.width != 0 ?
3934                _images->page.width : _images->columns,
3935                _images->page.height != 0 ? _images->page.height :
3936                _images->rows,_images->gravity,&geometry);
3937
3938              /* Compose the two image sequences together */
3939              CompositeLayers(_images,compose,source,geometry.x,geometry.y,
3940                _exception);
3941              source=DestroyImageList(source);
3942              break;
3943            }
3944          }
3945          break;
3946        }
3947      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3948    }
3949    case 'm':
3950    {
3951      if (LocaleCompare("map",option+1) == 0)
3952        {
3953          CLIWandWarnReplaced("+remap");
3954          (void) RemapImages(_quantize_info,_images,(Image *) NULL,_exception);
3955          break;
3956        }
3957      if (LocaleCompare("morph",option+1) == 0)
3958        {
3959          Image
3960            *morph_image;
3961
3962          if (IfMagickFalse(IsGeometry(arg1)))
3963            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
3964          morph_image=MorphImages(_images,StringToUnsignedLong(arg1),
3965            _exception);
3966          if (morph_image == (Image *) NULL)
3967            break;
3968          _images=DestroyImageList(_images);
3969          _images=morph_image;
3970          break;
3971        }
3972      if (LocaleCompare("mosaic",option+1) == 0)
3973        {
3974          /* REDIRECTED to use -layers mosaic instead */
3975          CLIListOperatorImages(cli_wand,"-layers",option+1,NULL);
3976          break;
3977        }
3978      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
3979    }
3980    case 'p':
3981    {
3982      if (LocaleCompare("print",option+1) == 0)
3983        {
3984          char
3985            *string;
3986
3987          string=InterpretImageProperties(_image_info,_images,arg1,_exception);
3988          if (string == (char *) NULL)
3989            break;
3990          (void) FormatLocaleFile(stdout,"%s",string);
3991          string=DestroyString(string);
3992          break;
3993        }
3994      if (LocaleCompare("process",option+1) == 0)
3995        {
3996          /* FUTURE: better parsing using ScriptToken() from string ??? */
3997          char
3998            **arguments;
3999
4000          int
4001            j,
4002            number_arguments;
4003
4004          arguments=StringToArgv(arg1,&number_arguments);
4005          if (arguments == (char **) NULL)
4006            break;
4007          if (strchr(arguments[1],'=') != (char *) NULL)
4008            {
4009              char
4010                breaker,
4011                quote,
4012                *token;
4013
4014              const char
4015                *arguments;
4016
4017              int
4018                next,
4019                status;
4020
4021              size_t
4022                length;
4023
4024              TokenInfo
4025                *token_info;
4026
4027              /*
4028                Support old style syntax, filter="-option arg1".
4029              */
4030              length=strlen(arg1);
4031              token=(char *) NULL;
4032              if (~length >= (MaxTextExtent-1))
4033                token=(char *) AcquireQuantumMemory(length+MaxTextExtent,
4034                  sizeof(*token));
4035              if (token == (char *) NULL)
4036                break;
4037              next=0;
4038              arguments=arg1;
4039              token_info=AcquireTokenInfo();
4040              status=Tokenizer(token_info,0,token,length,arguments,"","=",
4041                "\"",'\0',&breaker,&next,&quote);
4042              token_info=DestroyTokenInfo(token_info);
4043              if (status == 0)
4044                {
4045                  const char
4046                    *argv;
4047
4048                  argv=(&(arguments[next]));
4049                  (void) InvokeDynamicImageFilter(token,&_images,1,&argv,
4050                    _exception);
4051                }
4052              token=DestroyString(token);
4053              break;
4054            }
4055          (void) SubstituteString(&arguments[1],"-","");
4056          (void) InvokeDynamicImageFilter(arguments[1],&_images,
4057            number_arguments-2,(const char **) arguments+2,_exception);
4058          for (j=0; j < number_arguments; j++)
4059            arguments[j]=DestroyString(arguments[j]);
4060          arguments=(char **) RelinquishMagickMemory(arguments);
4061          break;
4062        }
4063      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
4064    }
4065    case 'r':
4066    {
4067      if (LocaleCompare("remap",option+1) == 0)
4068        {
4069          (void) RemapImages(_quantize_info,_images,(Image *) NULL,_exception);
4070          break;
4071        }
4072      if (LocaleCompare("reverse",option+1) == 0)
4073        {
4074          ReverseImageList(&_images);
4075          break;
4076        }
4077      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
4078    }
4079    case 's':
4080    {
4081      if (LocaleCompare("smush",option+1) == 0)
4082        {
4083          /* FUTURE: this option needs more work to make better */
4084          ssize_t
4085            offset;
4086
4087          if (IfMagickFalse(IsGeometry(arg1)))
4088            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
4089          offset=(ssize_t) StringToLong(arg1);
4090          new_images=SmushImages(_images,normal_op,offset,_exception);
4091          break;
4092        }
4093      if (LocaleCompare("subimage",option+1) == 0)
4094        {
4095          Image
4096            *base_image,
4097            *compare_image;
4098
4099          const char *
4100            value;
4101
4102          MetricType
4103            metric;
4104
4105          double
4106            similarity;
4107
4108          RectangleInfo
4109            offset;
4110
4111          base_image=GetImageFromList(_images,0);
4112          compare_image=GetImageFromList(_images,1);
4113
4114          /* Comparision Metric */
4115          metric=UndefinedMetric;
4116          value=GetImageOption(_image_info,"metric");
4117          if (value != (const char *) NULL)
4118            metric=(MetricType) ParseCommandOption(MagickMetricOptions,
4119              MagickFalse,value);
4120
4121          new_images=SimilarityImage(base_image,compare_image,metric,
4122               &offset,&similarity,_exception);
4123
4124          if ( new_images != (Image *)NULL ) {
4125            char
4126              result[MaxTextExtent];
4127
4128            (void) FormatLocaleString(result,MaxTextExtent,"%lf",similarity);
4129            (void) SetImageProperty(new_images,"subimage:similarity",result,
4130                 _exception);
4131            (void) FormatLocaleString(result,MaxTextExtent,"%+ld",
4132                (long) offset.x);
4133            (void) SetImageProperty(new_images,"subimage:x",result,
4134                 _exception);
4135            (void) FormatLocaleString(result,MaxTextExtent,"%+ld",
4136                (long) offset.y);
4137            (void) SetImageProperty(new_images,"subimage:y",result,
4138                 _exception);
4139            (void) FormatLocaleString(result,MaxTextExtent,"%lux%lu%+ld%+ld",
4140                (unsigned long) offset.width,(unsigned long) offset.height,
4141                (long) offset.x,(long) offset.y);
4142            (void) SetImageProperty(new_images,"subimage:offset",result,
4143                 _exception);
4144          }
4145          break;
4146        }
4147      if (LocaleCompare("swap",option+1) == 0) {
4148        Image
4149          *p,
4150          *q,
4151          *swap;
4152
4153        ssize_t
4154          index,
4155          swap_index;
4156
4157        index=-1;
4158        swap_index=-2;
4159        if (IfNormalOp) {
4160          GeometryInfo
4161            geometry_info;
4162
4163          MagickStatusType
4164            flags;
4165
4166          swap_index=(-1);
4167          flags=ParseGeometry(arg1,&geometry_info);
4168          if ((flags & RhoValue) != 0)
4169            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
4170          index=(ssize_t) geometry_info.rho;
4171          if ((flags & SigmaValue) != 0)
4172            swap_index=(ssize_t) geometry_info.sigma;
4173        }
4174        p=GetImageFromList(_images,index);
4175        q=GetImageFromList(_images,swap_index);
4176        if ((p == (Image *) NULL) || (q == (Image *) NULL)) {
4177          if (IfNormalOp)
4178            CLIWandExceptArgBreak(OptionError,"InvalidImageIndex",option,arg1)
4179          else
4180            CLIWandExceptionBreak(OptionError,"TwoOrMoreImagesRequired",option);
4181        }
4182        if (p == q)
4183          CLIWandExceptArgBreak(OptionError,"InvalidImageIndex",option,arg1);
4184        swap=CloneImage(p,0,0,MagickTrue,_exception);
4185        ReplaceImageInList(&p,CloneImage(q,0,0,MagickTrue,_exception));
4186        ReplaceImageInList(&q,swap);
4187        _images=GetFirstImageInList(q);
4188        break;
4189      }
4190      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
4191    }
4192    default:
4193      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
4194  }
4195  if (new_images == (Image *) NULL)
4196    return;
4197
4198  if (_images != (Image *) NULL)
4199    _images=DestroyImageList(_images);
4200  _images=GetFirstImageInList(new_images);
4201  return;
4202
4203#undef _image_info
4204#undef _images
4205#undef _exception
4206#undef _draw_info
4207#undef _quantize_info
4208#undef IfNormalOp
4209#undef IfPlusOp
4210#undef normal_op
4211}
4212
4213/*
4214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4215%                                                                             %
4216%                                                                             %
4217%                                                                             %
4218+   C L I S p e c i a l O p e r a t i o n s                                   %
4219%                                                                             %
4220%                                                                             %
4221%                                                                             %
4222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4223%
4224%  CLINoImageOperator() Applies operations that may not actually need images
4225%  in an image list wen it is applied.
4226%
4227%  The classic operators of this type is -read, which actually creates images
4228%  even when no images are present.  Or image stack operators, which can be
4229%  applied to empty image lists.
4230%
4231%  Note: unlike other Operators, these may involve other special 'option'
4232%  character prefixes, other than simply '-' or '+'.
4233%
4234%  The format of the CLINoImageOption method is:
4235%
4236%      void CLINoImageOption(MagickCLI *cli_wand,const char *option,
4237%           const char *arg1, const char *arg2)
4238%
4239%  A description of each parameter follows:
4240%
4241%    o cli_wand: the main CLI Wand to use.
4242%
4243%    o option: The special option (with any switch char) to process
4244%
4245%    o arg1 & arg2: Argument for option, if required
4246%                   Currently arg2 is not used.
4247%
4248*/
4249WandExport void CLINoImageOperator(MagickCLI *cli_wand,
4250  const char *option, const char *arg1, const char *magick_unused(arg2))
4251{
4252#define _image_info     (cli_wand->wand.image_info)
4253#define _images         (cli_wand->wand.images)
4254#define _exception      (cli_wand->wand.exception)
4255#define IfNormalOp      (*option=='-')
4256#define IfPlusOp        (*option!='-')
4257
4258  assert(cli_wand != (MagickCLI *) NULL);
4259  assert(cli_wand->signature == WandSignature);
4260  assert(cli_wand->wand.signature == WandSignature);
4261  if (IfMagickTrue(cli_wand->wand.debug))
4262    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
4263
4264  /*
4265    No-op options  (ignore these)
4266  */
4267  if (LocaleCompare("noop",option+1) == 0)   /* no argument */
4268    return;
4269  if (LocaleCompare("sans",option+1) == 0)   /* one argument */
4270    return;
4271  if (LocaleCompare("sans0",option+1) == 0)  /* no argument */
4272    return;
4273  if (LocaleCompare("sans2",option+1) == 0)  /* two arguments */
4274    return;
4275  /*
4276    Image Reading
4277  */
4278  if ( ( LocaleCompare("read",option+1) == 0 ) ||
4279     ( LocaleCompare("--",option) == 0 ) ) {
4280#if 0
4281    /* Directly read 'arg1' without filename expansion handling (see below).
4282       This does NOT turn off the 'coder:' prefix, or '[...]' read modifiers.
4283    */
4284# if !USE_WAND_METHODS
4285    Image    *new_images;
4286
4287    if (IfMagickTrue(_image_info->ping))
4288      new_images=PingImages(_image_info,arg1,_exception);
4289    else
4290      new_images=ReadImages(_image_info,arg1,_exception);
4291    AppendImageToList(&_images, new_images);
4292# else
4293    /* read images using MagickWand method - no ping */
4294    /* This is not working! - it locks up in a CPU loop! */
4295    MagickSetLastIterator(&cli_wand->wand);
4296    MagickReadImage(&cli_wand->wand,arg1);
4297    MagickSetFirstIterator(&cli_wand->wand);
4298# endif
4299#else
4300    /* Do Filename Expansion for 'arg1' then read all images.
4301     *
4302     * Expansion handles '@', '~', '*', and '?' meta-characters while ignoring
4303     * (but attaching to generated argument list) any [...] read modifiers
4304     * that may be present.
4305     *
4306     * For example: correctly expand '*.gif[20x20]' into a list such as
4307     * 'abc.gif[20x20',  'foobar.gif[20x20]',  'xyzzy.gif[20x20]'
4308     *
4309     * NOTE: In IMv6 this was done globally across all images. This
4310     * meant you could include IM options in '@filename' lists, but you
4311     * could not include comments.   Doing it only for image read makes
4312     * it far more secure.
4313     */
4314    int      argc;
4315    char     **argv;
4316    ssize_t  i;
4317
4318    argc = 1;
4319    argv = (char **) &arg1;
4320
4321    if (IfMagickFalse(ExpandFilenames(&argc,&argv)))
4322      CLIWandExceptArgReturn(ResourceLimitError,"MemoryAllocationFailed",
4323          option,GetExceptionMessage(errno));
4324
4325    /* loop over expanded filename list, and read then all in */
4326    for (i=0; i<argc; i++) {
4327      Image *
4328        new_images;
4329#if 1
4330fprintf(stderr, "DEBUG: Reading image: \"%s\"\n", argv[i]);
4331#endif
4332      if (IfMagickTrue(_image_info->ping))
4333        new_images=PingImages(_image_info,argv[i],_exception);
4334      else
4335        new_images=ReadImages(_image_info,argv[i],_exception);
4336      AppendImageToList(&_images, new_images);
4337    }
4338    /* FUTURE: how do I free the expanded filename array memory ??? */
4339    //argv=DestroyStringList(argv);  /* Is this correct? */
4340#endif
4341    return;
4342  }
4343  /*
4344    Image Writing
4345    Note: Writing a empty image list is valid in specific cases
4346  */
4347  if (LocaleCompare("write",option+1) == 0) {
4348    char
4349      key[MaxTextExtent];
4350
4351    Image
4352      *write_images;
4353
4354    ImageInfo
4355      *write_info;
4356
4357    /* Need images, unless a "null:" output coder is used */
4358    if ( cli_wand->wand.images == (Image *) NULL ) {
4359      if ( LocaleCompare(arg1,"null:") == 0 )
4360        return;
4361      CLIWandExceptArgReturn(OptionError,"NoImagesForWrite",option,arg1);
4362    }
4363
4364    (void) FormatLocaleString(key,MaxTextExtent,"cache:%s",arg1);
4365    (void) DeleteImageRegistry(key);
4366    write_images=_images;
4367    if (IfPlusOp)
4368      write_images=CloneImageList(_images,_exception);
4369    write_info=CloneImageInfo(_image_info);
4370    (void) WriteImages(write_info,write_images,arg1,_exception);
4371    write_info=DestroyImageInfo(write_info);
4372    if (IfPlusOp)
4373      write_images=DestroyImageList(write_images);
4374    return;
4375  }
4376  /*
4377    Parenthesis and Brace operations
4378  */
4379  if (LocaleCompare("(",option) == 0) {
4380    /* stack 'push' images */
4381    Stack
4382      *node;
4383
4384    size_t
4385      size;
4386
4387    size=0;
4388    node=cli_wand->image_list_stack;
4389    for ( ; node != (Stack *)NULL; node=node->next)
4390      size++;
4391    if ( size >= MAX_STACK_DEPTH )
4392      CLIWandExceptionReturn(OptionError,"ParenthesisNestedTooDeeply",option);
4393    node=(Stack *) AcquireMagickMemory(sizeof(*node));
4394    if (node == (Stack *) NULL)
4395      CLIWandExceptionReturn(ResourceLimitFatalError,
4396           "MemoryAllocationFailed",option);
4397    node->data = (void *)cli_wand->wand.images;
4398    cli_wand->wand.images = NewImageList();
4399    node->next = cli_wand->image_list_stack;
4400    cli_wand->image_list_stack = node;
4401
4402    /* handle respect-parenthesis */
4403    if (IfMagickTrue(IsStringTrue(GetImageOption(cli_wand->wand.image_info,
4404                  "respect-parenthesis"))))
4405      option="{"; /* fall-thru so as to push image settings too */
4406    else
4407      return;
4408  }
4409  if (LocaleCompare("{",option) == 0) {
4410    /* stack 'push' of image_info settings */
4411    Stack
4412      *node;
4413
4414    size_t
4415      size;
4416
4417    size=0;
4418    node=cli_wand->image_info_stack;
4419    for ( ; node != (Stack *)NULL; node=node->next)
4420      size++;
4421    if ( size >= MAX_STACK_DEPTH )
4422      CLIWandExceptionReturn(OptionError,"CurlyBracesNestedTooDeeply",option);
4423    node=(Stack *) AcquireMagickMemory(sizeof(*node));
4424    if (node == (Stack *) NULL)
4425      CLIWandExceptionReturn(ResourceLimitFatalError,
4426           "MemoryAllocationFailed",option);
4427
4428    node->data = (void *)cli_wand->wand.image_info;
4429    cli_wand->wand.image_info = CloneImageInfo(cli_wand->wand.image_info);
4430    if (cli_wand->wand.image_info == (ImageInfo *)NULL) {
4431      CLIWandException(ResourceLimitFatalError,"MemoryAllocationFailed",
4432           option);
4433      cli_wand->wand.image_info = (ImageInfo *)node->data;
4434      node = (Stack *)RelinquishMagickMemory(node);
4435      return;
4436    }
4437
4438    node->next = cli_wand->image_info_stack;
4439    cli_wand->image_info_stack = node;
4440
4441    return;
4442  }
4443  if (LocaleCompare(")",option) == 0) {
4444    /* pop images from stack */
4445    Stack
4446      *node;
4447
4448    node = (Stack *)cli_wand->image_list_stack;
4449    if ( node == (Stack *)NULL)
4450      CLIWandExceptionReturn(OptionError,"UnbalancedParenthesis",option);
4451    cli_wand->image_list_stack = node->next;
4452
4453    AppendImageToList((Image **)&node->data,cli_wand->wand.images);
4454    cli_wand->wand.images= (Image *)node->data;
4455    node = (Stack *)RelinquishMagickMemory(node);
4456
4457    /* handle respect-parenthesis - of the previous 'pushed' settings */
4458    node = cli_wand->image_info_stack;
4459    if ( node != (Stack *)NULL)
4460      {
4461        if (IfMagickTrue(IsStringTrue(GetImageOption(
4462               cli_wand->wand.image_info,"respect-parenthesis"))))
4463          option="}"; /* fall-thru so as to pop image settings too */
4464        else
4465          return;
4466      }
4467    else
4468      return;
4469  }
4470  if (LocaleCompare("}",option) == 0) {
4471    /* pop image_info settings from stack */
4472    Stack
4473      *node;
4474
4475    node = (Stack *)cli_wand->image_info_stack;
4476    if ( node == (Stack *)NULL)
4477      CLIWandExceptionReturn(OptionError,"UnbalancedCurlyBraces",option);
4478    cli_wand->image_info_stack = node->next;
4479
4480    (void) DestroyImageInfo(cli_wand->wand.image_info);
4481    cli_wand->wand.image_info = (ImageInfo *)node->data;
4482    node = (Stack *)RelinquishMagickMemory(node);
4483
4484    GetDrawInfo(cli_wand->wand.image_info, cli_wand->draw_info);
4485    cli_wand->quantize_info=DestroyQuantizeInfo(cli_wand->quantize_info);
4486    cli_wand->quantize_info=AcquireQuantizeInfo(cli_wand->wand.image_info);
4487
4488    return;
4489  }
4490  if (LocaleCompare("clone",option+1) == 0) {
4491      Image
4492        *new_images;
4493
4494      if (*option == '+')
4495        arg1="-1";
4496      if (IfMagickFalse(IsSceneGeometry(arg1,MagickFalse)))
4497        CLIWandExceptionReturn(OptionError,"InvalidArgument",option);
4498      if ( cli_wand->image_list_stack == (Stack *)NULL)
4499        CLIWandExceptionReturn(OptionError,"UnableToCloneImage",option);
4500      new_images = (Image *)cli_wand->image_list_stack->data;
4501      if (new_images == (Image *) NULL)
4502        CLIWandExceptionReturn(OptionError,"UnableToCloneImage",option);
4503      new_images=CloneImages(new_images,arg1,_exception);
4504      if (new_images == (Image *) NULL)
4505        CLIWandExceptionReturn(OptionError,"NoSuchImage",option);
4506      AppendImageToList(&_images,new_images);
4507      return;
4508    }
4509  /*
4510    Informational Operations
4511  */
4512  if (LocaleCompare("version",option+1) == 0) {
4513    (void) FormatLocaleFile(stdout,"Version: %s\n",
4514      GetMagickVersion((size_t *) NULL));
4515    (void) FormatLocaleFile(stdout,"Copyright: %s\n",
4516      GetMagickCopyright());
4517    (void) FormatLocaleFile(stdout,"Features: %s\n\n",
4518      GetMagickFeatures());
4519    return;
4520  }
4521  if (LocaleCompare("list",option+1) == 0) {
4522    /* FUTURE: This should really be built into the MagickCore
4523       It does not actually require a cli-wand or and images!
4524     */
4525    ssize_t
4526      list;
4527
4528    list=ParseCommandOption(MagickListOptions,MagickFalse,arg1);
4529    if ( list < 0 ) {
4530      CLIWandExceptionArg(OptionError,"UnrecognizedListType",option,arg1);
4531      return;
4532    }
4533    switch (list)
4534    {
4535      case MagickCoderOptions:
4536      {
4537        (void) ListCoderInfo((FILE *) NULL,_exception);
4538        break;
4539      }
4540      case MagickColorOptions:
4541      {
4542        (void) ListColorInfo((FILE *) NULL,_exception);
4543        break;
4544      }
4545      case MagickConfigureOptions:
4546      {
4547        (void) ListConfigureInfo((FILE *) NULL,_exception);
4548        break;
4549      }
4550      case MagickDelegateOptions:
4551      {
4552        (void) ListDelegateInfo((FILE *) NULL,_exception);
4553        break;
4554      }
4555      case MagickFontOptions:
4556      {
4557        (void) ListTypeInfo((FILE *) NULL,_exception);
4558        break;
4559      }
4560      case MagickFormatOptions:
4561        (void) ListMagickInfo((FILE *) NULL,_exception);
4562        break;
4563      case MagickLocaleOptions:
4564        (void) ListLocaleInfo((FILE *) NULL,_exception);
4565        break;
4566      case MagickLogOptions:
4567        (void) ListLogInfo((FILE *) NULL,_exception);
4568        break;
4569      case MagickMagicOptions:
4570        (void) ListMagicInfo((FILE *) NULL,_exception);
4571        break;
4572      case MagickMimeOptions:
4573        (void) ListMimeInfo((FILE *) NULL,_exception);
4574        break;
4575      case MagickModuleOptions:
4576        (void) ListModuleInfo((FILE *) NULL,_exception);
4577        break;
4578      case MagickPolicyOptions:
4579        (void) ListPolicyInfo((FILE *) NULL,_exception);
4580        break;
4581      case MagickResourceOptions:
4582        (void) ListMagickResourceInfo((FILE *) NULL,_exception);
4583        break;
4584      case MagickThresholdOptions:
4585        (void) ListThresholdMaps((FILE *) NULL,_exception);
4586        break;
4587      default:
4588        (void) ListCommandOptions((FILE *) NULL,(CommandOption) list,
4589          _exception);
4590        break;
4591    }
4592    return;
4593  }
4594
4595#if 0
4596  // Other 'special' options this should handle
4597  //    "region"  "reset"  "arg"
4598  if ( ( process_flags & ProcessUnknownOptionError ) != 0 )
4599#endif
4600    CLIWandException(OptionError,"UnrecognizedOption",option);
4601
4602#undef _image_info
4603#undef _images
4604#undef _exception
4605#undef IfNormalOp
4606#undef IfPlusOp
4607}
4608
4609/*
4610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4611%                                                                             %
4612%                                                                             %
4613%                                                                             %
4614+   C L I O p t i o n O p e r a t i o n s                                     %
4615%                                                                             %
4616%                                                                             %
4617%                                                                             %
4618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4619%
4620%  CLIOption() Processes the given option using the given CLI Magick Wand.
4621%
4622%  If the cli_wand->command pointer is non-null, then it is assumed that the
4623%  option has already been search for up from the CommandOptions[] table in
4624%  "MagickCore/options.c" using  GetCommandOptionInfo(), so that any command
4625%  specific options (such as "magick" scripting options, or "display" settings)
4626%  can be handled separatally.
4627%
4628%  The format of the CLIOption method is:
4629%
4630%       void CLIOption(MagickCLI *cli_wand,const char *option, ...)
4631%
4632%  A description of each parameter follows:
4633%
4634%     o cli_wand: the main CLI Wand to use.
4635%
4636%     o option: The special option (with any switch char) to process
4637%
4638%     o args: any required arguments for an option
4639%
4640%  Example Usage...
4641%
4642%    CLIoption(cli_wand,"-read","rose:");
4643%    CLIoption(cli_wand,"-virtual-pixel","transparent");
4644%    CLIoption(cli_wand,"-distort","SRT:","30");
4645%    CLIoption(cli_wand,"-write","rotated_rose.png");
4646%
4647*/
4648WandExport void CLIOption(MagickCLI *cli_wand,const char *option,...)
4649{
4650  const char
4651    *arg1,
4652    *arg2;
4653
4654  CommandOptionFlags
4655    option_type;
4656
4657  assert(cli_wand != (MagickCLI *) NULL);
4658  assert(cli_wand->signature == WandSignature);
4659  assert(cli_wand->wand.signature == WandSignature);
4660  if (IfMagickTrue(cli_wand->wand.debug))
4661    (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",cli_wand->wand.name);
4662
4663  if ( cli_wand->command == (const OptionInfo *) NULL )
4664    cli_wand->command = GetCommandOptionInfo(option);
4665#if 0
4666    (void) FormatLocaleFile(stderr, "CLIOption \"%s\" matched \"%s\"\n",
4667          option, cli_wand->command->mnemonic );
4668#endif
4669
4670  option_type=(CommandOptionFlags) cli_wand->command->flags;
4671
4672  if ( option_type == UndefinedOptionFlag )
4673    CLIWandExceptionReturn(OptionFatalError,"UnrecognizedOption",option);
4674
4675  assert( LocaleCompare(cli_wand->command->mnemonic,option) == 0 );
4676
4677  if ((option_type & (SpecialOptionFlag|GenesisOptionFlag)) != 0 )
4678    CLIWandExceptionReturn(OptionFatalError,"InvalidUseOfOption",option);
4679
4680  if ( (option_type & DeprecateOptionFlag) != 0 )
4681    CLIWandExceptionReturn(OptionError,"DeprecatedOptionNoCode",option);
4682
4683  if ( IfMagickTrue(CLICatchException(cli_wand, MagickFalse)) )
4684    return;
4685
4686
4687  { size_t
4688      count = cli_wand->command->type;
4689
4690    va_list
4691      operands;
4692
4693    va_start(operands,option);
4694
4695    arg1=arg2=NULL;
4696    if ( count >= 1 )
4697      arg1=(const char *) va_arg(operands, const char *);
4698    if ( count >= 2 )
4699      arg2=(const char *) va_arg(operands, const char *);
4700
4701    va_end(operands);
4702
4703#if 0
4704    (void) FormatLocaleFile(stderr,
4705      "CLIOption: \"%s\"  Count: %ld  Flags: %04x  Args: \"%s\" \"%s\"\n",
4706          option,(long) count,option_type,arg1,arg2);
4707#endif
4708  }
4709
4710  cli_wand->command = (const OptionInfo *) NULL; /* prevent re-use later */
4711
4712
4713  /*
4714     Call the appropriate option handler
4715  */
4716
4717  /* FUTURE: this is temporary - get 'settings' to handle
4718     distribution of settings to images attributes,proprieties,artifacts */
4719  if ( cli_wand->wand.images != (Image *)NULL )
4720    SyncImagesSettings(cli_wand->wand.image_info,cli_wand->wand.images,
4721        cli_wand->wand.exception);
4722
4723  if ( (option_type & SettingOptionFlags) != 0 ) {
4724    CLISettingOptionInfo(cli_wand, option, arg1, arg2);
4725    // FUTURE: Sync Specific Settings into Image Properities (not global)
4726  }
4727
4728  if ( (option_type & NoImageOperatorFlag) != 0)
4729    CLINoImageOperator(cli_wand, option, arg1, arg2);
4730
4731  /* FUTURE: The not a setting part below is a temporary hack due to
4732   * some options being both a Setting and a Simple operator.
4733   * Specifically -monitor, -depth, and  -colorspace */
4734  if ( cli_wand->wand.images == (Image *)NULL ) {
4735    if ( ((option_type & (SimpleOperatorFlag|ListOperatorFlag)) != 0 ) &&
4736         ((option_type & SettingOptionFlags) == 0 ))  /* temp hack */
4737      CLIWandException(OptionError,"NoImagesFound",option);
4738    return; /* on its own this is not an error */
4739  }
4740
4741  if ( (option_type & SimpleOperatorFlag) != 0)
4742    CLISimpleOperatorImages(cli_wand, option, arg1, arg2);
4743
4744  if ( (option_type & ListOperatorFlag) != 0 )
4745    CLIListOperatorImages(cli_wand, option, arg1, arg2);
4746
4747}
4748