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