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