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