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