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