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