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