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