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