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