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