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