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