blob: e6173eab4c1d84de2de3999d20ae9bb34670c824 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% FFFFF X X %
7% F X X %
8% FFF X %
9% F X X %
10% F X X %
11% %
12% %
13% MagickCore Image Special Effects Methods %
14% %
15% Software Design %
16% John Cristy %
17% October 1996 %
18% %
19% %
20% Copyright 1999-2009 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% http://www.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%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/annotate.h"
45#include "magick/artifact.h"
cristycd220102009-10-14 19:39:10 +000046#include "magick/attribute.h"
cristy3ed852e2009-09-05 21:47:34 +000047#include "magick/cache.h"
48#include "magick/cache-view.h"
49#include "magick/color.h"
50#include "magick/color-private.h"
51#include "magick/composite.h"
52#include "magick/decorate.h"
53#include "magick/draw.h"
54#include "magick/effect.h"
55#include "magick/enhance.h"
56#include "magick/exception.h"
57#include "magick/exception-private.h"
58#include "magick/fx.h"
59#include "magick/fx-private.h"
60#include "magick/gem.h"
61#include "magick/geometry.h"
62#include "magick/layer.h"
63#include "magick/list.h"
64#include "magick/log.h"
65#include "magick/image.h"
66#include "magick/image-private.h"
67#include "magick/memory_.h"
68#include "magick/monitor.h"
69#include "magick/monitor-private.h"
70#include "magick/option.h"
71#include "magick/pixel-private.h"
72#include "magick/property.h"
73#include "magick/quantum.h"
74#include "magick/random_.h"
75#include "magick/random-private.h"
76#include "magick/resample.h"
77#include "magick/resample-private.h"
78#include "magick/resize.h"
79#include "magick/shear.h"
80#include "magick/splay-tree.h"
81#include "magick/statistic.h"
82#include "magick/string_.h"
83#include "magick/thread-private.h"
84#include "magick/transform.h"
85#include "magick/utility.h"
86
87/*
88 Define declarations.
89*/
90#define LeftShiftOperator 0xf5
91#define RightShiftOperator 0xf6
92#define LessThanEqualOperator 0xf7
93#define GreaterThanEqualOperator 0xf8
94#define EqualOperator 0xf9
95#define NotEqualOperator 0xfa
96#define LogicalAndOperator 0xfb
97#define LogicalOrOperator 0xfc
98
99struct _FxInfo
100{
101 const Image
102 *images;
103
104 MagickBooleanType
105 matte;
106
107 char
108 *expression;
109
110 FILE
111 *file;
112
113 SplayTreeInfo
114 *colors,
115 *symbols;
116
117 ResampleFilter
118 **resample_filter;
119
120 RandomInfo
121 *random_info;
122
123 ExceptionInfo
124 *exception;
125};
126
127/*
128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129% %
130% %
131% %
132+ A c q u i r e F x I n f o %
133% %
134% %
135% %
136%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
137%
138% AcquireFxInfo() allocates the FxInfo structure.
139%
140% The format of the AcquireFxInfo method is:
141%
142% FxInfo *AcquireFxInfo(Image *image,const char *expression)
143% A description of each parameter follows:
144%
145% o image: the image.
146%
147% o expression: the expression.
148%
149*/
150MagickExport FxInfo *AcquireFxInfo(const Image *image,const char *expression)
151{
152 char
153 fx_op[2];
154
155 FxInfo
156 *fx_info;
157
158 register long
159 i;
160
161 fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
162 if (fx_info == (FxInfo *) NULL)
163 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
164 (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
165 fx_info->exception=AcquireExceptionInfo();
166 fx_info->images=image;
167 fx_info->matte=image->matte;
168 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
169 RelinquishMagickMemory);
170 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
171 RelinquishMagickMemory);
172 fx_info->resample_filter=(ResampleFilter **) AcquireQuantumMemory(
173 GetImageListLength(fx_info->images),sizeof(*fx_info->resample_filter));
174 if (fx_info->resample_filter == (ResampleFilter **) NULL)
175 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
176 for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
177 {
178 fx_info->resample_filter[i]=AcquireResampleFilter(GetImageFromList(
179 fx_info->images,i),fx_info->exception);
180 SetResampleFilter(fx_info->resample_filter[i],PointFilter,1.0);
181 }
182 fx_info->random_info=AcquireRandomInfo();
183 fx_info->expression=ConstantString(expression);
184 fx_info->file=stderr;
185 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
186 if ((strstr(fx_info->expression,"e+") != (char *) NULL) ||
187 (strstr(fx_info->expression,"e-") != (char *) NULL))
188 {
189 /*
190 Convert scientific notation.
191 */
192 (void) SubstituteString(&fx_info->expression,"0e+","0*10^");
193 (void) SubstituteString(&fx_info->expression,"1e+","1*10^");
194 (void) SubstituteString(&fx_info->expression,"2e+","2*10^");
195 (void) SubstituteString(&fx_info->expression,"3e+","3*10^");
196 (void) SubstituteString(&fx_info->expression,"4e+","4*10^");
197 (void) SubstituteString(&fx_info->expression,"5e+","5*10^");
198 (void) SubstituteString(&fx_info->expression,"6e+","6*10^");
199 (void) SubstituteString(&fx_info->expression,"7e+","7*10^");
200 (void) SubstituteString(&fx_info->expression,"8e+","8*10^");
201 (void) SubstituteString(&fx_info->expression,"9e+","9*10^");
202 (void) SubstituteString(&fx_info->expression,"0e-","0*10^-");
203 (void) SubstituteString(&fx_info->expression,"1e-","1*10^-");
204 (void) SubstituteString(&fx_info->expression,"2e-","2*10^-");
205 (void) SubstituteString(&fx_info->expression,"3e-","3*10^-");
206 (void) SubstituteString(&fx_info->expression,"4e-","4*10^-");
207 (void) SubstituteString(&fx_info->expression,"5e-","5*10^-");
208 (void) SubstituteString(&fx_info->expression,"6e-","6*10^-");
209 (void) SubstituteString(&fx_info->expression,"7e-","7*10^-");
210 (void) SubstituteString(&fx_info->expression,"8e-","8*10^-");
211 (void) SubstituteString(&fx_info->expression,"9e-","9*10^-");
212 }
213 /*
214 Convert complex to simple operators.
215 */
216 fx_op[1]='\0';
217 *fx_op=(char) LeftShiftOperator;
218 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
219 *fx_op=(char) RightShiftOperator;
220 (void) SubstituteString(&fx_info->expression,">>",fx_op);
221 *fx_op=(char) LessThanEqualOperator;
222 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
223 *fx_op=(char) GreaterThanEqualOperator;
224 (void) SubstituteString(&fx_info->expression,">=",fx_op);
225 *fx_op=(char) EqualOperator;
226 (void) SubstituteString(&fx_info->expression,"==",fx_op);
227 *fx_op=(char) NotEqualOperator;
228 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
229 *fx_op=(char) LogicalAndOperator;
230 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
231 *fx_op=(char) LogicalOrOperator;
232 (void) SubstituteString(&fx_info->expression,"||",fx_op);
233 return(fx_info);
234}
235
236/*
237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
238% %
239% %
240% %
241% A d d N o i s e I m a g e %
242% %
243% %
244% %
245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
246%
247% AddNoiseImage() adds random noise to the image.
248%
249% The format of the AddNoiseImage method is:
250%
251% Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
252% ExceptionInfo *exception)
253% Image *AddNoiseImageChannel(const Image *image,const ChannelType channel,
254% const NoiseType noise_type,ExceptionInfo *exception)
255%
256% A description of each parameter follows:
257%
258% o image: the image.
259%
260% o channel: the channel type.
261%
262% o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
263% Impulse, Laplacian, or Poisson.
264%
265% o exception: return any errors or warnings in this structure.
266%
267*/
268MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
269 ExceptionInfo *exception)
270{
271 Image
272 *noise_image;
273
274 noise_image=AddNoiseImageChannel(image,DefaultChannels,noise_type,exception);
275 return(noise_image);
276}
277
278MagickExport Image *AddNoiseImageChannel(const Image *image,
279 const ChannelType channel,const NoiseType noise_type,ExceptionInfo *exception)
280{
281#define AddNoiseImageTag "AddNoise/Image"
282
283 const char
284 *option;
285
286 Image
287 *noise_image;
288
289 long
290 progress,
291 y;
292
293 MagickBooleanType
294 status;
295
296 MagickRealType
297 attenuate;
298
299 RandomInfo
300 **random_info;
301
302 CacheView
303 *image_view,
304 *noise_view;
305
306 /*
307 Initialize noise image attributes.
308 */
309 assert(image != (const Image *) NULL);
310 assert(image->signature == MagickSignature);
311 if (image->debug != MagickFalse)
312 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
313 assert(exception != (ExceptionInfo *) NULL);
314 assert(exception->signature == MagickSignature);
315 noise_image=CloneImage(image,0,0,MagickTrue,exception);
316 if (noise_image == (Image *) NULL)
317 return((Image *) NULL);
318 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
319 {
320 InheritException(exception,&noise_image->exception);
321 noise_image=DestroyImage(noise_image);
322 return((Image *) NULL);
323 }
324 /*
325 Add noise in each row.
326 */
327 attenuate=1.0;
328 option=GetImageArtifact(image,"attenuate");
329 if (option != (char *) NULL)
330 attenuate=atof(option);
331 status=MagickTrue;
332 progress=0;
333 random_info=AcquireRandomInfoThreadSet();
334 image_view=AcquireCacheView(image);
335 noise_view=AcquireCacheView(noise_image);
cristyb28d6472009-10-17 20:13:35 +0000336#if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
cristyb5d5f722009-11-04 03:03:49 +0000337 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000338#endif
339 for (y=0; y < (long) image->rows; y++)
340 {
341 MagickBooleanType
342 sync;
343
344 register const IndexPacket
345 *__restrict indexes;
346
347 register const PixelPacket
348 *__restrict p;
349
350 register IndexPacket
351 *__restrict noise_indexes;
352
353 register long
354 id,
355 x;
356
357 register PixelPacket
358 *__restrict q;
359
360 if (status == MagickFalse)
361 continue;
362 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
363 q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
364 exception);
365 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
366 {
367 status=MagickFalse;
368 continue;
369 }
370 indexes=GetCacheViewVirtualIndexQueue(image_view);
371 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
372 id=GetOpenMPThreadId();
373 for (x=0; x < (long) image->columns; x++)
374 {
375 if ((channel & RedChannel) != 0)
376 q->red=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
377 p->red,noise_type,attenuate));
378 if ((channel & GreenChannel) != 0)
379 q->green=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
380 p->green,noise_type,attenuate));
381 if ((channel & BlueChannel) != 0)
382 q->blue=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
383 p->blue,noise_type,attenuate));
384 if ((channel & OpacityChannel) != 0)
385 q->opacity=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
386 p->opacity,noise_type,attenuate));
387 if (((channel & IndexChannel) != 0) &&
388 (image->colorspace == CMYKColorspace))
389 noise_indexes[x]=(IndexPacket) RoundToQuantum(GenerateDifferentialNoise(
390 random_info[id],indexes[x],noise_type,attenuate));
391 p++;
392 q++;
393 }
394 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
395 if (sync == MagickFalse)
396 status=MagickFalse;
397 if (image->progress_monitor != (MagickProgressMonitor) NULL)
398 {
399 MagickBooleanType
400 proceed;
401
cristyb5d5f722009-11-04 03:03:49 +0000402#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000403 #pragma omp critical (MagickCore_AverageImages)
404#endif
405 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
406 image->rows);
407 if (proceed == MagickFalse)
408 status=MagickFalse;
409 }
410 }
411 noise_view=DestroyCacheView(noise_view);
412 image_view=DestroyCacheView(image_view);
413 random_info=DestroyRandomInfoThreadSet(random_info);
414 if (status == MagickFalse)
415 noise_image=DestroyImage(noise_image);
416 return(noise_image);
417}
418
419/*
420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421% %
422% %
423% %
424% B l u e S h i f t I m a g e %
425% %
426% %
427% %
428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
429%
430% BlueShiftImage() mutes the colors of the image to simulate a scene at
431% nighttime in the moonlight.
432%
433% The format of the BlueShiftImage method is:
434%
435% Image *BlueShiftImage(const Image *image,const double factor,
436% ExceptionInfo *exception)
437%
438% A description of each parameter follows:
439%
440% o image: the image.
441%
442% o factor: the shift factor.
443%
444% o exception: return any errors or warnings in this structure.
445%
446*/
447MagickExport Image *BlueShiftImage(const Image *image,const double factor,
448 ExceptionInfo *exception)
449{
450#define BlueShiftImageTag "BlueShift/Image"
451
452 Image
453 *shift_image;
454
455 long
456 progress,
457 y;
458
459 MagickBooleanType
460 status;
461
462 CacheView
463 *image_view,
464 *shift_view;
465
466 /*
467 Allocate blue shift image.
468 */
469 assert(image != (const Image *) NULL);
470 assert(image->signature == MagickSignature);
471 if (image->debug != MagickFalse)
472 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
473 assert(exception != (ExceptionInfo *) NULL);
474 assert(exception->signature == MagickSignature);
475 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,
476 exception);
477 if (shift_image == (Image *) NULL)
478 return((Image *) NULL);
479 if (SetImageStorageClass(shift_image,DirectClass) == MagickFalse)
480 {
481 InheritException(exception,&shift_image->exception);
482 shift_image=DestroyImage(shift_image);
483 return((Image *) NULL);
484 }
485 /*
486 Blue-shift DirectClass image.
487 */
488 status=MagickTrue;
489 progress=0;
490 image_view=AcquireCacheView(image);
491 shift_view=AcquireCacheView(shift_image);
cristyb28d6472009-10-17 20:13:35 +0000492#if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
cristyb5d5f722009-11-04 03:03:49 +0000493 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000494#endif
495 for (y=0; y < (long) image->rows; y++)
496 {
497 MagickBooleanType
498 sync;
499
500 MagickPixelPacket
501 pixel;
502
503 Quantum
504 quantum;
505
506 register const PixelPacket
507 *__restrict p;
508
509 register long
510 x;
511
512 register PixelPacket
513 *__restrict q;
514
515 if (status == MagickFalse)
516 continue;
517 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
518 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
519 exception);
520 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
521 {
522 status=MagickFalse;
523 continue;
524 }
525 for (x=0; x < (long) image->columns; x++)
526 {
527 quantum=p->red;
528 if (p->green < quantum)
529 quantum=p->green;
530 if (p->blue < quantum)
531 quantum=p->blue;
532 pixel.red=0.5*(p->red+factor*quantum);
533 pixel.green=0.5*(p->green+factor*quantum);
534 pixel.blue=0.5*(p->blue+factor*quantum);
535 quantum=p->red;
536 if (p->green > quantum)
537 quantum=p->green;
538 if (p->blue > quantum)
539 quantum=p->blue;
540 pixel.red=0.5*(pixel.red+factor*quantum);
541 pixel.green=0.5*(pixel.green+factor*quantum);
542 pixel.blue=0.5*(pixel.blue+factor*quantum);
543 q->red=RoundToQuantum(pixel.red);
544 q->green=RoundToQuantum(pixel.green);
545 q->blue=RoundToQuantum(pixel.blue);
546 p++;
547 q++;
548 }
549 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
550 if (sync == MagickFalse)
551 status=MagickFalse;
552 if (image->progress_monitor != (MagickProgressMonitor) NULL)
553 {
554 MagickBooleanType
555 proceed;
556
cristyb28d6472009-10-17 20:13:35 +0000557#if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
cristy3ed852e2009-09-05 21:47:34 +0000558 #pragma omp critical (MagickCore_BlueShiftImage)
559#endif
560 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
561 image->rows);
562 if (proceed == MagickFalse)
563 status=MagickFalse;
564 }
565 }
566 image_view=DestroyCacheView(image_view);
567 shift_view=DestroyCacheView(shift_view);
568 if (status == MagickFalse)
569 shift_image=DestroyImage(shift_image);
570 return(shift_image);
571}
572
573/*
574%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
575% %
576% %
577% %
578% C h a r c o a l I m a g e %
579% %
580% %
581% %
582%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
583%
584% CharcoalImage() creates a new image that is a copy of an existing one with
585% the edge highlighted. It allocates the memory necessary for the new Image
586% structure and returns a pointer to the new image.
587%
588% The format of the CharcoalImage method is:
589%
590% Image *CharcoalImage(const Image *image,const double radius,
591% const double sigma,ExceptionInfo *exception)
592%
593% A description of each parameter follows:
594%
595% o image: the image.
596%
597% o radius: the radius of the pixel neighborhood.
598%
599% o sigma: the standard deviation of the Gaussian, in pixels.
600%
601% o exception: return any errors or warnings in this structure.
602%
603*/
604MagickExport Image *CharcoalImage(const Image *image,const double radius,
605 const double sigma,ExceptionInfo *exception)
606{
607 Image
608 *charcoal_image,
609 *clone_image,
610 *edge_image;
611
612 assert(image != (Image *) NULL);
613 assert(image->signature == MagickSignature);
614 if (image->debug != MagickFalse)
615 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
616 assert(exception != (ExceptionInfo *) NULL);
617 assert(exception->signature == MagickSignature);
618 clone_image=CloneImage(image,0,0,MagickTrue,exception);
619 if (clone_image == (Image *) NULL)
620 return((Image *) NULL);
621 (void) SetImageType(clone_image,GrayscaleType);
622 edge_image=EdgeImage(clone_image,radius,exception);
623 clone_image=DestroyImage(clone_image);
624 if (edge_image == (Image *) NULL)
625 return((Image *) NULL);
626 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
627 edge_image=DestroyImage(edge_image);
628 if (charcoal_image == (Image *) NULL)
629 return((Image *) NULL);
630 (void) NormalizeImage(charcoal_image);
631 (void) NegateImage(charcoal_image,MagickFalse);
632 (void) SetImageType(charcoal_image,GrayscaleType);
633 return(charcoal_image);
634}
635
636/*
637%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
638% %
639% %
640% %
641% C o l o r i z e I m a g e %
642% %
643% %
644% %
645%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
646%
647% ColorizeImage() blends the fill color with each pixel in the image.
648% A percentage blend is specified with opacity. Control the application
649% of different color components by specifying a different percentage for
650% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
651%
652% The format of the ColorizeImage method is:
653%
654% Image *ColorizeImage(const Image *image,const char *opacity,
655% const PixelPacket colorize,ExceptionInfo *exception)
656%
657% A description of each parameter follows:
658%
659% o image: the image.
660%
661% o opacity: A character string indicating the level of opacity as a
662% percentage.
663%
664% o colorize: A color value.
665%
666% o exception: return any errors or warnings in this structure.
667%
668*/
669MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
670 const PixelPacket colorize,ExceptionInfo *exception)
671{
672#define ColorizeImageTag "Colorize/Image"
673
674 GeometryInfo
675 geometry_info;
676
677 Image
678 *colorize_image;
679
680 long
681 progress,
682 y;
683
684 MagickBooleanType
685 status;
686
687 MagickPixelPacket
688 pixel;
689
690 MagickStatusType
691 flags;
692
693 CacheView
694 *colorize_view,
695 *image_view;
696
697 /*
698 Allocate colorized image.
699 */
700 assert(image != (const Image *) NULL);
701 assert(image->signature == MagickSignature);
702 if (image->debug != MagickFalse)
703 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
704 assert(exception != (ExceptionInfo *) NULL);
705 assert(exception->signature == MagickSignature);
706 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
707 exception);
708 if (colorize_image == (Image *) NULL)
709 return((Image *) NULL);
710 if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
711 {
712 InheritException(exception,&colorize_image->exception);
713 colorize_image=DestroyImage(colorize_image);
714 return((Image *) NULL);
715 }
716 if (opacity == (const char *) NULL)
717 return(colorize_image);
718 /*
719 Determine RGB values of the pen color.
720 */
721 flags=ParseGeometry(opacity,&geometry_info);
722 pixel.red=geometry_info.rho;
723 pixel.green=geometry_info.rho;
724 pixel.blue=geometry_info.rho;
725 pixel.opacity=(MagickRealType) OpaqueOpacity;
726 if ((flags & SigmaValue) != 0)
727 pixel.green=geometry_info.sigma;
728 if ((flags & XiValue) != 0)
729 pixel.blue=geometry_info.xi;
730 if ((flags & PsiValue) != 0)
731 pixel.opacity=geometry_info.psi;
732 /*
733 Colorize DirectClass image.
734 */
735 status=MagickTrue;
736 progress=0;
737 image_view=AcquireCacheView(image);
738 colorize_view=AcquireCacheView(colorize_image);
cristyb28d6472009-10-17 20:13:35 +0000739#if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
cristyb5d5f722009-11-04 03:03:49 +0000740 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000741#endif
742 for (y=0; y < (long) image->rows; y++)
743 {
744 MagickBooleanType
745 sync;
746
747 register const PixelPacket
748 *__restrict p;
749
750 register long
751 x;
752
753 register PixelPacket
754 *__restrict q;
755
756 if (status == MagickFalse)
757 continue;
758 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
759 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
760 exception);
761 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
762 {
763 status=MagickFalse;
764 continue;
765 }
766 for (x=0; x < (long) image->columns; x++)
767 {
768 q->red=(Quantum) ((p->red*(100.0-pixel.red)+
769 colorize.red*pixel.red)/100.0);
770 q->green=(Quantum) ((p->green*(100.0-pixel.green)+
771 colorize.green*pixel.green)/100.0);
772 q->blue=(Quantum) ((p->blue*(100.0-pixel.blue)+
773 colorize.blue*pixel.blue)/100.0);
774 q->opacity=(Quantum) ((p->opacity*(100.0-pixel.opacity)+
775 colorize.opacity*pixel.opacity)/100.0);
776 p++;
777 q++;
778 }
779 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
780 if (sync == MagickFalse)
781 status=MagickFalse;
782 if (image->progress_monitor != (MagickProgressMonitor) NULL)
783 {
784 MagickBooleanType
785 proceed;
786
cristyb28d6472009-10-17 20:13:35 +0000787#if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
cristy3ed852e2009-09-05 21:47:34 +0000788 #pragma omp critical (MagickCore_ColorizeImage)
789#endif
790 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
791 if (proceed == MagickFalse)
792 status=MagickFalse;
793 }
794 }
795 image_view=DestroyCacheView(image_view);
796 colorize_view=DestroyCacheView(colorize_view);
797 if (status == MagickFalse)
798 colorize_image=DestroyImage(colorize_image);
799 return(colorize_image);
800}
801
802/*
803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
804% %
805% %
806% %
807% C o n v o l v e I m a g e %
808% %
809% %
810% %
811%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
812%
813% ConvolveImage() applies a custom convolution kernel to the image.
814%
815% The format of the ConvolveImage method is:
816%
817% Image *ConvolveImage(const Image *image,const unsigned long order,
818% const double *kernel,ExceptionInfo *exception)
819% Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
820% const unsigned long order,const double *kernel,
821% ExceptionInfo *exception)
822%
823% A description of each parameter follows:
824%
825% o image: the image.
826%
827% o channel: the channel type.
828%
829% o order: the number of columns and rows in the filter kernel.
830%
831% o kernel: An array of double representing the convolution kernel.
832%
833% o exception: return any errors or warnings in this structure.
834%
835*/
836
837MagickExport Image *ConvolveImage(const Image *image,const unsigned long order,
838 const double *kernel,ExceptionInfo *exception)
839{
840 Image
841 *convolve_image;
842
843 convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
844 exception);
845 return(convolve_image);
846}
847
848MagickExport Image *ConvolveImageChannel(const Image *image,
849 const ChannelType channel,const unsigned long order,const double *kernel,
850 ExceptionInfo *exception)
851{
852#define ConvolveImageTag "Convolve/Image"
853
854 double
855 *normal_kernel;
856
857 Image
858 *convolve_image;
859
860 long
861 progress,
862 y;
863
864 MagickBooleanType
865 status;
866
867 MagickPixelPacket
cristyddd82202009-11-03 20:14:50 +0000868 bias;
cristy3ed852e2009-09-05 21:47:34 +0000869
870 MagickRealType
cristy3ed852e2009-09-05 21:47:34 +0000871 gamma;
872
873 register long
874 i;
875
876 unsigned long
877 width;
878
879 CacheView
880 *convolve_view,
881 *image_view;
882
883 /*
884 Initialize convolve image attributes.
885 */
886 assert(image != (Image *) NULL);
887 assert(image->signature == MagickSignature);
888 if (image->debug != MagickFalse)
889 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
890 assert(exception != (ExceptionInfo *) NULL);
891 assert(exception->signature == MagickSignature);
892 width=order;
893 if ((width % 2) == 0)
894 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
895 convolve_image=CloneImage(image,0,0,MagickTrue,exception);
896 if (convolve_image == (Image *) NULL)
897 return((Image *) NULL);
898 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
899 {
900 InheritException(exception,&convolve_image->exception);
901 convolve_image=DestroyImage(convolve_image);
902 return((Image *) NULL);
903 }
904 if (image->debug != MagickFalse)
905 {
906 char
907 format[MaxTextExtent],
908 *message;
909
910 long
911 u,
912 v;
913
914 register const double
915 *k;
916
917 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
918 " ConvolveImage with %ldx%ld kernel:",width,width);
919 message=AcquireString("");
920 k=kernel;
921 for (v=0; v < (long) width; v++)
922 {
923 *message='\0';
924 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
925 (void) ConcatenateString(&message,format);
926 for (u=0; u < (long) width; u++)
927 {
928 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
929 (void) ConcatenateString(&message,format);
930 }
931 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
932 }
933 message=DestroyString(message);
934 }
935 /*
936 Normalize kernel.
937 */
938 normal_kernel=(double *) AcquireQuantumMemory(width*width,
939 sizeof(*normal_kernel));
940 if (normal_kernel == (double *) NULL)
941 {
942 convolve_image=DestroyImage(convolve_image);
943 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
944 }
945 gamma=0.0;
946 for (i=0; i < (long) (width*width); i++)
947 gamma+=kernel[i];
948 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
949 for (i=0; i < (long) (width*width); i++)
950 normal_kernel[i]=gamma*kernel[i];
951 /*
952 Convolve image.
953 */
954 status=MagickTrue;
955 progress=0;
cristyddd82202009-11-03 20:14:50 +0000956 GetMagickPixelPacket(image,&bias);
957 SetMagickPixelPacketBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +0000958 image_view=AcquireCacheView(image);
959 convolve_view=AcquireCacheView(convolve_image);
cristyb5d5f722009-11-04 03:03:49 +0000960#if defined(MAGICKCORE_OPENMP_SUPPORT)
961 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000962#endif
963 for (y=0; y < (long) image->rows; y++)
964 {
965 MagickBooleanType
966 sync;
967
968 register const IndexPacket
969 *__restrict indexes;
970
971 register const PixelPacket
972 *__restrict p;
973
974 register IndexPacket
975 *__restrict convolve_indexes;
976
977 register long
978 x;
979
980 register PixelPacket
981 *__restrict q;
982
983 if (status == MagickFalse)
984 continue;
985 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
986 2L),image->columns+width,width,exception);
987 q=GetCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
988 exception);
989 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
990 {
991 status=MagickFalse;
992 continue;
993 }
994 indexes=GetCacheViewVirtualIndexQueue(image_view);
995 convolve_indexes=GetCacheViewAuthenticIndexQueue(convolve_view);
996 for (x=0; x < (long) image->columns; x++)
997 {
998 long
999 v;
1000
1001 MagickPixelPacket
1002 pixel;
1003
1004 register const double
1005 *__restrict k;
1006
1007 register const PixelPacket
1008 *__restrict kernel_pixels;
1009
1010 register long
1011 u;
1012
cristyddd82202009-11-03 20:14:50 +00001013 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +00001014 k=normal_kernel;
1015 kernel_pixels=p;
1016 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
1017 {
1018 for (v=0; v < (long) width; v++)
1019 {
1020 for (u=0; u < (long) width; u++)
1021 {
1022 pixel.red+=(*k)*kernel_pixels[u].red;
1023 pixel.green+=(*k)*kernel_pixels[u].green;
1024 pixel.blue+=(*k)*kernel_pixels[u].blue;
1025 k++;
1026 }
1027 kernel_pixels+=image->columns+width;
1028 }
1029 if ((channel & RedChannel) != 0)
cristyddd82202009-11-03 20:14:50 +00001030 q->red=RoundToQuantum(pixel.red);
cristy3ed852e2009-09-05 21:47:34 +00001031 if ((channel & GreenChannel) != 0)
cristyddd82202009-11-03 20:14:50 +00001032 q->green=RoundToQuantum(pixel.green);
cristy3ed852e2009-09-05 21:47:34 +00001033 if ((channel & BlueChannel) != 0)
cristyddd82202009-11-03 20:14:50 +00001034 q->blue=RoundToQuantum(pixel.blue);
cristy3ed852e2009-09-05 21:47:34 +00001035 if ((channel & OpacityChannel) != 0)
1036 {
1037 k=normal_kernel;
1038 kernel_pixels=p;
1039 for (v=0; v < (long) width; v++)
1040 {
1041 for (u=0; u < (long) width; u++)
1042 {
1043 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1044 k++;
1045 }
1046 kernel_pixels+=image->columns+width;
1047 }
cristyddd82202009-11-03 20:14:50 +00001048 q->opacity=RoundToQuantum(pixel.opacity);
cristy3ed852e2009-09-05 21:47:34 +00001049 }
1050 if (((channel & IndexChannel) != 0) &&
1051 (image->colorspace == CMYKColorspace))
1052 {
1053 register const IndexPacket
1054 *__restrict kernel_indexes;
1055
1056 k=normal_kernel;
1057 kernel_indexes=indexes;
1058 for (v=0; v < (long) width; v++)
1059 {
1060 for (u=0; u < (long) width; u++)
1061 {
1062 pixel.index+=(*k)*kernel_indexes[u];
1063 k++;
1064 }
1065 kernel_indexes+=image->columns+width;
1066 }
cristyddd82202009-11-03 20:14:50 +00001067 convolve_indexes[x]=RoundToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00001068 }
1069 }
1070 else
1071 {
1072 MagickRealType
1073 alpha,
1074 gamma;
1075
1076 gamma=0.0;
1077 for (v=0; v < (long) width; v++)
1078 {
1079 for (u=0; u < (long) width; u++)
1080 {
1081 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1082 kernel_pixels[u].opacity));
1083 pixel.red+=(*k)*alpha*kernel_pixels[u].red;
1084 pixel.green+=(*k)*alpha*kernel_pixels[u].green;
1085 pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
1086 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1087 gamma+=(*k)*alpha;
1088 k++;
1089 }
1090 kernel_pixels+=image->columns+width;
1091 }
1092 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1093 if ((channel & RedChannel) != 0)
cristyddd82202009-11-03 20:14:50 +00001094 q->red=RoundToQuantum(gamma*pixel.red);
cristy3ed852e2009-09-05 21:47:34 +00001095 if ((channel & GreenChannel) != 0)
cristyddd82202009-11-03 20:14:50 +00001096 q->green=RoundToQuantum(gamma*pixel.green);
cristy3ed852e2009-09-05 21:47:34 +00001097 if ((channel & BlueChannel) != 0)
cristyddd82202009-11-03 20:14:50 +00001098 q->blue=RoundToQuantum(gamma*pixel.blue);
cristy3ed852e2009-09-05 21:47:34 +00001099 if ((channel & OpacityChannel) != 0)
1100 {
1101 k=normal_kernel;
1102 kernel_pixels=p;
1103 for (v=0; v < (long) width; v++)
1104 {
1105 for (u=0; u < (long) width; u++)
1106 {
1107 pixel.opacity+=(*k)*kernel_pixels[u].opacity;
1108 k++;
1109 }
1110 kernel_pixels+=image->columns+width;
1111 }
cristyddd82202009-11-03 20:14:50 +00001112 q->opacity=RoundToQuantum(pixel.opacity);
cristy3ed852e2009-09-05 21:47:34 +00001113 }
1114 if (((channel & IndexChannel) != 0) &&
1115 (image->colorspace == CMYKColorspace))
1116 {
1117 register const IndexPacket
1118 *__restrict kernel_indexes;
1119
1120 k=normal_kernel;
1121 kernel_pixels=p;
1122 kernel_indexes=indexes;
1123 for (v=0; v < (long) width; v++)
1124 {
1125 for (u=0; u < (long) width; u++)
1126 {
1127 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
1128 kernel_pixels[u].opacity));
1129 pixel.index+=(*k)*alpha*kernel_indexes[u];
1130 k++;
1131 }
1132 kernel_pixels+=image->columns+width;
1133 kernel_indexes+=image->columns+width;
1134 }
cristyddd82202009-11-03 20:14:50 +00001135 convolve_indexes[x]=RoundToQuantum(gamma*pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00001136 }
1137 }
1138 p++;
1139 q++;
1140 }
1141 sync=SyncCacheViewAuthenticPixels(convolve_view,exception);
1142 if (sync == MagickFalse)
1143 status=MagickFalse;
1144 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1145 {
1146 MagickBooleanType
1147 proceed;
1148
cristyb5d5f722009-11-04 03:03:49 +00001149#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001150 #pragma omp critical (MagickCore_ConvolveImageChannel)
1151#endif
1152 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1153 if (proceed == MagickFalse)
1154 status=MagickFalse;
1155 }
1156 }
1157 convolve_image->type=image->type;
1158 convolve_view=DestroyCacheView(convolve_view);
1159 image_view=DestroyCacheView(image_view);
1160 normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
1161 if (status == MagickFalse)
1162 convolve_image=DestroyImage(convolve_image);
1163 return(convolve_image);
1164}
1165
1166/*
1167%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1168% %
1169% %
1170% %
1171+ D e s t r o y F x I n f o %
1172% %
1173% %
1174% %
1175%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176%
1177% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1178%
1179% The format of the DestroyFxInfo method is:
1180%
1181% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1182%
1183% A description of each parameter follows:
1184%
1185% o fx_info: the fx info.
1186%
1187*/
1188MagickExport FxInfo *DestroyFxInfo(FxInfo *fx_info)
1189{
1190 register long
1191 i;
1192
1193 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1194 fx_info->expression=DestroyString(fx_info->expression);
1195 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1196 fx_info->colors=DestroySplayTree(fx_info->colors);
1197 for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
1198 fx_info->resample_filter[i]=DestroyResampleFilter(
1199 fx_info->resample_filter[i]);
1200 fx_info->resample_filter=(ResampleFilter **) RelinquishMagickMemory(
1201 fx_info->resample_filter);
1202 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1203 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1204 return(fx_info);
1205}
1206
1207/*
1208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1209% %
1210% %
1211% %
1212% E v a l u a t e I m a g e %
1213% %
1214% %
1215% %
1216%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1217%
1218% EvaluateImage() applies a value to the image with an arithmetic, relational,
1219% or logical operator to an image. Use these operations to lighten or darken
1220% an image, to increase or decrease contrast in an image, or to produce the
1221% "negative" of an image.
1222%
1223% The format of the EvaluateImageChannel method is:
1224%
1225% MagickBooleanType EvaluateImage(Image *image,
1226% const MagickEvaluateOperator op,const double value,
1227% ExceptionInfo *exception)
1228% MagickBooleanType EvaluateImageChannel(Image *image,
1229% const ChannelType channel,const MagickEvaluateOperator op,
1230% const double value,ExceptionInfo *exception)
1231%
1232% A description of each parameter follows:
1233%
1234% o image: the image.
1235%
1236% o channel: the channel.
1237%
1238% o op: A channel op.
1239%
1240% o value: A value value.
1241%
1242% o exception: return any errors or warnings in this structure.
1243%
1244*/
1245
1246static inline double MagickMax(const double x,const double y)
1247{
1248 if (x > y)
1249 return(x);
1250 return(y);
1251}
1252
1253static inline double MagickMin(const double x,const double y)
1254{
1255 if (x < y)
1256 return(x);
1257 return(y);
1258}
1259
1260static Quantum ApplyEvaluateOperator(RandomInfo *random_info,Quantum pixel,
1261 const MagickEvaluateOperator op,const MagickRealType value)
1262{
1263 MagickRealType
1264 result;
1265
1266 result=0.0;
1267 switch (op)
1268 {
1269 case UndefinedEvaluateOperator:
1270 break;
1271 case AddEvaluateOperator:
1272 {
1273 result=(MagickRealType) (pixel+value);
1274 break;
1275 }
1276 case AddModulusEvaluateOperator:
1277 {
1278 /* This will return a 'floored modulus' of the addition which will
1279 * always return a positive result, regardless of if the result is
1280 * positive or negative, and thus in the 0 to QuantumRange range.
1281 *
1282 * WARNING: this is NOT the same as a % or fmod() which returns a
1283 * 'truncated modulus' result, where floor() is replaced by trunc()
1284 * and could return a negative result, which will be clipped.
1285 */
1286 result = pixel+value;
1287 result -= (QuantumRange+1)*floor(result/(QuantumRange+1));
1288 break;
1289 }
1290 case AndEvaluateOperator:
1291 {
1292 result=(MagickRealType) ((unsigned long) pixel & (unsigned long)
1293 (value+0.5));
1294 break;
1295 }
1296 case CosineEvaluateOperator:
1297 {
1298 result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
1299 QuantumScale*pixel*value))+0.5));
1300 break;
1301 }
1302 case DivideEvaluateOperator:
1303 {
1304 result=pixel/(value == 0.0 ? 1.0 : value);
1305 break;
1306 }
1307 case GaussianNoiseEvaluateOperator:
1308 {
1309 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
1310 GaussianNoise,value);
1311 break;
1312 }
1313 case ImpulseNoiseEvaluateOperator:
1314 {
1315 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
1316 ImpulseNoise,value);
1317 break;
1318 }
1319 case LaplacianNoiseEvaluateOperator:
1320 {
1321 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
1322 LaplacianNoise,value);
1323 break;
1324 }
1325 case LeftShiftEvaluateOperator:
1326 {
1327 result=(MagickRealType) ((unsigned long) pixel << (unsigned long)
1328 (value+0.5));
1329 break;
1330 }
1331 case LogEvaluateOperator:
1332 {
1333 result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
1334 pixel+1.0))/log((double) (value+1.0)));
1335 break;
1336 }
1337 case MaxEvaluateOperator:
1338 {
1339 result=(MagickRealType) MagickMax((double) pixel,value);
1340 break;
1341 }
1342 case MinEvaluateOperator:
1343 {
1344 result=(MagickRealType) MagickMin((double) pixel,value);
1345 break;
1346 }
1347 case MultiplicativeNoiseEvaluateOperator:
1348 {
1349 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
1350 MultiplicativeGaussianNoise,value);
1351 break;
1352 }
1353 case MultiplyEvaluateOperator:
1354 {
1355 result=(MagickRealType) (value*pixel);
1356 break;
1357 }
1358 case OrEvaluateOperator:
1359 {
1360 result=(MagickRealType) ((unsigned long) pixel | (unsigned long)
1361 (value+0.5));
1362 break;
1363 }
1364 case PoissonNoiseEvaluateOperator:
1365 {
1366 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
1367 PoissonNoise,value);
1368 break;
1369 }
1370 case PowEvaluateOperator:
1371 {
1372 result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
1373 (double) value));
1374 break;
1375 }
1376 case RightShiftEvaluateOperator:
1377 {
1378 result=(MagickRealType) ((unsigned long) pixel >> (unsigned long)
1379 (value+0.5));
1380 break;
1381 }
1382 case SetEvaluateOperator:
1383 {
1384 result=value;
1385 break;
1386 }
1387 case SineEvaluateOperator:
1388 {
1389 result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
1390 QuantumScale*pixel*value))+0.5));
1391 break;
1392 }
1393 case SubtractEvaluateOperator:
1394 {
1395 result=(MagickRealType) (pixel-value);
1396 break;
1397 }
1398 case ThresholdEvaluateOperator:
1399 {
1400 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
1401 QuantumRange);
1402 break;
1403 }
1404 case ThresholdBlackEvaluateOperator:
1405 {
1406 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
1407 break;
1408 }
1409 case ThresholdWhiteEvaluateOperator:
1410 {
1411 result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
1412 pixel);
1413 break;
1414 }
1415 case UniformNoiseEvaluateOperator:
1416 {
1417 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
1418 UniformNoise,value);
1419 break;
1420 }
1421 case XorEvaluateOperator:
1422 {
1423 result=(MagickRealType) ((unsigned long) pixel ^ (unsigned long)
1424 (value+0.5));
1425 break;
1426 }
1427 }
1428 return(RoundToQuantum(result));
1429}
1430
1431MagickExport MagickBooleanType EvaluateImage(Image *image,
1432 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
1433{
1434 MagickBooleanType
1435 status;
1436
1437 status=EvaluateImageChannel(image,AllChannels,op,value,exception);
1438 return(status);
1439}
1440
1441MagickExport MagickBooleanType EvaluateImageChannel(Image *image,
1442 const ChannelType channel,const MagickEvaluateOperator op,const double value,
1443 ExceptionInfo *exception)
1444{
1445#define EvaluateImageTag "Evaluate/Image "
1446
1447 long
1448 progress,
1449 y;
1450
1451 MagickBooleanType
1452 status;
1453
1454 RandomInfo
1455 **random_info;
1456
1457 CacheView
1458 *image_view;
1459
1460 assert(image != (Image *) NULL);
1461 assert(image->signature == MagickSignature);
1462 if (image->debug != MagickFalse)
1463 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1464 assert(exception != (ExceptionInfo *) NULL);
1465 assert(exception->signature == MagickSignature);
1466 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1467 {
1468 InheritException(exception,&image->exception);
1469 return(MagickFalse);
1470 }
1471 status=MagickTrue;
1472 progress=0;
1473 random_info=AcquireRandomInfoThreadSet();
1474 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001475#if defined(MAGICKCORE_OPENMP_SUPPORT)
1476 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001477#endif
1478 for (y=0; y < (long) image->rows; y++)
1479 {
1480 register IndexPacket
1481 *__restrict indexes;
1482
1483 register long
1484 id,
1485 x;
1486
1487 register PixelPacket
1488 *__restrict q;
1489
1490 if (status == MagickFalse)
1491 continue;
1492 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1493 if (q == (PixelPacket *) NULL)
1494 {
1495 status=MagickFalse;
1496 continue;
1497 }
1498 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1499 id=GetOpenMPThreadId();
1500 for (x=0; x < (long) image->columns; x++)
1501 {
1502 if ((channel & RedChannel) != 0)
1503 q->red=ApplyEvaluateOperator(random_info[id],q->red,op,value);
1504 if ((channel & GreenChannel) != 0)
1505 q->green=ApplyEvaluateOperator(random_info[id],q->green,op,value);
1506 if ((channel & BlueChannel) != 0)
1507 q->blue=ApplyEvaluateOperator(random_info[id],q->blue,op,value);
1508 if ((channel & OpacityChannel) != 0)
1509 {
1510 if (image->matte == MagickFalse)
1511 q->opacity=ApplyEvaluateOperator(random_info[id],q->opacity,op,
1512 value);
1513 else
1514 q->opacity=(Quantum) QuantumRange-ApplyEvaluateOperator(
1515 random_info[id],(Quantum) (QuantumRange-q->opacity),op,value);
1516 }
1517 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
1518 indexes[x]=(IndexPacket) ApplyEvaluateOperator(random_info[id],
1519 indexes[x],op,value);
1520 q++;
1521 }
1522 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1523 status=MagickFalse;
1524 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1525 {
1526 MagickBooleanType
1527 proceed;
1528
cristyb5d5f722009-11-04 03:03:49 +00001529#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001530 #pragma omp critical (MagickCore_EvaluateImageChannel)
1531#endif
1532 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
1533 if (proceed == MagickFalse)
1534 status=MagickFalse;
1535 }
1536 }
1537 image_view=DestroyCacheView(image_view);
1538 random_info=DestroyRandomInfoThreadSet(random_info);
1539 return(status);
1540}
1541
1542/*
1543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1544% %
1545% %
1546% %
1547% F u n c t i o n I m a g e %
1548% %
1549% %
1550% %
1551%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1552%
1553% FunctionImage() applies a value to the image with an arithmetic, relational,
1554% or logical operator to an image. Use these operations to lighten or darken
1555% an image, to increase or decrease contrast in an image, or to produce the
1556% "negative" of an image.
1557%
1558% The format of the FunctionImageChannel method is:
1559%
1560% MagickBooleanType FunctionImage(Image *image,
1561% const MagickFunction function,const long number_parameters,
1562% const double *parameters,ExceptionInfo *exception)
1563% MagickBooleanType FunctionImageChannel(Image *image,
1564% const ChannelType channel,const MagickFunction function,
1565% const long number_parameters,const double *argument,
1566% ExceptionInfo *exception)
1567%
1568% A description of each parameter follows:
1569%
1570% o image: the image.
1571%
1572% o channel: the channel.
1573%
1574% o function: A channel function.
1575%
1576% o parameters: one or more parameters.
1577%
1578% o exception: return any errors or warnings in this structure.
1579%
1580*/
1581
1582static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristy67d99e62009-09-12 15:38:30 +00001583 const unsigned long number_parameters,const double *parameters,
cristy3ed852e2009-09-05 21:47:34 +00001584 ExceptionInfo *exception)
1585{
1586 MagickRealType
1587 result;
1588
1589 register long
1590 i;
1591
1592 (void) exception;
1593 result=0.0;
1594 switch (function)
1595 {
1596 case PolynomialFunction:
1597 {
1598 /*
1599 * Polynomial
1600 * Parameters: polynomial constants, highest to lowest order
1601 * For example: c0*x^3 + c1*x^2 + c2*x + c3
1602 */
1603 result=0.0;
1604 for (i=0; i < (long) number_parameters; i++)
1605 result = result*QuantumScale*pixel + parameters[i];
1606 result *= QuantumRange;
1607 break;
1608 }
1609 case SinusoidFunction:
1610 {
1611 /* Sinusoid Function
1612 * Parameters: Freq, Phase, Ampl, bias
1613 */
1614 double freq,phase,ampl,bias;
1615 freq = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
1616 phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
1617 ampl = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
1618 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
1619 result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
1620 (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
1621 break;
1622 }
1623 case ArcsinFunction:
1624 {
1625 /* Arcsin Function (peged at range limits for invalid results)
1626 * Parameters: Width, Center, Range, Bias
1627 */
1628 double width,range,center,bias;
1629 width = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
1630 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
1631 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
1632 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
1633 result = 2.0/width*(QuantumScale*pixel - center);
1634 if ( result <= -1.0 )
1635 result = bias - range/2.0;
1636 else if ( result >= 1.0 )
1637 result = bias + range/2.0;
1638 else
1639 result=range/MagickPI*asin((double)result) + bias;
1640 result *= QuantumRange;
1641 break;
1642 }
1643 case ArctanFunction:
1644 {
1645 /* Arctan Function
1646 * Parameters: Slope, Center, Range, Bias
1647 */
1648 double slope,range,center,bias;
1649 slope = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
1650 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
1651 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
1652 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
1653 result = MagickPI*slope*(QuantumScale*pixel - center);
1654 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
1655 result) + bias ) );
1656 break;
1657 }
1658 case UndefinedFunction:
1659 break;
1660 }
1661 return(RoundToQuantum(result));
1662}
1663
1664MagickExport MagickBooleanType FunctionImage(Image *image,
1665 const MagickFunction function,const unsigned long number_parameters,
1666 const double *parameters,ExceptionInfo *exception)
1667{
1668 MagickBooleanType
1669 status;
1670
1671 status=FunctionImageChannel(image,AllChannels,function,number_parameters,
1672 parameters,exception);
1673 return(status);
1674}
1675
1676MagickExport MagickBooleanType FunctionImageChannel(Image *image,
1677 const ChannelType channel,const MagickFunction function,
1678 const unsigned long number_parameters,const double *parameters,
1679 ExceptionInfo *exception)
1680{
1681#define FunctionImageTag "Function/Image "
1682
1683 long
1684 progress,
1685 y;
1686
1687 MagickBooleanType
1688 status;
1689
1690 CacheView
1691 *image_view;
1692
1693 assert(image != (Image *) NULL);
1694 assert(image->signature == MagickSignature);
1695 if (image->debug != MagickFalse)
1696 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1697 assert(exception != (ExceptionInfo *) NULL);
1698 assert(exception->signature == MagickSignature);
1699 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1700 {
1701 InheritException(exception,&image->exception);
1702 return(MagickFalse);
1703 }
1704 status=MagickTrue;
1705 progress=0;
1706 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001707#if defined(MAGICKCORE_OPENMP_SUPPORT)
1708 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001709#endif
1710 for (y=0; y < (long) image->rows; y++)
1711 {
1712 register IndexPacket
1713 *__restrict indexes;
1714
1715 register long
1716 x;
1717
1718 register PixelPacket
1719 *__restrict q;
1720
1721 if (status == MagickFalse)
1722 continue;
1723 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1724 if (q == (PixelPacket *) NULL)
1725 {
1726 status=MagickFalse;
1727 continue;
1728 }
1729 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1730 for (x=0; x < (long) image->columns; x++)
1731 {
1732 if ((channel & RedChannel) != 0)
1733 q->red=ApplyFunction(q->red,function,number_parameters,parameters,
1734 exception);
1735 if ((channel & GreenChannel) != 0)
1736 q->green=ApplyFunction(q->green,function,number_parameters,parameters,
1737 exception);
1738 if ((channel & BlueChannel) != 0)
1739 q->blue=ApplyFunction(q->blue,function,number_parameters,parameters,
1740 exception);
1741 if ((channel & OpacityChannel) != 0)
1742 {
1743 if (image->matte == MagickFalse)
1744 q->opacity=ApplyFunction(q->opacity,function,number_parameters,
1745 parameters,exception);
1746 else
1747 q->opacity=(Quantum) QuantumRange-ApplyFunction((Quantum) (
1748 QuantumRange-q->opacity),function,number_parameters,parameters,
1749 exception);
1750 }
1751 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
1752 indexes[x]=(IndexPacket) ApplyFunction(indexes[x],function,
1753 number_parameters,parameters,exception);
1754 q++;
1755 }
1756 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1757 status=MagickFalse;
1758 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1759 {
1760 MagickBooleanType
1761 proceed;
1762
cristyb5d5f722009-11-04 03:03:49 +00001763#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001764 #pragma omp critical (MagickCore_FunctionImageChannel)
1765#endif
1766 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1767 if (proceed == MagickFalse)
1768 status=MagickFalse;
1769 }
1770 }
1771 image_view=DestroyCacheView(image_view);
1772 return(status);
1773}
1774
1775/*
1776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777% %
1778% %
1779% %
1780+ F x E v a l u a t e C h a n n e l E x p r e s s i o n %
1781% %
1782% %
1783% %
1784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1785%
1786% FxEvaluateChannelExpression() evaluates an expression and returns the
1787% results.
1788%
1789% The format of the FxEvaluateExpression method is:
1790%
1791% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
1792% const ChannelType channel,const long x,const long y,
1793% MagickRealType *alpha,Exceptioninfo *exception)
1794% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1795% MagickRealType *alpha,Exceptioninfo *exception)
1796%
1797% A description of each parameter follows:
1798%
1799% o fx_info: the fx info.
1800%
1801% o channel: the channel.
1802%
1803% o x,y: the pixel position.
1804%
1805% o alpha: the result.
1806%
1807% o exception: return any errors or warnings in this structure.
1808%
1809*/
1810
1811static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
1812 ChannelType channel,const char *symbol,ExceptionInfo *exception)
1813{
1814 char
1815 key[MaxTextExtent],
1816 statistic[MaxTextExtent];
1817
1818 const char
1819 *value;
1820
1821 register const char
1822 *p;
1823
1824 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1825 if (*p == '.')
1826 switch (*++p) /* e.g. depth.r */
1827 {
1828 case 'r': channel=RedChannel; break;
1829 case 'g': channel=GreenChannel; break;
1830 case 'b': channel=BlueChannel; break;
1831 case 'c': channel=CyanChannel; break;
1832 case 'm': channel=MagentaChannel; break;
1833 case 'y': channel=YellowChannel; break;
1834 case 'k': channel=BlackChannel; break;
1835 default: break;
1836 }
1837 (void) FormatMagickString(key,MaxTextExtent,"%p.%ld.%s",image,(long) channel,
1838 symbol);
1839 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1840 if (value != (const char *) NULL)
1841 return(QuantumScale*atof(value));
1842 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1843 if (LocaleNCompare(symbol,"depth",5) == 0)
1844 {
1845 unsigned long
1846 depth;
1847
1848 depth=GetImageChannelDepth(image,channel,exception);
1849 (void) FormatMagickString(statistic,MaxTextExtent,"%lu",depth);
1850 }
1851 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1852 {
1853 double
1854 kurtosis,
1855 skewness;
1856
1857 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1858 exception);
1859 (void) FormatMagickString(statistic,MaxTextExtent,"%g",kurtosis);
1860 }
1861 if (LocaleNCompare(symbol,"maxima",6) == 0)
1862 {
1863 double
1864 maxima,
1865 minima;
1866
1867 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
1868 (void) FormatMagickString(statistic,MaxTextExtent,"%g",maxima);
1869 }
1870 if (LocaleNCompare(symbol,"mean",4) == 0)
1871 {
1872 double
1873 mean,
1874 standard_deviation;
1875
1876 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1877 exception);
1878 (void) FormatMagickString(statistic,MaxTextExtent,"%g",mean);
1879 }
1880 if (LocaleNCompare(symbol,"minima",6) == 0)
1881 {
1882 double
1883 maxima,
1884 minima;
1885
1886 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
1887 (void) FormatMagickString(statistic,MaxTextExtent,"%g",minima);
1888 }
1889 if (LocaleNCompare(symbol,"skewness",8) == 0)
1890 {
1891 double
1892 kurtosis,
1893 skewness;
1894
1895 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1896 exception);
1897 (void) FormatMagickString(statistic,MaxTextExtent,"%g",skewness);
1898 }
1899 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1900 {
1901 double
1902 mean,
1903 standard_deviation;
1904
1905 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1906 exception);
1907 (void) FormatMagickString(statistic,MaxTextExtent,"%g",
1908 standard_deviation);
1909 }
1910 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1911 ConstantString(statistic));
1912 return(QuantumScale*atof(statistic));
1913}
1914
1915static MagickRealType
1916 FxEvaluateSubexpression(FxInfo *,const ChannelType,const long,const long,
1917 const char *,MagickRealType *,ExceptionInfo *);
1918
1919static inline MagickRealType FxMax(FxInfo *fx_info,const ChannelType channel,
1920 const long x,const long y,const char *expression,ExceptionInfo *exception)
1921{
1922 MagickRealType
1923 alpha,
1924 beta;
1925
1926 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1927 return((MagickRealType) MagickMax((double) alpha,(double) beta));
1928}
1929
1930static inline MagickRealType FxMin(FxInfo *fx_info,ChannelType channel,
1931 const long x,const long y,const char *expression,ExceptionInfo *exception)
1932{
1933 MagickRealType
1934 alpha,
1935 beta;
1936
1937 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1938 return((MagickRealType) MagickMin((double) alpha,(double) beta));
1939}
1940
1941static inline const char *FxSubexpression(const char *expression,
1942 ExceptionInfo *exception)
1943{
1944 const char
1945 *subexpression;
1946
1947 register long
1948 level;
1949
1950 level=0;
1951 subexpression=expression;
1952 while ((*subexpression != '\0') &&
1953 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1954 {
1955 if (strchr("(",(int) *subexpression) != (char *) NULL)
1956 level++;
1957 else
1958 if (strchr(")",(int) *subexpression) != (char *) NULL)
1959 level--;
1960 subexpression++;
1961 }
1962 if (*subexpression == '\0')
1963 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1964 "UnbalancedParenthesis","`%s'",expression);
1965 return(subexpression);
1966}
1967
1968static MagickRealType FxGetSymbol(FxInfo *fx_info,const ChannelType channel,
1969 const long x,const long y,const char *expression,ExceptionInfo *exception)
1970{
1971 char
1972 *q,
1973 subexpression[MaxTextExtent],
1974 symbol[MaxTextExtent];
1975
1976 const char
1977 *p,
1978 *value;
1979
1980 Image
1981 *image;
1982
1983 MagickPixelPacket
1984 pixel;
1985
1986 MagickRealType
1987 alpha,
1988 beta;
1989
1990 PointInfo
1991 point;
1992
1993 register long
1994 i;
1995
1996 size_t
1997 length;
1998
1999 unsigned long
2000 level;
2001
2002 p=expression;
2003 i=GetImageIndexInList(fx_info->images);
2004 level=0;
2005 point.x=(double) x;
2006 point.y=(double) y;
2007 if (isalpha((int) *(p+1)) == 0)
2008 {
2009 if (strchr("suv",(int) *p) != (char *) NULL)
2010 {
2011 switch (*p)
2012 {
2013 case 's':
2014 default:
2015 {
2016 i=GetImageIndexInList(fx_info->images);
2017 break;
2018 }
2019 case 'u': i=0; break;
2020 case 'v': i=1; break;
2021 }
2022 p++;
2023 if (*p == '[')
2024 {
2025 level++;
2026 q=subexpression;
2027 for (p++; *p != '\0'; )
2028 {
2029 if (*p == '[')
2030 level++;
2031 else
2032 if (*p == ']')
2033 {
2034 level--;
2035 if (level == 0)
2036 break;
2037 }
2038 *q++=(*p++);
2039 }
2040 *q='\0';
2041 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
2042 &beta,exception);
2043 i=(long) (alpha+0.5);
2044 p++;
2045 }
2046 if (*p == '.')
2047 p++;
2048 }
2049 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
2050 {
2051 p++;
2052 if (*p == '{')
2053 {
2054 level++;
2055 q=subexpression;
2056 for (p++; *p != '\0'; )
2057 {
2058 if (*p == '{')
2059 level++;
2060 else
2061 if (*p == '}')
2062 {
2063 level--;
2064 if (level == 0)
2065 break;
2066 }
2067 *q++=(*p++);
2068 }
2069 *q='\0';
2070 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
2071 &beta,exception);
2072 point.x=alpha;
2073 point.y=beta;
2074 p++;
2075 }
2076 else
2077 if (*p == '[')
2078 {
2079 level++;
2080 q=subexpression;
2081 for (p++; *p != '\0'; )
2082 {
2083 if (*p == '[')
2084 level++;
2085 else
2086 if (*p == ']')
2087 {
2088 level--;
2089 if (level == 0)
2090 break;
2091 }
2092 *q++=(*p++);
2093 }
2094 *q='\0';
2095 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
2096 &beta,exception);
2097 point.x+=alpha;
2098 point.y+=beta;
2099 p++;
2100 }
2101 if (*p == '.')
2102 p++;
2103 }
2104 }
2105 length=GetImageListLength(fx_info->images);
2106 while (i < 0)
2107 i+=(long) length;
2108 i%=length;
2109 image=GetImageFromList(fx_info->images,i);
2110 if (image == (Image *) NULL)
2111 {
2112 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2113 "NoSuchImage","`%s'",expression);
2114 return(0.0);
2115 }
2116 (void) ResamplePixelColor(fx_info->resample_filter[i],point.x,point.y,&pixel);
2117 if ((strlen(p) > 2) &&
2118 (LocaleCompare(p,"intensity") != 0) &&
2119 (LocaleCompare(p,"luminance") != 0) &&
2120 (LocaleCompare(p,"hue") != 0) &&
2121 (LocaleCompare(p,"saturation") != 0) &&
2122 (LocaleCompare(p,"lightness") != 0))
2123 {
2124 char
2125 name[MaxTextExtent];
2126
2127 (void) CopyMagickString(name,p,MaxTextExtent);
2128 for (q=name+(strlen(name)-1); q > name; q--)
2129 {
2130 if (*q == ')')
2131 break;
2132 if (*q == '.')
2133 {
2134 *q='\0';
2135 break;
2136 }
2137 }
2138 if ((strlen(name) > 2) &&
2139 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
2140 {
2141 MagickPixelPacket
2142 *color;
2143
2144 color=(MagickPixelPacket *) GetValueFromSplayTree(fx_info->colors,
2145 name);
2146 if (color != (MagickPixelPacket *) NULL)
2147 {
2148 pixel=(*color);
2149 p+=strlen(name);
2150 }
2151 else
2152 if (QueryMagickColor(name,&pixel,fx_info->exception) != MagickFalse)
2153 {
2154 (void) AddValueToSplayTree(fx_info->colors,ConstantString(name),
2155 CloneMagickPixelPacket(&pixel));
2156 p+=strlen(name);
2157 }
2158 }
2159 }
2160 (void) CopyMagickString(symbol,p,MaxTextExtent);
2161 StripString(symbol);
2162 if (*symbol == '\0')
2163 {
2164 switch (channel)
2165 {
2166 case RedChannel: return(QuantumScale*pixel.red);
2167 case GreenChannel: return(QuantumScale*pixel.green);
2168 case BlueChannel: return(QuantumScale*pixel.blue);
2169 case OpacityChannel:
2170 {
2171 if (pixel.matte == MagickFalse)
2172 {
2173 fx_info->matte=MagickFalse;
2174 return(1.0);
2175 }
2176 return((MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity)));
2177 }
2178 case IndexChannel:
2179 {
2180 if (image->colorspace != CMYKColorspace)
2181 {
2182 (void) ThrowMagickException(exception,GetMagickModule(),
2183 OptionError,"ColorSeparatedImageRequired","`%s'",
2184 image->filename);
2185 return(0.0);
2186 }
2187 return(QuantumScale*pixel.index);
2188 }
2189 default:
2190 break;
2191 }
2192 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2193 "UnableToParseExpression","`%s'",p);
2194 return(0.0);
2195 }
2196 switch (*symbol)
2197 {
2198 case 'A':
2199 case 'a':
2200 {
2201 if (LocaleCompare(symbol,"a") == 0)
2202 return((MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity)));
2203 break;
2204 }
2205 case 'B':
2206 case 'b':
2207 {
2208 if (LocaleCompare(symbol,"b") == 0)
2209 return(QuantumScale*pixel.blue);
2210 break;
2211 }
2212 case 'C':
2213 case 'c':
2214 {
2215 if (LocaleNCompare(symbol,"channel",7) == 0)
2216 {
2217 GeometryInfo
2218 channel_info;
2219
2220 MagickStatusType
2221 flags;
2222
2223 flags=ParseGeometry(symbol+7,&channel_info);
2224 if (image->colorspace == CMYKColorspace)
2225 switch (channel)
2226 {
2227 case CyanChannel:
2228 {
2229 if ((flags & RhoValue) == 0)
2230 return(0.0);
2231 return(channel_info.rho);
2232 }
2233 case MagentaChannel:
2234 {
2235 if ((flags & SigmaValue) == 0)
2236 return(0.0);
2237 return(channel_info.sigma);
2238 }
2239 case YellowChannel:
2240 {
2241 if ((flags & XiValue) == 0)
2242 return(0.0);
2243 return(channel_info.xi);
2244 }
2245 case BlackChannel:
2246 {
2247 if ((flags & PsiValue) == 0)
2248 return(0.0);
2249 return(channel_info.psi);
2250 }
2251 case OpacityChannel:
2252 {
2253 if ((flags & ChiValue) == 0)
2254 return(0.0);
2255 return(channel_info.chi);
2256 }
2257 default:
2258 return(0.0);
2259 }
2260 switch (channel)
2261 {
2262 case RedChannel:
2263 {
2264 if ((flags & RhoValue) == 0)
2265 return(0.0);
2266 return(channel_info.rho);
2267 }
2268 case GreenChannel:
2269 {
2270 if ((flags & SigmaValue) == 0)
2271 return(0.0);
2272 return(channel_info.sigma);
2273 }
2274 case BlueChannel:
2275 {
2276 if ((flags & XiValue) == 0)
2277 return(0.0);
2278 return(channel_info.xi);
2279 }
2280 case OpacityChannel:
2281 {
2282 if ((flags & PsiValue) == 0)
2283 return(0.0);
2284 return(channel_info.psi);
2285 }
2286 case IndexChannel:
2287 {
2288 if ((flags & ChiValue) == 0)
2289 return(0.0);
2290 return(channel_info.chi);
2291 }
2292 default:
2293 return(0.0);
2294 }
2295 return(0.0);
2296 }
2297 if (LocaleCompare(symbol,"c") == 0)
2298 return(QuantumScale*pixel.red);
2299 break;
2300 }
2301 case 'D':
2302 case 'd':
2303 {
2304 if (LocaleNCompare(symbol,"depth",5) == 0)
2305 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2306 break;
2307 }
2308 case 'G':
2309 case 'g':
2310 {
2311 if (LocaleCompare(symbol,"g") == 0)
2312 return(QuantumScale*pixel.green);
2313 break;
2314 }
2315 case 'K':
2316 case 'k':
2317 {
2318 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
2319 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2320 if (LocaleCompare(symbol,"k") == 0)
2321 {
2322 if (image->colorspace != CMYKColorspace)
2323 {
2324 (void) ThrowMagickException(exception,GetMagickModule(),
2325 OptionError,"ColorSeparatedImageRequired","`%s'",
2326 image->filename);
2327 return(0.0);
2328 }
2329 return(QuantumScale*pixel.index);
2330 }
2331 break;
2332 }
2333 case 'H':
2334 case 'h':
2335 {
2336 if (LocaleCompare(symbol,"h") == 0)
2337 return((MagickRealType) image->rows);
2338 if (LocaleCompare(symbol,"hue") == 0)
2339 {
2340 double
2341 hue,
2342 lightness,
2343 saturation;
2344
2345 ConvertRGBToHSL(RoundToQuantum(pixel.red),RoundToQuantum(pixel.green),
2346 RoundToQuantum(pixel.blue),&hue,&saturation,&lightness);
2347 return(hue);
2348 }
2349 break;
2350 }
2351 case 'I':
2352 case 'i':
2353 {
2354 if ((LocaleCompare(symbol,"image.depth") == 0) ||
2355 (LocaleCompare(symbol,"image.minima") == 0) ||
2356 (LocaleCompare(symbol,"image.maxima") == 0) ||
2357 (LocaleCompare(symbol,"image.mean") == 0) ||
2358 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
2359 (LocaleCompare(symbol,"image.skewness") == 0) ||
2360 (LocaleCompare(symbol,"image.standard_deviation") == 0))
2361 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
2362 if (LocaleCompare(symbol,"image.resolution.x") == 0)
2363 return(image->x_resolution);
2364 if (LocaleCompare(symbol,"image.resolution.y") == 0)
2365 return(image->y_resolution);
2366 if (LocaleCompare(symbol,"intensity") == 0)
2367 return(QuantumScale*MagickPixelIntensityToQuantum(&pixel));
2368 if (LocaleCompare(symbol,"i") == 0)
2369 return((MagickRealType) x);
2370 break;
2371 }
2372 case 'J':
2373 case 'j':
2374 {
2375 if (LocaleCompare(symbol,"j") == 0)
2376 return((MagickRealType) y);
2377 break;
2378 }
2379 case 'L':
2380 case 'l':
2381 {
2382 if (LocaleCompare(symbol,"lightness") == 0)
2383 {
2384 double
2385 hue,
2386 lightness,
2387 saturation;
2388
2389 ConvertRGBToHSL(RoundToQuantum(pixel.red),RoundToQuantum(pixel.green),
2390 RoundToQuantum(pixel.blue),&hue,&saturation,&lightness);
2391 return(lightness);
2392 }
2393 if (LocaleCompare(symbol,"luminance") == 0)
2394 {
2395 double
2396 luminence;
2397
2398 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
2399 return(QuantumScale*luminence);
2400 }
2401 break;
2402 }
2403 case 'M':
2404 case 'm':
2405 {
2406 if (LocaleNCompare(symbol,"maxima",6) == 0)
2407 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2408 if (LocaleNCompare(symbol,"mean",4) == 0)
2409 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2410 if (LocaleNCompare(symbol,"minima",6) == 0)
2411 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2412 if (LocaleCompare(symbol,"m") == 0)
2413 return(QuantumScale*pixel.blue);
2414 break;
2415 }
2416 case 'N':
2417 case 'n':
2418 {
2419 if (LocaleCompare(symbol,"n") == 0)
2420 return((MagickRealType) GetImageListLength(fx_info->images));
2421 break;
2422 }
2423 case 'O':
2424 case 'o':
2425 {
2426 if (LocaleCompare(symbol,"o") == 0)
2427 return(QuantumScale*pixel.opacity);
2428 break;
2429 }
2430 case 'P':
2431 case 'p':
2432 {
2433 if (LocaleCompare(symbol,"page.height") == 0)
2434 return((MagickRealType) image->page.height);
2435 if (LocaleCompare(symbol,"page.width") == 0)
2436 return((MagickRealType) image->page.width);
2437 if (LocaleCompare(symbol,"page.x") == 0)
2438 return((MagickRealType) image->page.x);
2439 if (LocaleCompare(symbol,"page.y") == 0)
2440 return((MagickRealType) image->page.y);
2441 break;
2442 }
2443 case 'R':
2444 case 'r':
2445 {
2446 if (LocaleCompare(symbol,"resolution.x") == 0)
2447 return(image->x_resolution);
2448 if (LocaleCompare(symbol,"resolution.y") == 0)
2449 return(image->y_resolution);
2450 if (LocaleCompare(symbol,"r") == 0)
2451 return(QuantumScale*pixel.red);
2452 break;
2453 }
2454 case 'S':
2455 case 's':
2456 {
2457 if (LocaleCompare(symbol,"saturation") == 0)
2458 {
2459 double
2460 hue,
2461 lightness,
2462 saturation;
2463
2464 ConvertRGBToHSL(RoundToQuantum(pixel.red),RoundToQuantum(pixel.green),
2465 RoundToQuantum(pixel.blue),&hue,&saturation,&lightness);
2466 return(saturation);
2467 }
2468 if (LocaleNCompare(symbol,"skewness",8) == 0)
2469 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2470 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
2471 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2472 break;
2473 }
2474 case 'T':
2475 case 't':
2476 {
2477 if (LocaleCompare(symbol,"t") == 0)
2478 return((MagickRealType) fx_info->images->scene);
2479 break;
2480 }
2481 case 'W':
2482 case 'w':
2483 {
2484 if (LocaleCompare(symbol,"w") == 0)
2485 return((MagickRealType) image->columns);
2486 break;
2487 }
2488 case 'Y':
2489 case 'y':
2490 {
2491 if (LocaleCompare(symbol,"y") == 0)
2492 return(QuantumScale*pixel.green);
2493 break;
2494 }
2495 case 'Z':
2496 case 'z':
2497 {
2498 if (LocaleCompare(symbol,"z") == 0)
2499 {
2500 MagickRealType
2501 depth;
2502
2503 depth=(MagickRealType) GetImageChannelDepth(image,channel,
2504 fx_info->exception);
2505 return(depth);
2506 }
2507 break;
2508 }
2509 default:
2510 break;
2511 }
2512 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
2513 if (value != (const char *) NULL)
2514 return((MagickRealType) atof(value));
2515 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2516 "UnableToParseExpression","`%s'",symbol);
2517 return(0.0);
2518}
2519
2520static const char *FxOperatorPrecedence(const char *expression,
2521 ExceptionInfo *exception)
2522{
2523 typedef enum
2524 {
2525 UndefinedPrecedence,
2526 NullPrecedence,
2527 BitwiseComplementPrecedence,
2528 ExponentPrecedence,
2529 MultiplyPrecedence,
2530 AdditionPrecedence,
2531 ShiftPrecedence,
2532 RelationalPrecedence,
2533 EquivalencyPrecedence,
2534 BitwiseAndPrecedence,
2535 BitwiseOrPrecedence,
2536 LogicalAndPrecedence,
2537 LogicalOrPrecedence,
2538 TernaryPrecedence,
2539 AssignmentPrecedence,
2540 CommaPrecedence,
2541 SeparatorPrecedence
2542 } FxPrecedence;
2543
2544 FxPrecedence
2545 precedence,
2546 target;
2547
2548 register const char
2549 *subexpression;
2550
2551 register int
2552 c;
2553
2554 unsigned long
2555 level;
2556
2557 c=0;
2558 level=0;
2559 subexpression=(const char *) NULL;
2560 target=NullPrecedence;
2561 while (*expression != '\0')
2562 {
2563 precedence=UndefinedPrecedence;
2564 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
2565 {
2566 expression++;
2567 continue;
2568 }
2569 if (LocaleNCompare(expression,"atan2",5) == 0)
2570 {
2571 expression+=5;
2572 continue;
2573 }
2574 if ((c == (int) '{') || (c == (int) '['))
2575 level++;
2576 else
2577 if ((c == (int) '}') || (c == (int) ']'))
2578 level--;
2579 if (level == 0)
2580 switch ((unsigned char) *expression)
2581 {
2582 case '~':
2583 case '!':
2584 {
2585 precedence=BitwiseComplementPrecedence;
2586 break;
2587 }
2588 case '^':
2589 {
2590 precedence=ExponentPrecedence;
2591 break;
2592 }
2593 default:
2594 {
2595 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
2596 (strchr(")",c) != (char *) NULL))) &&
2597 (((islower((int) ((char) *expression)) != 0) ||
2598 (strchr("(",(int) *expression) != (char *) NULL)) ||
2599 ((isdigit((int) ((char) c)) == 0) &&
2600 (isdigit((int) ((char) *expression)) != 0))) &&
2601 (strchr("xy",(int) *expression) == (char *) NULL))
2602 precedence=MultiplyPrecedence;
2603 break;
2604 }
2605 case '*':
2606 case '/':
2607 case '%':
2608 {
2609 precedence=MultiplyPrecedence;
2610 break;
2611 }
2612 case '+':
2613 case '-':
2614 {
2615 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
2616 (isalpha(c) != 0))
2617 precedence=AdditionPrecedence;
2618 break;
2619 }
2620 case LeftShiftOperator:
2621 case RightShiftOperator:
2622 {
2623 precedence=ShiftPrecedence;
2624 break;
2625 }
2626 case '<':
2627 case LessThanEqualOperator:
2628 case GreaterThanEqualOperator:
2629 case '>':
2630 {
2631 precedence=RelationalPrecedence;
2632 break;
2633 }
2634 case EqualOperator:
2635 case NotEqualOperator:
2636 {
2637 precedence=EquivalencyPrecedence;
2638 break;
2639 }
2640 case '&':
2641 {
2642 precedence=BitwiseAndPrecedence;
2643 break;
2644 }
2645 case '|':
2646 {
2647 precedence=BitwiseOrPrecedence;
2648 break;
2649 }
2650 case LogicalAndOperator:
2651 {
2652 precedence=LogicalAndPrecedence;
2653 break;
2654 }
2655 case LogicalOrOperator:
2656 {
2657 precedence=LogicalOrPrecedence;
2658 break;
2659 }
2660 case ':':
2661 case '?':
2662 {
2663 precedence=TernaryPrecedence;
2664 break;
2665 }
2666 case '=':
2667 {
2668 precedence=AssignmentPrecedence;
2669 break;
2670 }
2671 case ',':
2672 {
2673 precedence=CommaPrecedence;
2674 break;
2675 }
2676 case ';':
2677 {
2678 precedence=SeparatorPrecedence;
2679 break;
2680 }
2681 }
2682 if ((precedence == BitwiseComplementPrecedence) ||
2683 (precedence == TernaryPrecedence) ||
2684 (precedence == AssignmentPrecedence))
2685 {
2686 if (precedence > target)
2687 {
2688 /*
2689 Right-to-left associativity.
2690 */
2691 target=precedence;
2692 subexpression=expression;
2693 }
2694 }
2695 else
2696 if (precedence >= target)
2697 {
2698 /*
2699 Left-to-right associativity.
2700 */
2701 target=precedence;
2702 subexpression=expression;
2703 }
2704 if (strchr("(",(int) *expression) != (char *) NULL)
2705 expression=FxSubexpression(expression,exception);
2706 c=(int) (*expression++);
2707 }
2708 return(subexpression);
2709}
2710
2711static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
2712 const ChannelType channel,const long x,const long y,const char *expression,
2713 MagickRealType *beta,ExceptionInfo *exception)
2714{
2715 char
2716 *q,
2717 subexpression[MaxTextExtent];
2718
2719 MagickRealType
2720 alpha,
2721 gamma;
2722
2723 register const char
2724 *p;
2725
2726 *beta=0.0;
2727 if (exception->severity != UndefinedException)
2728 return(0.0);
2729 while (isspace((int) *expression) != 0)
2730 expression++;
2731 if (*expression == '\0')
2732 {
2733 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2734 "MissingExpression","`%s'",expression);
2735 return(0.0);
2736 }
2737 p=FxOperatorPrecedence(expression,exception);
2738 if (p != (const char *) NULL)
2739 {
2740 (void) CopyMagickString(subexpression,expression,(size_t)
2741 (p-expression+1));
2742 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2743 exception);
2744 switch ((unsigned char) *p)
2745 {
2746 case '~':
2747 {
2748 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2749 *beta=(MagickRealType) (~(unsigned long) *beta);
2750 return(*beta);
2751 }
2752 case '!':
2753 {
2754 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2755 return(*beta == 0.0 ? 1.0 : 0.0);
2756 }
2757 case '^':
2758 {
2759 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2760 channel,x,y,++p,beta,exception));
2761 return(*beta);
2762 }
2763 case '*':
2764 {
2765 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2766 return(alpha*(*beta));
2767 }
2768 case '/':
2769 {
2770 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2771 if (*beta == 0.0)
2772 {
2773 if (exception->severity == UndefinedException)
2774 (void) ThrowMagickException(exception,GetMagickModule(),
2775 OptionError,"DivideByZero","`%s'",expression);
2776 return(0.0);
2777 }
2778 return(alpha/(*beta));
2779 }
2780 case '%':
2781 {
2782 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2783 *beta=fabs(floor(((double) *beta)+0.5));
2784 if (*beta == 0.0)
2785 {
2786 (void) ThrowMagickException(exception,GetMagickModule(),
2787 OptionError,"DivideByZero","`%s'",expression);
2788 return(0.0);
2789 }
2790 return(fmod((double) alpha,(double) *beta));
2791 }
2792 case '+':
2793 {
2794 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2795 return(alpha+(*beta));
2796 }
2797 case '-':
2798 {
2799 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2800 return(alpha-(*beta));
2801 }
2802 case LeftShiftOperator:
2803 {
2804 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2805 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) << (unsigned long)
2806 (gamma+0.5));
2807 return(*beta);
2808 }
2809 case RightShiftOperator:
2810 {
2811 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2812 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) >> (unsigned long)
2813 (gamma+0.5));
2814 return(*beta);
2815 }
2816 case '<':
2817 {
2818 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2819 return(alpha < *beta ? 1.0 : 0.0);
2820 }
2821 case LessThanEqualOperator:
2822 {
2823 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2824 return(alpha <= *beta ? 1.0 : 0.0);
2825 }
2826 case '>':
2827 {
2828 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2829 return(alpha > *beta ? 1.0 : 0.0);
2830 }
2831 case GreaterThanEqualOperator:
2832 {
2833 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2834 return(alpha >= *beta ? 1.0 : 0.0);
2835 }
2836 case EqualOperator:
2837 {
2838 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2839 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2840 }
2841 case NotEqualOperator:
2842 {
2843 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2844 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2845 }
2846 case '&':
2847 {
2848 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2849 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) & (unsigned long)
2850 (gamma+0.5));
2851 return(*beta);
2852 }
2853 case '|':
2854 {
2855 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2856 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) | (unsigned long)
2857 (gamma+0.5));
2858 return(*beta);
2859 }
2860 case LogicalAndOperator:
2861 {
2862 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2863 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2864 return(*beta);
2865 }
2866 case LogicalOrOperator:
2867 {
2868 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2869 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2870 return(*beta);
2871 }
2872 case '?':
2873 {
2874 MagickRealType
2875 gamma;
2876
2877 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2878 q=subexpression;
2879 p=StringToken(":",&q);
2880 if (q == (char *) NULL)
2881 {
2882 (void) ThrowMagickException(exception,GetMagickModule(),
2883 OptionError,"UnableToParseExpression","`%s'",subexpression);
2884 return(0.0);
2885 }
2886 if (fabs((double) alpha) > MagickEpsilon)
2887 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2888 else
2889 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2890 return(gamma);
2891 }
2892 case '=':
2893 {
2894 char
2895 numeric[MaxTextExtent];
2896
2897 q=subexpression;
2898 while (isalpha((int) ((unsigned char) *q)) != 0)
2899 q++;
2900 if (*q != '\0')
2901 {
2902 (void) ThrowMagickException(exception,GetMagickModule(),
2903 OptionError,"UnableToParseExpression","`%s'",subexpression);
2904 return(0.0);
2905 }
2906 ClearMagickException(exception);
2907 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2908 (void) FormatMagickString(numeric,MaxTextExtent,"%g",(double) *beta);
2909 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2910 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2911 subexpression),ConstantString(numeric));
2912 return(*beta);
2913 }
2914 case ',':
2915 {
2916 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2917 return(alpha);
2918 }
2919 case ';':
2920 {
2921 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2922 return(*beta);
2923 }
2924 default:
2925 {
2926 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2927 exception);
2928 return(gamma);
2929 }
2930 }
2931 }
2932 if (strchr("(",(int) *expression) != (char *) NULL)
2933 {
2934 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2935 subexpression[strlen(subexpression)-1]='\0';
2936 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2937 exception);
2938 return(gamma);
2939 }
2940 switch (*expression)
2941 {
2942 case '+':
2943 {
2944 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2945 exception);
2946 return(1.0*gamma);
2947 }
2948 case '-':
2949 {
2950 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2951 exception);
2952 return(-1.0*gamma);
2953 }
2954 case '~':
2955 {
2956 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2957 exception);
2958 return((MagickRealType) (~(unsigned long) (gamma+0.5)));
2959 }
2960 case 'A':
2961 case 'a':
2962 {
2963 if (LocaleNCompare(expression,"abs",3) == 0)
2964 {
2965 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2966 exception);
2967 return((MagickRealType) fabs((double) alpha));
2968 }
2969 if (LocaleNCompare(expression,"acos",4) == 0)
2970 {
2971 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2972 exception);
2973 return((MagickRealType) acos((double) alpha));
2974 }
2975 if (LocaleNCompare(expression,"asin",4) == 0)
2976 {
2977 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2978 exception);
2979 return((MagickRealType) asin((double) alpha));
2980 }
2981 if (LocaleNCompare(expression,"alt",3) == 0)
2982 {
2983 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2984 exception);
2985 return(((long) alpha) & 0x01 ? -1.0 : 1.0);
2986 }
2987 if (LocaleNCompare(expression,"atan2",5) == 0)
2988 {
2989 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2990 exception);
2991 return((MagickRealType) atan2((double) alpha,(double) *beta));
2992 }
2993 if (LocaleNCompare(expression,"atan",4) == 0)
2994 {
2995 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2996 exception);
2997 return((MagickRealType) atan((double) alpha));
2998 }
2999 if (LocaleCompare(expression,"a") == 0)
3000 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3001 break;
3002 }
3003 case 'B':
3004 case 'b':
3005 {
3006 if (LocaleCompare(expression,"b") == 0)
3007 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3008 break;
3009 }
3010 case 'C':
3011 case 'c':
3012 {
3013 if (LocaleNCompare(expression,"ceil",4) == 0)
3014 {
3015 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
3016 exception);
3017 return((MagickRealType) ceil((double) alpha));
3018 }
3019 if (LocaleNCompare(expression,"cosh",4) == 0)
3020 {
3021 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
3022 exception);
3023 return((MagickRealType) cosh((double) alpha));
3024 }
3025 if (LocaleNCompare(expression,"cos",3) == 0)
3026 {
3027 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
3028 exception);
3029 return((MagickRealType) cos((double) alpha));
3030 }
3031 if (LocaleCompare(expression,"c") == 0)
3032 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3033 break;
3034 }
3035 case 'D':
3036 case 'd':
3037 {
3038 if (LocaleNCompare(expression,"debug",5) == 0)
3039 {
3040 const char
3041 *type;
3042
3043 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
3044 exception);
3045 if (fx_info->images->colorspace == CMYKColorspace)
3046 switch (channel)
3047 {
3048 case CyanChannel: type="cyan"; break;
3049 case MagentaChannel: type="magenta"; break;
3050 case YellowChannel: type="yellow"; break;
3051 case OpacityChannel: type="opacity"; break;
3052 case BlackChannel: type="black"; break;
3053 default: type="unknown"; break;
3054 }
3055 else
3056 switch (channel)
3057 {
3058 case RedChannel: type="red"; break;
3059 case GreenChannel: type="green"; break;
3060 case BlueChannel: type="blue"; break;
3061 case OpacityChannel: type="opacity"; break;
3062 default: type="unknown"; break;
3063 }
3064 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
3065 if (strlen(subexpression) > 1)
3066 subexpression[strlen(subexpression)-1]='\0';
3067 if (fx_info->file != (FILE *) NULL)
3068 (void) fprintf(fx_info->file,"%s[%ld,%ld].%s: %s=%g\n",
3069 fx_info->images->filename,x,y,type,subexpression,(double) alpha);
3070 return(0.0);
3071 }
3072 break;
3073 }
3074 case 'E':
3075 case 'e':
3076 {
3077 if (LocaleCompare(expression,"epsilon") == 0)
3078 return((MagickRealType) MagickEpsilon);
3079 if (LocaleNCompare(expression,"exp",3) == 0)
3080 {
3081 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
3082 exception);
3083 return((MagickRealType) exp((double) alpha));
3084 }
3085 if (LocaleCompare(expression,"e") == 0)
3086 return((MagickRealType) 2.7182818284590452354);
3087 break;
3088 }
3089 case 'F':
3090 case 'f':
3091 {
3092 if (LocaleNCompare(expression,"floor",5) == 0)
3093 {
3094 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
3095 exception);
3096 return((MagickRealType) floor((double) alpha));
3097 }
3098 break;
3099 }
3100 case 'G':
3101 case 'g':
3102 {
3103 if (LocaleCompare(expression,"g") == 0)
3104 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3105 break;
3106 }
3107 case 'H':
3108 case 'h':
3109 {
3110 if (LocaleCompare(expression,"h") == 0)
3111 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3112 if (LocaleCompare(expression,"hue") == 0)
3113 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3114 if (LocaleNCompare(expression,"hypot",5) == 0)
3115 {
3116 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
3117 exception);
3118 return((MagickRealType) hypot((double) alpha,(double) *beta));
3119 }
3120 break;
3121 }
3122 case 'K':
3123 case 'k':
3124 {
3125 if (LocaleCompare(expression,"k") == 0)
3126 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3127 break;
3128 }
3129 case 'I':
3130 case 'i':
3131 {
3132 if (LocaleCompare(expression,"intensity") == 0)
3133 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3134 if (LocaleNCompare(expression,"int",3) == 0)
3135 {
3136 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
3137 exception);
3138 return((MagickRealType) floor(alpha+0.5));
3139 }
3140 if (LocaleCompare(expression,"i") == 0)
3141 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3142 break;
3143 }
3144 case 'J':
3145 case 'j':
3146 {
3147 if (LocaleCompare(expression,"j") == 0)
3148 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3149 break;
3150 }
3151 case 'L':
3152 case 'l':
3153 {
3154 if (LocaleNCompare(expression,"ln",2) == 0)
3155 {
3156 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
3157 exception);
3158 return((MagickRealType) log((double) alpha));
3159 }
3160 if (LocaleNCompare(expression,"logtwo",4) == 0)
3161 {
3162 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
3163 exception);
3164 return((MagickRealType) log10((double) alpha))/log10(2.0);
3165 }
3166 if (LocaleNCompare(expression,"log",3) == 0)
3167 {
3168 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
3169 exception);
3170 return((MagickRealType) log10((double) alpha));
3171 }
3172 if (LocaleCompare(expression,"lightness") == 0)
3173 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3174 break;
3175 }
3176 case 'M':
3177 case 'm':
3178 {
3179 if (LocaleCompare(expression,"MaxRGB") == 0)
3180 return((MagickRealType) QuantumRange);
3181 if (LocaleNCompare(expression,"maxima",6) == 0)
3182 break;
3183 if (LocaleNCompare(expression,"max",3) == 0)
3184 return(FxMax(fx_info,channel,x,y,expression+3,exception));
3185 if (LocaleNCompare(expression,"minima",6) == 0)
3186 break;
3187 if (LocaleNCompare(expression,"min",3) == 0)
3188 return(FxMin(fx_info,channel,x,y,expression+3,exception));
3189 if (LocaleNCompare(expression,"mod",3) == 0)
3190 {
3191 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
3192 exception);
3193 return((MagickRealType) fmod((double) alpha,(double) *beta));
3194 }
3195 if (LocaleCompare(expression,"m") == 0)
3196 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3197 break;
3198 }
3199 case 'N':
3200 case 'n':
3201 {
3202 if (LocaleCompare(expression,"n") == 0)
3203 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3204 break;
3205 }
3206 case 'O':
3207 case 'o':
3208 {
3209 if (LocaleCompare(expression,"Opaque") == 0)
3210 return(1.0);
3211 if (LocaleCompare(expression,"o") == 0)
3212 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3213 break;
3214 }
3215 case 'P':
3216 case 'p':
3217 {
3218 if (LocaleCompare(expression,"pi") == 0)
3219 return((MagickRealType) MagickPI);
3220 if (LocaleNCompare(expression,"pow",3) == 0)
3221 {
3222 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
3223 exception);
3224 return((MagickRealType) pow((double) alpha,(double) *beta));
3225 }
3226 if (LocaleCompare(expression,"p") == 0)
3227 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3228 break;
3229 }
3230 case 'Q':
3231 case 'q':
3232 {
3233 if (LocaleCompare(expression,"QuantumRange") == 0)
3234 return((MagickRealType) QuantumRange);
3235 if (LocaleCompare(expression,"QuantumScale") == 0)
3236 return((MagickRealType) QuantumScale);
3237 break;
3238 }
3239 case 'R':
3240 case 'r':
3241 {
3242 if (LocaleNCompare(expression,"rand",4) == 0)
3243 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
3244 if (LocaleNCompare(expression,"round",5) == 0)
3245 {
3246 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
3247 exception);
3248 if (alpha >= 0.0)
3249 return((MagickRealType) floor((double) alpha+0.5));
3250 return((MagickRealType) ceil((double) alpha-0.5));
3251 }
3252 if (LocaleCompare(expression,"r") == 0)
3253 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3254 break;
3255 }
3256 case 'S':
3257 case 's':
3258 {
3259 if (LocaleCompare(expression,"saturation") == 0)
3260 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3261 if (LocaleNCompare(expression,"sign",4) == 0)
3262 {
3263 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
3264 exception);
3265 return(alpha < 0.0 ? -1.0 : 1.0);
3266 }
3267 if (LocaleNCompare(expression,"sinh",4) == 0)
3268 {
3269 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
3270 exception);
3271 return((MagickRealType) sinh((double) alpha));
3272 }
3273 if (LocaleNCompare(expression,"sin",3) == 0)
3274 {
3275 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
3276 exception);
3277 return((MagickRealType) sin((double) alpha));
3278 }
3279 if (LocaleNCompare(expression,"sqrt",4) == 0)
3280 {
3281 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
3282 exception);
3283 return((MagickRealType) sqrt((double) alpha));
3284 }
3285 if (LocaleCompare(expression,"s") == 0)
3286 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3287 break;
3288 }
3289 case 'T':
3290 case 't':
3291 {
3292 if (LocaleNCompare(expression,"tanh",4) == 0)
3293 {
3294 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
3295 exception);
3296 return((MagickRealType) tanh((double) alpha));
3297 }
3298 if (LocaleNCompare(expression,"tan",3) == 0)
3299 {
3300 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
3301 exception);
3302 return((MagickRealType) tan((double) alpha));
3303 }
3304 if (LocaleCompare(expression,"Transparent") == 0)
3305 return(0.0);
3306 if (LocaleCompare(expression,"t") == 0)
3307 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3308 break;
3309 }
3310 case 'U':
3311 case 'u':
3312 {
3313 if (LocaleCompare(expression,"u") == 0)
3314 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3315 break;
3316 }
3317 case 'V':
3318 case 'v':
3319 {
3320 if (LocaleCompare(expression,"v") == 0)
3321 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3322 break;
3323 }
3324 case 'W':
3325 case 'w':
3326 {
3327 if (LocaleCompare(expression,"w") == 0)
3328 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3329 break;
3330 }
3331 case 'Y':
3332 case 'y':
3333 {
3334 if (LocaleCompare(expression,"y") == 0)
3335 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3336 break;
3337 }
3338 case 'Z':
3339 case 'z':
3340 {
3341 if (LocaleCompare(expression,"z") == 0)
3342 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3343 break;
3344 }
3345 default:
3346 break;
3347 }
3348 q=(char *) expression;
3349 alpha=strtod(expression,&q);
3350 if (q == expression)
3351 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
3352 return(alpha);
3353}
3354
3355MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
3356 MagickRealType *alpha,ExceptionInfo *exception)
3357{
3358 MagickBooleanType
3359 status;
3360
3361 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
3362 return(status);
3363}
3364
3365MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
3366 MagickRealType *alpha,ExceptionInfo *exception)
3367{
3368 FILE
3369 *file;
3370
3371 MagickBooleanType
3372 status;
3373
3374 file=fx_info->file;
3375 fx_info->file=(FILE *) NULL;
3376 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
3377 fx_info->file=file;
3378 return(status);
3379}
3380
3381MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
3382 const ChannelType channel,const long x,const long y,MagickRealType *alpha,
3383 ExceptionInfo *exception)
3384{
3385 MagickRealType
3386 beta;
3387
3388 beta=0.0;
3389 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
3390 exception);
3391 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
3392}
3393
3394/*
3395%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3396% %
3397% %
3398% %
3399% F x I m a g e %
3400% %
3401% %
3402% %
3403%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3404%
3405% FxImage() applies a mathematical expression to the specified image.
3406%
3407% The format of the FxImage method is:
3408%
3409% Image *FxImage(const Image *image,const char *expression,
3410% ExceptionInfo *exception)
3411% Image *FxImageChannel(const Image *image,const ChannelType channel,
3412% const char *expression,ExceptionInfo *exception)
3413%
3414% A description of each parameter follows:
3415%
3416% o image: the image.
3417%
3418% o channel: the channel.
3419%
3420% o expression: A mathematical expression.
3421%
3422% o exception: return any errors or warnings in this structure.
3423%
3424*/
3425
3426static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
3427{
3428 register long
3429 i;
3430
3431 assert(fx_info != (FxInfo **) NULL);
3432 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
3433 if (fx_info[i] != (FxInfo *) NULL)
3434 fx_info[i]=DestroyFxInfo(fx_info[i]);
3435 fx_info=(FxInfo **) RelinquishAlignedMemory(fx_info);
3436 return(fx_info);
3437}
3438
3439static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
3440 ExceptionInfo *exception)
3441{
3442 char
3443 *fx_expression;
3444
3445 FxInfo
3446 **fx_info;
3447
3448 MagickRealType
3449 alpha;
3450
3451 register long
3452 i;
3453
3454 unsigned long
3455 number_threads;
3456
3457 number_threads=GetOpenMPMaximumThreads();
3458 fx_info=(FxInfo **) AcquireAlignedMemory(number_threads,sizeof(*fx_info));
3459 if (fx_info == (FxInfo **) NULL)
3460 return((FxInfo **) NULL);
3461 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
3462 if (*expression != '@')
3463 fx_expression=ConstantString(expression);
3464 else
3465 fx_expression=FileToString(expression+1,~0,exception);
3466 for (i=0; i < (long) number_threads; i++)
3467 {
3468 fx_info[i]=AcquireFxInfo(image,fx_expression);
3469 if (fx_info[i] == (FxInfo *) NULL)
3470 return(DestroyFxThreadSet(fx_info));
3471 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
3472 }
3473 fx_expression=DestroyString(fx_expression);
3474 return(fx_info);
3475}
3476
3477MagickExport Image *FxImage(const Image *image,const char *expression,
3478 ExceptionInfo *exception)
3479{
3480 Image
3481 *fx_image;
3482
3483 fx_image=FxImageChannel(image,GrayChannel,expression,exception);
3484 return(fx_image);
3485}
3486
3487MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
3488 const char *expression,ExceptionInfo *exception)
3489{
3490#define FxImageTag "Fx/Image"
3491
3492 FxInfo
3493 **fx_info;
3494
3495 Image
3496 *fx_image;
3497
3498 long
3499 progress,
3500 y;
3501
3502 MagickBooleanType
3503 status;
3504
3505 MagickRealType
3506 alpha;
3507
3508 CacheView
3509 *fx_view;
3510
3511 assert(image != (Image *) NULL);
3512 assert(image->signature == MagickSignature);
3513 if (image->debug != MagickFalse)
3514 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3515 fx_image=CloneImage(image,0,0,MagickTrue,exception);
3516 if (fx_image == (Image *) NULL)
3517 return((Image *) NULL);
3518 if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
3519 {
3520 InheritException(exception,&fx_image->exception);
3521 fx_image=DestroyImage(fx_image);
3522 return((Image *) NULL);
3523 }
3524 fx_info=AcquireFxThreadSet(image,expression,exception);
3525 if (fx_info == (FxInfo **) NULL)
3526 {
3527 fx_image=DestroyImage(fx_image);
3528 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3529 }
3530 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3531 if (status == MagickFalse)
3532 {
3533 fx_image=DestroyImage(fx_image);
3534 fx_info=DestroyFxThreadSet(fx_info);
3535 return((Image *) NULL);
3536 }
3537 /*
3538 Fx image.
3539 */
3540 status=MagickTrue;
3541 progress=0;
3542 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003543#if defined(MAGICKCORE_OPENMP_SUPPORT)
3544 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003545#endif
3546 for (y=0; y < (long) fx_image->rows; y++)
3547 {
3548 MagickRealType
3549 alpha;
3550
3551 register IndexPacket
3552 *__restrict fx_indexes;
3553
3554 register long
3555 id,
3556 x;
3557
3558 register PixelPacket
3559 *__restrict q;
3560
3561 if (status == MagickFalse)
3562 continue;
3563 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
3564 if (q == (PixelPacket *) NULL)
3565 {
3566 status=MagickFalse;
3567 continue;
3568 }
3569 fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
3570 id=GetOpenMPThreadId();
3571 alpha=0.0;
3572 for (x=0; x < (long) fx_image->columns; x++)
3573 {
3574 if ((channel & RedChannel) != 0)
3575 {
3576 (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
3577 &alpha,exception);
3578 q->red=RoundToQuantum((MagickRealType) QuantumRange*alpha);
3579 }
3580 if ((channel & GreenChannel) != 0)
3581 {
3582 (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
3583 &alpha,exception);
3584 q->green=RoundToQuantum((MagickRealType) QuantumRange*alpha);
3585 }
3586 if ((channel & BlueChannel) != 0)
3587 {
3588 (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
3589 &alpha,exception);
3590 q->blue=RoundToQuantum((MagickRealType) QuantumRange*alpha);
3591 }
3592 if ((channel & OpacityChannel) != 0)
3593 {
3594 (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
3595 &alpha,exception);
3596 if (image->matte == MagickFalse)
3597 q->opacity=RoundToQuantum((MagickRealType) QuantumRange*alpha);
3598 else
3599 q->opacity=RoundToQuantum((MagickRealType) (QuantumRange-
3600 QuantumRange*alpha));
3601 }
3602 if (((channel & IndexChannel) != 0) &&
3603 (fx_image->colorspace == CMYKColorspace))
3604 {
3605 (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
3606 &alpha,exception);
3607 fx_indexes[x]=(IndexPacket) RoundToQuantum((MagickRealType)
3608 QuantumRange*alpha);
3609 }
3610 q++;
3611 }
3612 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3613 status=MagickFalse;
3614 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3615 {
3616 MagickBooleanType
3617 proceed;
3618
cristyb5d5f722009-11-04 03:03:49 +00003619#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003620 #pragma omp critical (MagickCore_FxImageChannel)
3621#endif
3622 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3623 if (proceed == MagickFalse)
3624 status=MagickFalse;
3625 }
3626 }
3627 fx_image->matte=fx_info[0]->matte;
3628 fx_view=DestroyCacheView(fx_view);
3629 fx_info=DestroyFxThreadSet(fx_info);
3630 if (status == MagickFalse)
3631 fx_image=DestroyImage(fx_image);
3632 return(fx_image);
3633}
3634
3635/*
3636%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3637% %
3638% %
3639% %
3640% I m p l o d e I m a g e %
3641% %
3642% %
3643% %
3644%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3645%
3646% ImplodeImage() creates a new image that is a copy of an existing
3647% one with the image pixels "implode" by the specified percentage. It
3648% allocates the memory necessary for the new Image structure and returns a
3649% pointer to the new image.
3650%
3651% The format of the ImplodeImage method is:
3652%
3653% Image *ImplodeImage(const Image *image,const double amount,
3654% ExceptionInfo *exception)
3655%
3656% A description of each parameter follows:
3657%
3658% o implode_image: Method ImplodeImage returns a pointer to the image
3659% after it is implode. A null image is returned if there is a memory
3660% shortage.
3661%
3662% o image: the image.
3663%
3664% o amount: Define the extent of the implosion.
3665%
3666% o exception: return any errors or warnings in this structure.
3667%
3668*/
3669MagickExport Image *ImplodeImage(const Image *image,const double amount,
3670 ExceptionInfo *exception)
3671{
3672#define ImplodeImageTag "Implode/Image"
3673
3674 Image
3675 *implode_image;
3676
3677 long
3678 progress,
3679 y;
3680
3681 MagickBooleanType
3682 status;
3683
3684 MagickPixelPacket
3685 zero;
3686
3687 MagickRealType
3688 radius;
3689
3690 PointInfo
3691 center,
3692 scale;
3693
3694 ResampleFilter
3695 **resample_filter;
3696
3697 CacheView
3698 *image_view,
3699 *implode_view;
3700
3701 /*
3702 Initialize implode image attributes.
3703 */
3704 assert(image != (Image *) NULL);
3705 assert(image->signature == MagickSignature);
3706 if (image->debug != MagickFalse)
3707 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3708 assert(exception != (ExceptionInfo *) NULL);
3709 assert(exception->signature == MagickSignature);
3710 implode_image=CloneImage(image,0,0,MagickTrue,exception);
3711 if (implode_image == (Image *) NULL)
3712 return((Image *) NULL);
3713 if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
3714 {
3715 InheritException(exception,&implode_image->exception);
3716 implode_image=DestroyImage(implode_image);
3717 return((Image *) NULL);
3718 }
3719 if (implode_image->background_color.opacity != OpaqueOpacity)
3720 implode_image->matte=MagickTrue;
3721 /*
3722 Compute scaling factor.
3723 */
3724 scale.x=1.0;
3725 scale.y=1.0;
3726 center.x=0.5*image->columns;
3727 center.y=0.5*image->rows;
3728 radius=center.x;
3729 if (image->columns > image->rows)
3730 scale.y=(double) image->columns/(double) image->rows;
3731 else
3732 if (image->columns < image->rows)
3733 {
3734 scale.x=(double) image->rows/(double) image->columns;
3735 radius=center.y;
3736 }
3737 /*
3738 Implode image.
3739 */
3740 status=MagickTrue;
3741 progress=0;
3742 GetMagickPixelPacket(implode_image,&zero);
3743 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
3744 image_view=AcquireCacheView(image);
3745 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003746#if defined(MAGICKCORE_OPENMP_SUPPORT)
3747 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003748#endif
3749 for (y=0; y < (long) image->rows; y++)
3750 {
3751 MagickPixelPacket
3752 pixel;
3753
3754 MagickRealType
3755 distance;
3756
3757 PointInfo
3758 delta;
3759
3760 register IndexPacket
3761 *__restrict implode_indexes;
3762
3763 register long
3764 id,
3765 x;
3766
3767 register PixelPacket
3768 *__restrict q;
3769
3770 if (status == MagickFalse)
3771 continue;
3772 q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3773 exception);
3774 if (q == (PixelPacket *) NULL)
3775 {
3776 status=MagickFalse;
3777 continue;
3778 }
3779 implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
3780 delta.y=scale.y*(double) (y-center.y);
3781 pixel=zero;
3782 id=GetOpenMPThreadId();
3783 for (x=0; x < (long) image->columns; x++)
3784 {
3785 /*
3786 Determine if the pixel is within an ellipse.
3787 */
3788 delta.x=scale.x*(double) (x-center.x);
3789 distance=delta.x*delta.x+delta.y*delta.y;
3790 if (distance < (radius*radius))
3791 {
3792 double
3793 factor;
3794
3795 /*
3796 Implode the pixel.
3797 */
3798 factor=1.0;
3799 if (distance > 0.0)
3800 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3801 radius/2)),-amount);
3802 (void) ResamplePixelColor(resample_filter[id],(double)
3803 (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3804 scale.y+center.y),&pixel);
3805 SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
3806 }
3807 q++;
3808 }
3809 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3810 status=MagickFalse;
3811 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3812 {
3813 MagickBooleanType
3814 proceed;
3815
cristyb5d5f722009-11-04 03:03:49 +00003816#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003817 #pragma omp critical (MagickCore_ImplodeImage)
3818#endif
3819 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3820 if (proceed == MagickFalse)
3821 status=MagickFalse;
3822 }
3823 }
3824 implode_view=DestroyCacheView(implode_view);
3825 image_view=DestroyCacheView(image_view);
3826 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
3827 if (status == MagickFalse)
3828 implode_image=DestroyImage(implode_image);
3829 return(implode_image);
3830}
3831
3832/*
3833%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3834% %
3835% %
3836% %
3837% M o r p h I m a g e s %
3838% %
3839% %
3840% %
3841%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3842%
3843% The MorphImages() method requires a minimum of two images. The first
3844% image is transformed into the second by a number of intervening images
3845% as specified by frames.
3846%
3847% The format of the MorphImage method is:
3848%
3849% Image *MorphImages(const Image *image,const unsigned long number_frames,
3850% ExceptionInfo *exception)
3851%
3852% A description of each parameter follows:
3853%
3854% o image: the image.
3855%
3856% o number_frames: Define the number of in-between image to generate.
3857% The more in-between frames, the smoother the morph.
3858%
3859% o exception: return any errors or warnings in this structure.
3860%
3861*/
3862MagickExport Image *MorphImages(const Image *image,
3863 const unsigned long number_frames,ExceptionInfo *exception)
3864{
3865#define MorphImageTag "Morph/Image"
3866
3867 Image
3868 *morph_image,
3869 *morph_images;
3870
3871 long
3872 y;
3873
3874 MagickOffsetType
3875 scene;
3876
3877 MagickRealType
3878 alpha,
3879 beta;
3880
3881 register const Image
3882 *next;
3883
3884 register long
3885 i;
3886
3887 MagickBooleanType
3888 status;
3889
3890 /*
3891 Clone first frame in sequence.
3892 */
3893 assert(image != (Image *) NULL);
3894 assert(image->signature == MagickSignature);
3895 if (image->debug != MagickFalse)
3896 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3897 assert(exception != (ExceptionInfo *) NULL);
3898 assert(exception->signature == MagickSignature);
3899 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3900 if (morph_images == (Image *) NULL)
3901 return((Image *) NULL);
3902 if (GetNextImageInList(image) == (Image *) NULL)
3903 {
3904 /*
3905 Morph single image.
3906 */
3907 for (i=1; i < (long) number_frames; i++)
3908 {
3909 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3910 if (morph_image == (Image *) NULL)
3911 {
3912 morph_images=DestroyImageList(morph_images);
3913 return((Image *) NULL);
3914 }
3915 AppendImageToList(&morph_images,morph_image);
3916 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
3917 (QuantumTick(i,number_frames) != MagickFalse))
3918 {
3919 status=image->progress_monitor(MorphImageTag,i,number_frames,
3920 image->client_data);
3921 if (status == MagickFalse)
3922 break;
3923 }
3924 }
3925 return(GetFirstImageInList(morph_images));
3926 }
3927 /*
3928 Morph image sequence.
3929 */
3930 status=MagickTrue;
3931 scene=0;
3932 next=image;
3933 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3934 {
3935 for (i=0; i < (long) number_frames; i++)
3936 {
3937 CacheView
3938 *image_view,
3939 *morph_view;
3940
3941 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3942 alpha=1.0-beta;
3943 morph_image=ZoomImage(next,(unsigned long) (alpha*next->columns+beta*
3944 GetNextImageInList(next)->columns+0.5),(unsigned long) (alpha*
3945 next->rows+beta*GetNextImageInList(next)->rows+0.5),exception);
3946 if (morph_image == (Image *) NULL)
3947 {
3948 morph_images=DestroyImageList(morph_images);
3949 return((Image *) NULL);
3950 }
3951 if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
3952 {
3953 InheritException(exception,&morph_image->exception);
3954 morph_image=DestroyImage(morph_image);
3955 return((Image *) NULL);
3956 }
3957 AppendImageToList(&morph_images,morph_image);
3958 morph_images=GetLastImageInList(morph_images);
3959 morph_image=ZoomImage(GetNextImageInList(next),morph_images->columns,
3960 morph_images->rows,exception);
3961 if (morph_image == (Image *) NULL)
3962 {
3963 morph_images=DestroyImageList(morph_images);
3964 return((Image *) NULL);
3965 }
3966 image_view=AcquireCacheView(morph_image);
3967 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003968#if defined(MAGICKCORE_OPENMP_SUPPORT)
3969 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003970#endif
3971 for (y=0; y < (long) morph_images->rows; y++)
3972 {
3973 MagickBooleanType
3974 sync;
3975
3976 register const PixelPacket
3977 *__restrict p;
3978
3979 register long
3980 x;
3981
3982 register PixelPacket
3983 *__restrict q;
3984
3985 if (status == MagickFalse)
3986 continue;
3987 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3988 exception);
3989 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3990 exception);
3991 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3992 {
3993 status=MagickFalse;
3994 continue;
3995 }
3996 for (x=0; x < (long) morph_images->columns; x++)
3997 {
3998 q->red=RoundToQuantum(alpha*q->red+beta*p->red);
3999 q->green=RoundToQuantum(alpha*q->green+beta*p->green);
4000 q->blue=RoundToQuantum(alpha*q->blue+beta*p->blue);
4001 q->opacity=RoundToQuantum(alpha*q->opacity+beta*p->opacity);
4002 p++;
4003 q++;
4004 }
4005 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
4006 if (sync == MagickFalse)
4007 status=MagickFalse;
4008 }
4009 morph_view=DestroyCacheView(morph_view);
4010 image_view=DestroyCacheView(image_view);
4011 morph_image=DestroyImage(morph_image);
4012 }
4013 if (i < (long) number_frames)
4014 break;
4015 /*
4016 Clone last frame in sequence.
4017 */
4018 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
4019 if (morph_image == (Image *) NULL)
4020 {
4021 morph_images=DestroyImageList(morph_images);
4022 return((Image *) NULL);
4023 }
4024 AppendImageToList(&morph_images,morph_image);
4025 morph_images=GetLastImageInList(morph_images);
4026 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4027 {
4028 MagickBooleanType
4029 proceed;
4030
cristyb5d5f722009-11-04 03:03:49 +00004031#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004032 #pragma omp critical (MagickCore_MorphImages)
4033#endif
4034 proceed=SetImageProgress(image,MorphImageTag,scene,
4035 GetImageListLength(image));
4036 if (proceed == MagickFalse)
4037 status=MagickFalse;
4038 }
4039 scene++;
4040 }
4041 if (GetNextImageInList(next) != (Image *) NULL)
4042 {
4043 morph_images=DestroyImageList(morph_images);
4044 return((Image *) NULL);
4045 }
4046 return(GetFirstImageInList(morph_images));
4047}
4048
4049/*
4050%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4051% %
4052% %
4053% %
4054% P l a s m a I m a g e %
4055% %
4056% %
4057% %
4058%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4059%
4060% PlasmaImage() initializes an image with plasma fractal values. The image
4061% must be initialized with a base color and the random number generator
4062% seeded before this method is called.
4063%
4064% The format of the PlasmaImage method is:
4065%
4066% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
4067% unsigned long attenuate,unsigned long depth)
4068%
4069% A description of each parameter follows:
4070%
4071% o image: the image.
4072%
4073% o segment: Define the region to apply plasma fractals values.
4074%
4075% o attenuate: Define the plasmattenuation factor.
4076%
4077% o depth: Limit the plasma recursion depth.
4078%
4079*/
4080
4081static inline Quantum PlasmaPixel(RandomInfo *random_info,
4082 const MagickRealType pixel,const MagickRealType noise)
4083{
4084 Quantum
4085 plasma;
4086
4087 plasma=RoundToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
4088 noise/2.0);
4089 return(plasma);
4090}
4091
4092MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
4093 RandomInfo *random_info,const SegmentInfo *segment,unsigned long attenuate,
4094 unsigned long depth)
4095{
4096 ExceptionInfo
4097 *exception;
4098
4099 long
4100 x,
4101 x_mid,
4102 y,
4103 y_mid;
4104
4105 MagickRealType
4106 plasma;
4107
4108 PixelPacket
4109 u,
4110 v;
4111
4112 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
4113 return(MagickTrue);
4114 if (depth != 0)
4115 {
4116 SegmentInfo
4117 local_info;
4118
4119 /*
4120 Divide the area into quadrants and recurse.
4121 */
4122 depth--;
4123 attenuate++;
4124 x_mid=(long) (segment->x1+segment->x2+0.5)/2;
4125 y_mid=(long) (segment->y1+segment->y2+0.5)/2;
4126 local_info=(*segment);
4127 local_info.x2=(double) x_mid;
4128 local_info.y2=(double) y_mid;
4129 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
4130 local_info=(*segment);
4131 local_info.y1=(double) y_mid;
4132 local_info.x2=(double) x_mid;
4133 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
4134 local_info=(*segment);
4135 local_info.x1=(double) x_mid;
4136 local_info.y2=(double) y_mid;
4137 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
4138 local_info=(*segment);
4139 local_info.x1=(double) x_mid;
4140 local_info.y1=(double) y_mid;
4141 return(PlasmaImageProxy(image,random_info,&local_info,attenuate,depth));
4142 }
4143 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4144 return(MagickFalse);
4145 x_mid=(long) (segment->x1+segment->x2+0.5)/2;
4146 y_mid=(long) (segment->y1+segment->y2+0.5)/2;
4147 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
4148 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
4149 return(MagickFalse);
4150 /*
4151 Average pixels and apply plasma.
4152 */
4153 exception=(&image->exception);
4154 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
4155 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
4156 {
4157 register PixelPacket
4158 *__restrict q;
4159
4160 /*
4161 Left pixel.
4162 */
4163 x=(long) (segment->x1+0.5);
4164 (void) GetOneVirtualPixel(image,x,(long) (segment->y1+0.5),&u,exception);
4165 (void) GetOneVirtualPixel(image,x,(long) (segment->y2+0.5),&v,exception);
4166 q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
4167 if (q == (PixelPacket *) NULL)
4168 return(MagickTrue);
4169 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
4170 plasma);
4171 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
4172 plasma);
4173 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
4174 plasma);
4175 (void) SyncAuthenticPixels(image,exception);
4176 if (segment->x1 != segment->x2)
4177 {
4178 /*
4179 Right pixel.
4180 */
4181 x=(long) (segment->x2+0.5);
4182 (void) GetOneVirtualPixel(image,x,(long) (segment->y1+0.5),&u,
4183 exception);
4184 (void) GetOneVirtualPixel(image,x,(long) (segment->y2+0.5),&v,
4185 exception);
4186 q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
4187 if (q == (PixelPacket *) NULL)
4188 return(MagickTrue);
4189 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
4190 plasma);
4191 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
4192 2.0,plasma);
4193 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
4194 plasma);
4195 (void) SyncAuthenticPixels(image,exception);
4196 }
4197 }
4198 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
4199 {
4200 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
4201 {
4202 register PixelPacket
4203 *__restrict q;
4204
4205 /*
4206 Bottom pixel.
4207 */
4208 y=(long) (segment->y2+0.5);
4209 (void) GetOneVirtualPixel(image,(long) (segment->x1+0.5),y,&u,
4210 exception);
4211 (void) GetOneVirtualPixel(image,(long) (segment->x2+0.5),y,&v,
4212 exception);
4213 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
4214 if (q == (PixelPacket *) NULL)
4215 return(MagickTrue);
4216 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
4217 plasma);
4218 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
4219 2.0,plasma);
4220 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
4221 plasma);
4222 (void) SyncAuthenticPixels(image,exception);
4223 }
4224 if (segment->y1 != segment->y2)
4225 {
4226 register PixelPacket
4227 *__restrict q;
4228
4229 /*
4230 Top pixel.
4231 */
4232 y=(long) (segment->y1+0.5);
4233 (void) GetOneVirtualPixel(image,(long) (segment->x1+0.5),y,&u,
4234 exception);
4235 (void) GetOneVirtualPixel(image,(long) (segment->x2+0.5),y,&v,
4236 exception);
4237 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
4238 if (q == (PixelPacket *) NULL)
4239 return(MagickTrue);
4240 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
4241 plasma);
4242 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
4243 2.0,plasma);
4244 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
4245 plasma);
4246 (void) SyncAuthenticPixels(image,exception);
4247 }
4248 }
4249 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
4250 {
4251 register PixelPacket
4252 *__restrict q;
4253
4254 /*
4255 Middle pixel.
4256 */
4257 x=(long) (segment->x1+0.5);
4258 y=(long) (segment->y1+0.5);
4259 (void) GetOneVirtualPixel(image,x,y,&u,exception);
4260 x=(long) (segment->x2+0.5);
4261 y=(long) (segment->y2+0.5);
4262 (void) GetOneVirtualPixel(image,x,y,&v,exception);
4263 q=QueueAuthenticPixels(image,x_mid,y_mid,1,1,exception);
4264 if (q == (PixelPacket *) NULL)
4265 return(MagickTrue);
4266 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
4267 plasma);
4268 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
4269 plasma);
4270 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
4271 plasma);
4272 (void) SyncAuthenticPixels(image,exception);
4273 }
4274 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
4275 return(MagickTrue);
4276 return(MagickFalse);
4277}
4278
4279MagickExport MagickBooleanType PlasmaImage(Image *image,
4280 const SegmentInfo *segment,unsigned long attenuate,unsigned long depth)
4281{
4282 MagickBooleanType
4283 status;
4284
4285 RandomInfo
4286 *random_info;
4287
4288 if (image->debug != MagickFalse)
4289 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4290 assert(image != (Image *) NULL);
4291 assert(image->signature == MagickSignature);
4292 if (image->debug != MagickFalse)
4293 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
4294 random_info=AcquireRandomInfo();
4295 status=PlasmaImageProxy(image,random_info,segment,attenuate,depth);
4296 random_info=DestroyRandomInfo(random_info);
4297 return(status);
4298}
4299
4300/*
4301%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4302% %
4303% %
4304% %
4305% P o l a r o i d I m a g e %
4306% %
4307% %
4308% %
4309%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4310%
4311% PolaroidImage() simulates a Polaroid picture.
4312%
4313% The format of the AnnotateImage method is:
4314%
4315% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
4316% const double angle,ExceptionInfo exception)
4317%
4318% A description of each parameter follows:
4319%
4320% o image: the image.
4321%
4322% o draw_info: the draw info.
4323%
4324% o angle: Apply the effect along this angle.
4325%
4326% o exception: return any errors or warnings in this structure.
4327%
4328*/
4329MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
4330 const double angle,ExceptionInfo *exception)
4331{
4332 const char
4333 *value;
4334
4335 long
4336 quantum;
4337
4338 Image
4339 *bend_image,
4340 *caption_image,
4341 *flop_image,
4342 *picture_image,
4343 *polaroid_image,
4344 *rotate_image,
4345 *trim_image;
4346
4347 unsigned long
4348 height;
4349
4350 /*
4351 Simulate a Polaroid picture.
4352 */
4353 assert(image != (Image *) NULL);
4354 assert(image->signature == MagickSignature);
4355 if (image->debug != MagickFalse)
4356 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4357 assert(exception != (ExceptionInfo *) NULL);
4358 assert(exception->signature == MagickSignature);
4359 quantum=(long) MagickMax(MagickMax((double) image->columns,(double)
4360 image->rows)/25.0,10.0);
4361 height=image->rows+2*quantum;
4362 caption_image=(Image *) NULL;
4363 value=GetImageProperty(image,"Caption");
4364 if (value != (const char *) NULL)
4365 {
4366 char
4367 *caption,
4368 geometry[MaxTextExtent];
4369
4370 DrawInfo
4371 *annotate_info;
4372
4373 long
4374 count;
4375
4376 MagickBooleanType
4377 status;
4378
4379 TypeMetric
4380 metrics;
4381
4382 /*
4383 Generate caption image.
4384 */
4385 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
4386 if (caption_image == (Image *) NULL)
4387 return((Image *) NULL);
4388 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
4389 caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
4390 value);
4391 (void) CloneString(&annotate_info->text,caption);
4392 count=FormatMagickCaption(caption_image,annotate_info,&metrics,&caption);
4393 status=SetImageExtent(caption_image,image->columns,(unsigned long)
4394 ((count+1)*(metrics.ascent-metrics.descent)+0.5));
4395 if (status == MagickFalse)
4396 caption_image=DestroyImage(caption_image);
4397 else
4398 {
4399 caption_image->background_color=image->border_color;
4400 (void) SetImageBackgroundColor(caption_image);
4401 (void) CloneString(&annotate_info->text,caption);
4402 (void) FormatMagickString(geometry,MaxTextExtent,"+0+%g",
4403 metrics.ascent);
4404 if (annotate_info->gravity == UndefinedGravity)
4405 (void) CloneString(&annotate_info->geometry,AcquireString(
4406 geometry));
4407 (void) AnnotateImage(caption_image,annotate_info);
4408 height+=caption_image->rows;
4409 }
4410 annotate_info=DestroyDrawInfo(annotate_info);
4411 caption=DestroyString(caption);
4412 }
4413 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4414 exception);
4415 if (picture_image == (Image *) NULL)
4416 {
4417 if (caption_image != (Image *) NULL)
4418 caption_image=DestroyImage(caption_image);
4419 return((Image *) NULL);
4420 }
4421 picture_image->background_color=image->border_color;
4422 (void) SetImageBackgroundColor(picture_image);
4423 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
4424 if (caption_image != (Image *) NULL)
4425 {
4426 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
4427 quantum,(long) (image->rows+3*quantum/2));
4428 caption_image=DestroyImage(caption_image);
4429 }
4430 (void) QueryColorDatabase("none",&picture_image->background_color,exception);
4431 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
4432 rotate_image=RotateImage(picture_image,90.0,exception);
4433 picture_image=DestroyImage(picture_image);
4434 if (rotate_image == (Image *) NULL)
4435 return((Image *) NULL);
4436 picture_image=rotate_image;
4437 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
4438 picture_image->columns,exception);
4439 picture_image=DestroyImage(picture_image);
4440 if (bend_image == (Image *) NULL)
4441 return((Image *) NULL);
4442 InheritException(&bend_image->exception,exception);
4443 picture_image=bend_image;
4444 rotate_image=RotateImage(picture_image,-90.0,exception);
4445 picture_image=DestroyImage(picture_image);
4446 if (rotate_image == (Image *) NULL)
4447 return((Image *) NULL);
4448 picture_image=rotate_image;
4449 picture_image->background_color=image->background_color;
4450 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
4451 exception);
4452 if (polaroid_image == (Image *) NULL)
4453 {
4454 picture_image=DestroyImage(picture_image);
4455 return(picture_image);
4456 }
4457 flop_image=FlopImage(polaroid_image,exception);
4458 polaroid_image=DestroyImage(polaroid_image);
4459 if (flop_image == (Image *) NULL)
4460 {
4461 picture_image=DestroyImage(picture_image);
4462 return(picture_image);
4463 }
4464 polaroid_image=flop_image;
4465 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
4466 (long) (-0.01*picture_image->columns/2.0),0L);
4467 picture_image=DestroyImage(picture_image);
4468 (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
4469 rotate_image=RotateImage(polaroid_image,angle,exception);
4470 polaroid_image=DestroyImage(polaroid_image);
4471 if (rotate_image == (Image *) NULL)
4472 return((Image *) NULL);
4473 polaroid_image=rotate_image;
4474 trim_image=TrimImage(polaroid_image,exception);
4475 polaroid_image=DestroyImage(polaroid_image);
4476 if (trim_image == (Image *) NULL)
4477 return((Image *) NULL);
4478 polaroid_image=trim_image;
4479 return(polaroid_image);
4480}
4481
4482/*
4483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4484% %
4485% %
4486% %
4487% R e c o l o r I m a g e %
4488% %
4489% %
4490% %
4491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4492%
4493% RecolorImage() translate, scale, shear, or rotate image colors. Although
4494% you can use variable sized matrices, typically you use a 5 x 5 for an RGBA
4495% image and a 6x6 for CMYKA. Populate the last row with normalized values to
4496% translate.
4497%
4498% The format of the RecolorImage method is:
4499%
4500% Image *RecolorImage(const Image *image,const unsigned long order,
4501% const double *color_matrix,ExceptionInfo *exception)
4502%
4503% A description of each parameter follows:
4504%
4505% o image: the image.
4506%
4507% o order: the number of columns and rows in the recolor matrix.
4508%
4509% o color_matrix: An array of double representing the recolor matrix.
4510%
4511% o exception: return any errors or warnings in this structure.
4512%
4513*/
4514MagickExport Image *RecolorImage(const Image *image,const unsigned long order,
4515 const double *color_matrix,ExceptionInfo *exception)
4516{
4517#define RecolorImageTag "Recolor/Image"
4518
4519 Image
4520 *recolor_image;
4521
4522 long
4523 progress,
4524 y;
4525
4526 MagickBooleanType
4527 status;
4528
4529 MagickPixelPacket
4530 zero;
4531
4532 register const double
4533 *k;
4534
4535 CacheView
4536 *image_view,
4537 *recolor_view;
4538
4539 /*
4540 Initialize image attributes.
4541 */
4542 assert(image != (Image *) NULL);
4543 assert(image->signature == MagickSignature);
4544 if (image->debug != MagickFalse)
4545 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4546 assert(exception != (ExceptionInfo *) NULL);
4547 assert(exception->signature == MagickSignature);
4548 recolor_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4549 exception);
4550 if (recolor_image == (Image *) NULL)
4551 return((Image *) NULL);
4552 if (SetImageStorageClass(recolor_image,DirectClass) == MagickFalse)
4553 {
4554 InheritException(exception,&recolor_image->exception);
4555 recolor_image=DestroyImage(recolor_image);
4556 return((Image *) NULL);
4557 }
4558 if (image->debug != MagickFalse)
4559 {
4560 char
4561 format[MaxTextExtent],
4562 *message;
4563
4564 long
4565 u,
4566 v;
4567
4568 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4569 " Recolor image with %ldx%ld color matrix:",order,order);
4570 message=AcquireString("");
4571 k=color_matrix;
4572 for (v=0; v < (long) order; v++)
4573 {
4574 *message='\0';
4575 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
4576 (void) ConcatenateString(&message,format);
4577 for (u=0; u < (long) order; u++)
4578 {
4579 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4580 (void) ConcatenateString(&message,format);
4581 }
4582 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4583 }
4584 message=DestroyString(message);
4585 }
4586 /*
4587 Recolor image.
4588 */
4589 status=MagickTrue;
4590 progress=0;
4591 GetMagickPixelPacket(image,&zero);
4592 k=color_matrix;
4593 image_view=AcquireCacheView(image);
4594 recolor_view=AcquireCacheView(recolor_image);
cristyb5d5f722009-11-04 03:03:49 +00004595#if defined(MAGICKCORE_OPENMP_SUPPORT)
4596 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004597#endif
4598 for (y=0; y < (long) image->rows; y++)
4599 {
4600 MagickPixelPacket
4601 pixel,
4602 recolor_pixel;
4603
4604 register const IndexPacket
4605 *__restrict indexes;
4606
4607 register const PixelPacket
4608 *__restrict p;
4609
4610 register long
4611 x;
4612
4613 register IndexPacket
4614 *__restrict recolor_indexes;
4615
4616 register PixelPacket
4617 *__restrict q;
4618
4619 if (status == MagickFalse)
4620 continue;
4621 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4622 q=QueueCacheViewAuthenticPixels(recolor_view,0,y,recolor_image->columns,1,
4623 exception);
4624 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4625 {
4626 status=MagickFalse;
4627 continue;
4628 }
4629 indexes=GetCacheViewAuthenticIndexQueue(image_view);
4630 recolor_indexes=GetCacheViewAuthenticIndexQueue(recolor_view);
4631 pixel=zero;
4632 recolor_pixel=zero;
4633 for (x=0; x < (long) image->columns; x++)
4634 {
4635 SetMagickPixelPacket(image,p,indexes,&pixel);
4636 SetMagickPixelPacket(recolor_image,p,indexes,&recolor_pixel);
4637 switch (order)
4638 {
4639 case 0:
4640 break;
4641 case 1:
4642 {
4643 recolor_pixel.red=k[0]*pixel.red;
4644 break;
4645 }
4646 case 2:
4647 {
4648 recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green;
4649 recolor_pixel.green=k[2]*pixel.red+k[3]*pixel.green;
4650 break;
4651 }
4652 case 3:
4653 {
4654 recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue;
4655 recolor_pixel.green=k[3]*pixel.red+k[4]*pixel.green+k[5]*pixel.blue;
4656 recolor_pixel.blue=k[6]*pixel.red+k[7]*pixel.green+k[8]*pixel.blue;
4657 break;
4658 }
4659 case 4:
4660 {
4661 recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue+
4662 k[12]*QuantumRange;
4663 recolor_pixel.green=k[4]*pixel.red+k[5]*pixel.green+k[6]*pixel.blue+
4664 k[13]*QuantumRange;
4665 recolor_pixel.blue=k[8]*pixel.red+k[9]*pixel.green+k[10]*pixel.blue+
4666 k[14]*QuantumRange;
4667 break;
4668 }
4669 case 5:
4670 {
4671 recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue+
4672 k[3]*(QuantumRange-pixel.opacity)+k[20]*QuantumRange;
4673 recolor_pixel.green=k[5]*pixel.red+k[6]*pixel.green+k[7]*pixel.blue+
4674 k[8]*(QuantumRange-pixel.opacity)+k[21]*QuantumRange;
4675 recolor_pixel.blue=k[10]*pixel.red+k[11]*pixel.green+k[12]*pixel.blue+
4676 k[13]*(QuantumRange-pixel.opacity)+k[22]*QuantumRange;
4677 recolor_pixel.opacity=(MagickRealType) QuantumRange-(k[15]*pixel.red+
4678 k[16]*pixel.green+k[17]*pixel.blue+k[18]*(QuantumRange-
4679 pixel.opacity)+k[23]*QuantumRange);
4680 break;
4681 }
4682 default:
4683 {
4684 recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue+
4685 k[3]*pixel.index+k[4]*((Quantum) QuantumRange-pixel.opacity)+
4686 k[30]*QuantumRange;
4687 recolor_pixel.green=k[6]*pixel.red+k[7]*pixel.green+k[8]*pixel.blue+
4688 k[9]*pixel.index+k[10]*((Quantum) QuantumRange-pixel.opacity)+
4689 k[31]*QuantumRange;
4690 recolor_pixel.blue=k[12]*pixel.red+k[13]*pixel.green+k[14]*pixel.blue+
4691 k[15]*pixel.index+k[16]*((Quantum) QuantumRange-pixel.opacity)+
4692 k[32]*QuantumRange;
4693 if (image->colorspace == CMYKColorspace)
4694 recolor_pixel.index=k[18]*pixel.red+k[19]*pixel.green+k[20]*
4695 pixel.blue+k[21]*pixel.index+k[22]*((Quantum) QuantumRange-
4696 pixel.opacity)+k[33]*QuantumRange;
4697 recolor_pixel.opacity=(MagickRealType) QuantumRange-(k[24]*pixel.red+
4698 k[25]*pixel.green+k[26]*pixel.blue+k[27]*pixel.index+k[28]*
4699 (QuantumRange-pixel.opacity)+k[34]*QuantumRange);
4700 break;
4701 }
4702 }
4703 q->red=RoundToQuantum(recolor_pixel.red);
4704 q->green=RoundToQuantum(recolor_pixel.green);
4705 q->blue=RoundToQuantum(recolor_pixel.blue);
4706 q->opacity=RoundToQuantum(recolor_pixel.opacity);
4707 if (image->colorspace == CMYKColorspace)
4708 recolor_indexes[x]=RoundToQuantum(recolor_pixel.index);
4709 p++;
4710 q++;
4711 }
4712 if (SyncCacheViewAuthenticPixels(recolor_view,exception) == MagickFalse)
4713 status=MagickFalse;
4714 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4715 {
4716 MagickBooleanType
4717 proceed;
4718
cristyb5d5f722009-11-04 03:03:49 +00004719#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004720 #pragma omp critical (MagickCore_RecolorImage)
4721#endif
4722 proceed=SetImageProgress(image,RecolorImageTag,progress++,image->rows);
4723 if (proceed == MagickFalse)
4724 status=MagickFalse;
4725 }
4726 }
4727 recolor_view=DestroyCacheView(recolor_view);
4728 image_view=DestroyCacheView(image_view);
4729 if (status == MagickFalse)
4730 recolor_image=DestroyImage(recolor_image);
4731 return(recolor_image);
4732}
4733
4734/*
4735%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4736% %
4737% %
4738% %
4739% S e p i a T o n e I m a g e %
4740% %
4741% %
4742% %
4743%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4744%
4745% MagickSepiaToneImage() applies a special effect to the image, similar to the
4746% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4747% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4748% threshold of 80% is a good starting point for a reasonable tone.
4749%
4750% The format of the SepiaToneImage method is:
4751%
4752% Image *SepiaToneImage(const Image *image,const double threshold,
4753% ExceptionInfo *exception)
4754%
4755% A description of each parameter follows:
4756%
4757% o image: the image.
4758%
4759% o threshold: the tone threshold.
4760%
4761% o exception: return any errors or warnings in this structure.
4762%
4763*/
4764MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4765 ExceptionInfo *exception)
4766{
4767#define SepiaToneImageTag "SepiaTone/Image"
4768
4769 Image
4770 *sepia_image;
4771
4772 long
4773 progress,
4774 y;
4775
4776 MagickBooleanType
4777 status;
4778
4779 CacheView
4780 *image_view,
4781 *sepia_view;
4782
4783 /*
4784 Initialize sepia-toned image attributes.
4785 */
4786 assert(image != (const Image *) NULL);
4787 assert(image->signature == MagickSignature);
4788 if (image->debug != MagickFalse)
4789 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4790 assert(exception != (ExceptionInfo *) NULL);
4791 assert(exception->signature == MagickSignature);
4792 sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4793 if (sepia_image == (Image *) NULL)
4794 return((Image *) NULL);
4795 if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
4796 {
4797 InheritException(exception,&sepia_image->exception);
4798 sepia_image=DestroyImage(sepia_image);
4799 return((Image *) NULL);
4800 }
4801 /*
4802 Tone each row of the image.
4803 */
4804 status=MagickTrue;
4805 progress=0;
4806 image_view=AcquireCacheView(image);
4807 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004808#if defined(MAGICKCORE_OPENMP_SUPPORT)
4809 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004810#endif
4811 for (y=0; y < (long) image->rows; y++)
4812 {
4813 register const PixelPacket
4814 *__restrict p;
4815
4816 register long
4817 x;
4818
4819 register PixelPacket
4820 *__restrict q;
4821
4822 if (status == MagickFalse)
4823 continue;
4824 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4825 q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4826 exception);
4827 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4828 {
4829 status=MagickFalse;
4830 continue;
4831 }
4832 for (x=0; x < (long) image->columns; x++)
4833 {
4834 MagickRealType
4835 intensity,
4836 tone;
4837
4838 intensity=(MagickRealType) PixelIntensityToQuantum(p);
4839 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4840 (MagickRealType) QuantumRange-threshold;
4841 q->red=RoundToQuantum(tone);
4842 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4843 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
4844 q->green=RoundToQuantum(tone);
4845 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
4846 q->blue=RoundToQuantum(tone);
4847 tone=threshold/7.0;
4848 if ((MagickRealType) q->green < tone)
4849 q->green=RoundToQuantum(tone);
4850 if ((MagickRealType) q->blue < tone)
4851 q->blue=RoundToQuantum(tone);
4852 p++;
4853 q++;
4854 }
4855 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4856 status=MagickFalse;
4857 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4858 {
4859 MagickBooleanType
4860 proceed;
4861
cristyb5d5f722009-11-04 03:03:49 +00004862#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004863 #pragma omp critical (MagickCore_SepiaToneImage)
4864#endif
4865 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4866 image->rows);
4867 if (proceed == MagickFalse)
4868 status=MagickFalse;
4869 }
4870 }
4871 sepia_view=DestroyCacheView(sepia_view);
4872 image_view=DestroyCacheView(image_view);
4873 (void) NormalizeImage(sepia_image);
4874 (void) ContrastImage(sepia_image,MagickTrue);
4875 if (status == MagickFalse)
4876 sepia_image=DestroyImage(sepia_image);
4877 return(sepia_image);
4878}
4879
4880/*
4881%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4882% %
4883% %
4884% %
4885% S h a d o w I m a g e %
4886% %
4887% %
4888% %
4889%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4890%
4891% ShadowImage() simulates a shadow from the specified image and returns it.
4892%
4893% The format of the ShadowImage method is:
4894%
4895% Image *ShadowImage(const Image *image,const double opacity,
4896% const double sigma,const long x_offset,const long y_offset,
4897% ExceptionInfo *exception)
4898%
4899% A description of each parameter follows:
4900%
4901% o image: the image.
4902%
4903% o opacity: percentage transparency.
4904%
4905% o sigma: the standard deviation of the Gaussian, in pixels.
4906%
4907% o x_offset: the shadow x-offset.
4908%
4909% o y_offset: the shadow y-offset.
4910%
4911% o exception: return any errors or warnings in this structure.
4912%
4913*/
4914MagickExport Image *ShadowImage(const Image *image,const double opacity,
4915 const double sigma,const long x_offset,const long y_offset,
4916 ExceptionInfo *exception)
4917{
4918#define ShadowImageTag "Shadow/Image"
4919
4920 Image
4921 *border_image,
4922 *clone_image,
4923 *shadow_image;
4924
4925 long
4926 progress,
4927 y;
4928
4929 MagickBooleanType
4930 status;
4931
4932 RectangleInfo
4933 border_info;
4934
4935 CacheView
4936 *image_view;
4937
4938 assert(image != (Image *) NULL);
4939 assert(image->signature == MagickSignature);
4940 if (image->debug != MagickFalse)
4941 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4942 assert(exception != (ExceptionInfo *) NULL);
4943 assert(exception->signature == MagickSignature);
4944 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4945 if (clone_image == (Image *) NULL)
4946 return((Image *) NULL);
4947 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4948 clone_image->compose=OverCompositeOp;
4949 border_info.width=(unsigned long) (2.0*sigma+0.5);
4950 border_info.height=(unsigned long) (2.0*sigma+0.5);
4951 border_info.x=0;
4952 border_info.y=0;
4953 (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4954 border_image=BorderImage(clone_image,&border_info,exception);
4955 clone_image=DestroyImage(clone_image);
4956 if (border_image == (Image *) NULL)
4957 return((Image *) NULL);
4958 if (border_image->matte == MagickFalse)
4959 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4960 /*
4961 Shadow image.
4962 */
4963 status=MagickTrue;
4964 progress=0;
4965 image_view=AcquireCacheView(border_image);
cristyb5d5f722009-11-04 03:03:49 +00004966#if defined(MAGICKCORE_OPENMP_SUPPORT)
4967 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004968#endif
4969 for (y=0; y < (long) border_image->rows; y++)
4970 {
4971 register long
4972 x;
4973
4974 register PixelPacket
4975 *__restrict q;
4976
4977 if (status == MagickFalse)
4978 continue;
4979 q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4980 exception);
4981 if (q == (PixelPacket *) NULL)
4982 {
4983 status=MagickFalse;
4984 continue;
4985 }
4986 for (x=0; x < (long) border_image->columns; x++)
4987 {
4988 q->red=border_image->background_color.red;
4989 q->green=border_image->background_color.green;
4990 q->blue=border_image->background_color.blue;
4991 if (border_image->matte == MagickFalse)
4992 q->opacity=border_image->background_color.opacity;
4993 else
4994 q->opacity=RoundToQuantum((MagickRealType) (QuantumRange-(QuantumRange-
4995 q->opacity)*opacity/100.0));
4996 q++;
4997 }
4998 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4999 status=MagickFalse;
5000 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5001 {
5002 MagickBooleanType
5003 proceed;
5004
cristyb5d5f722009-11-04 03:03:49 +00005005#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005006 #pragma omp critical (MagickCore_ShadowImage)
5007#endif
5008 proceed=SetImageProgress(image,ShadowImageTag,progress++,
5009 border_image->rows);
5010 if (proceed == MagickFalse)
5011 status=MagickFalse;
5012 }
5013 }
5014 image_view=DestroyCacheView(image_view);
5015 shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
5016 border_image=DestroyImage(border_image);
5017 if (shadow_image == (Image *) NULL)
5018 return((Image *) NULL);
5019 if (shadow_image->page.width == 0)
5020 shadow_image->page.width=shadow_image->columns;
5021 if (shadow_image->page.height == 0)
5022 shadow_image->page.height=shadow_image->rows;
5023 shadow_image->page.width+=x_offset-(long) border_info.width;
5024 shadow_image->page.height+=y_offset-(long) border_info.height;
5025 shadow_image->page.x+=x_offset-(long) border_info.width;
5026 shadow_image->page.y+=y_offset-(long) border_info.height;
5027 return(shadow_image);
5028}
5029
5030/*
5031%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5032% %
5033% %
5034% %
5035% S k e t c h I m a g e %
5036% %
5037% %
5038% %
5039%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5040%
5041% SketchImage() simulates a pencil sketch. We convolve the image with a
5042% Gaussian operator of the given radius and standard deviation (sigma). For
5043% reasonable results, radius should be larger than sigma. Use a radius of 0
5044% and SketchImage() selects a suitable radius for you. Angle gives the angle
5045% of the sketch.
5046%
5047% The format of the SketchImage method is:
5048%
5049% Image *SketchImage(const Image *image,const double radius,
5050% const double sigma,const double angle,ExceptionInfo *exception)
5051%
5052% A description of each parameter follows:
5053%
5054% o image: the image.
5055%
5056% o radius: the radius of the Gaussian, in pixels, not counting
5057% the center pixel.
5058%
5059% o sigma: the standard deviation of the Gaussian, in pixels.
5060%
5061% o angle: Apply the effect along this angle.
5062%
5063% o exception: return any errors or warnings in this structure.
5064%
5065*/
5066MagickExport Image *SketchImage(const Image *image,const double radius,
5067 const double sigma,const double angle,ExceptionInfo *exception)
5068{
5069 Image
5070 *blend_image,
5071 *blur_image,
5072 *dodge_image,
5073 *random_image,
5074 *sketch_image;
5075
5076 long
5077 y;
5078
5079 MagickBooleanType
5080 status;
5081
5082 MagickPixelPacket
5083 zero;
5084
5085 RandomInfo
5086 *random_info;
5087
5088 CacheView
5089 *random_view;
5090
5091 /*
5092 Sketch image.
5093 */
5094 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
5095 MagickTrue,exception);
5096 if (random_image == (Image *) NULL)
5097 return((Image *) NULL);
5098 status=MagickTrue;
5099 GetMagickPixelPacket(random_image,&zero);
5100 random_info=AcquireRandomInfo();
5101 random_view=AcquireCacheView(random_image);
5102 for (y=0; y < (long) random_image->rows; y++)
5103 {
5104 MagickPixelPacket
5105 pixel;
5106
5107 register IndexPacket
5108 *__restrict indexes;
5109
5110 register long
5111 x;
5112
5113 register PixelPacket
5114 *__restrict q;
5115
5116 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
5117 exception);
5118 if (q == (PixelPacket *) NULL)
5119 {
5120 status=MagickFalse;
5121 continue;
5122 }
5123 indexes=GetCacheViewAuthenticIndexQueue(random_view);
5124 pixel=zero;
5125 for (x=0; x < (long) random_image->columns; x++)
5126 {
5127 pixel.red=(MagickRealType) (QuantumRange*
5128 GetPseudoRandomValue(random_info));
5129 pixel.green=pixel.red;
5130 pixel.blue=pixel.red;
5131 if (image->colorspace == CMYKColorspace)
5132 pixel.index=pixel.red;
5133 SetPixelPacket(random_image,&pixel,q,indexes+x);
5134 q++;
5135 }
5136 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
5137 status=MagickFalse;
5138 if (status == MagickFalse)
5139 break;
5140 }
5141 random_view=DestroyCacheView(random_view);
5142 random_info=DestroyRandomInfo(random_info);
5143 if (status == MagickFalse)
5144 {
5145 random_image=DestroyImage(random_image);
5146 return(random_image);
5147 }
5148 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
5149 random_image=DestroyImage(random_image);
5150 if (blur_image == (Image *) NULL)
5151 return((Image *) NULL);
5152 dodge_image=EdgeImage(blur_image,radius,exception);
5153 blur_image=DestroyImage(blur_image);
5154 if (dodge_image == (Image *) NULL)
5155 return((Image *) NULL);
5156 (void) NormalizeImage(dodge_image);
5157 (void) NegateImage(dodge_image,MagickFalse);
5158 (void) TransformImage(&dodge_image,(char *) NULL,"50%");
5159 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
5160 if (sketch_image == (Image *) NULL)
5161 {
5162 dodge_image=DestroyImage(dodge_image);
5163 return((Image *) NULL);
5164 }
5165 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
5166 dodge_image=DestroyImage(dodge_image);
5167 blend_image=CloneImage(image,0,0,MagickTrue,exception);
5168 if (blend_image == (Image *) NULL)
5169 {
5170 sketch_image=DestroyImage(sketch_image);
5171 return((Image *) NULL);
5172 }
5173 (void) SetImageArtifact(blend_image,"compose:args","20x80");
5174 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
5175 blend_image=DestroyImage(blend_image);
5176 return(sketch_image);
5177}
5178
5179/*
5180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5181% %
5182% %
5183% %
5184% S o l a r i z e I m a g e %
5185% %
5186% %
5187% %
5188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5189%
5190% SolarizeImage() applies a special effect to the image, similar to the effect
5191% achieved in a photo darkroom by selectively exposing areas of photo
5192% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
5193% measure of the extent of the solarization.
5194%
5195% The format of the SolarizeImage method is:
5196%
5197% MagickBooleanType SolarizeImage(Image *image,const double threshold)
5198%
5199% A description of each parameter follows:
5200%
5201% o image: the image.
5202%
5203% o threshold: Define the extent of the solarization.
5204%
5205*/
5206MagickExport MagickBooleanType SolarizeImage(Image *image,
5207 const double threshold)
5208{
5209#define SolarizeImageTag "Solarize/Image"
5210
5211 ExceptionInfo
5212 *exception;
5213
5214 long
5215 progress,
5216 y;
5217
5218 MagickBooleanType
5219 status;
5220
5221 CacheView
5222 *image_view;
5223
5224 assert(image != (Image *) NULL);
5225 assert(image->signature == MagickSignature);
5226 if (image->debug != MagickFalse)
5227 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5228 if (image->storage_class == PseudoClass)
5229 {
5230 register long
5231 i;
5232
5233 /*
5234 Solarize colormap.
5235 */
5236 for (i=0; i < (long) image->colors; i++)
5237 {
5238 if ((MagickRealType) image->colormap[i].red > threshold)
5239 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
5240 if ((MagickRealType) image->colormap[i].green > threshold)
5241 image->colormap[i].green=(Quantum) QuantumRange-
5242 image->colormap[i].green;
5243 if ((MagickRealType) image->colormap[i].blue > threshold)
5244 image->colormap[i].blue=(Quantum) QuantumRange-
5245 image->colormap[i].blue;
5246 }
5247 }
5248 /*
5249 Solarize image.
5250 */
5251 status=MagickTrue;
5252 progress=0;
5253 exception=(&image->exception);
5254 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00005255#if defined(MAGICKCORE_OPENMP_SUPPORT)
5256 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005257#endif
5258 for (y=0; y < (long) image->rows; y++)
5259 {
5260 register long
5261 x;
5262
5263 register PixelPacket
5264 *__restrict q;
5265
5266 if (status == MagickFalse)
5267 continue;
5268 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5269 exception);
5270 if (q == (PixelPacket *) NULL)
5271 {
5272 status=MagickFalse;
5273 continue;
5274 }
5275 for (x=0; x < (long) image->columns; x++)
5276 {
5277 if ((MagickRealType) q->red > threshold)
5278 q->red=(Quantum) QuantumRange-q->red;
5279 if ((MagickRealType) q->green > threshold)
5280 q->green=(Quantum) QuantumRange-q->green;
5281 if ((MagickRealType) q->blue > threshold)
5282 q->blue=(Quantum) QuantumRange-q->blue;
5283 q++;
5284 }
5285 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5286 status=MagickFalse;
5287 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5288 {
5289 MagickBooleanType
5290 proceed;
5291
cristyb5d5f722009-11-04 03:03:49 +00005292#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005293 #pragma omp critical (MagickCore_SolarizeImage)
5294#endif
5295 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
5296 if (proceed == MagickFalse)
5297 status=MagickFalse;
5298 }
5299 }
5300 image_view=DestroyCacheView(image_view);
5301 return(status);
5302}
5303
5304/*
5305%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5306% %
5307% %
5308% %
5309% S t e g a n o I m a g e %
5310% %
5311% %
5312% %
5313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5314%
5315% SteganoImage() hides a digital watermark within the image. Recover
5316% the hidden watermark later to prove that the authenticity of an image.
5317% Offset defines the start position within the image to hide the watermark.
5318%
5319% The format of the SteganoImage method is:
5320%
5321% Image *SteganoImage(const Image *image,Image *watermark,
5322% ExceptionInfo *exception)
5323%
5324% A description of each parameter follows:
5325%
5326% o image: the image.
5327%
5328% o watermark: the watermark image.
5329%
5330% o exception: return any errors or warnings in this structure.
5331%
5332*/
5333MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
5334 ExceptionInfo *exception)
5335{
5336#define GetBit(alpha,i) ((((unsigned long) (alpha) >> (unsigned long) \
5337 (i)) & 0x01) != 0)
5338#define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) ? (unsigned long) (alpha) \
5339 | (1UL << (unsigned long) (i)) : (unsigned long) (alpha) & \
5340 ~(1UL << (unsigned long) (i)))
5341#define SteganoImageTag "Stegano/Image"
5342
5343 Image
5344 *stegano_image;
5345
5346 int
5347 c;
5348
5349 long
5350 i,
5351 j,
5352 k,
5353 y;
5354
5355 MagickBooleanType
5356 status;
5357
5358 PixelPacket
5359 pixel;
5360
5361 register long
5362 x;
5363
5364 register PixelPacket
5365 *q;
5366
5367 unsigned long
5368 depth;
5369
5370 /*
5371 Initialize steganographic image attributes.
5372 */
5373 assert(image != (const Image *) NULL);
5374 assert(image->signature == MagickSignature);
5375 if (image->debug != MagickFalse)
5376 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5377 assert(watermark != (const Image *) NULL);
5378 assert(watermark->signature == MagickSignature);
5379 assert(exception != (ExceptionInfo *) NULL);
5380 assert(exception->signature == MagickSignature);
5381 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
5382 if (stegano_image == (Image *) NULL)
5383 return((Image *) NULL);
5384 if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
5385 {
5386 InheritException(exception,&stegano_image->exception);
5387 stegano_image=DestroyImage(stegano_image);
5388 return((Image *) NULL);
5389 }
5390 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
5391 /*
5392 Hide watermark in low-order bits of image.
5393 */
5394 c=0;
5395 i=0;
5396 j=0;
5397 depth=stegano_image->depth;
5398 k=image->offset;
5399 for (i=(long) depth-1; (i >= 0) && (j < (long) depth); i--)
5400 {
5401 for (y=0; (y < (long) watermark->rows) && (j < (long) depth); y++)
5402 {
5403 for (x=0; (x < (long) watermark->columns) && (j < (long) depth); x++)
5404 {
5405 (void) GetOneVirtualPixel(watermark,x,y,&pixel,exception);
5406 if ((k/(long) stegano_image->columns) >= (long) stegano_image->rows)
5407 break;
5408 q=GetAuthenticPixels(stegano_image,k % (long) stegano_image->columns,
5409 k/(long) stegano_image->columns,1,1,exception);
5410 if (q == (PixelPacket *) NULL)
5411 break;
5412 switch (c)
5413 {
5414 case 0:
5415 {
5416 SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5417 break;
5418 }
5419 case 1:
5420 {
5421 SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5422 break;
5423 }
5424 case 2:
5425 {
5426 SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5427 break;
5428 }
5429 }
5430 if (SyncAuthenticPixels(stegano_image,exception) == MagickFalse)
5431 break;
5432 c++;
5433 if (c == 3)
5434 c=0;
5435 k++;
5436 if (k == (long) (stegano_image->columns*stegano_image->columns))
5437 k=0;
5438 if (k == image->offset)
5439 j++;
5440 }
5441 }
5442 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
5443 (QuantumTick((MagickOffsetType) depth-i,depth) != MagickFalse))
5444 {
5445 status=image->progress_monitor(SteganoImageTag,(MagickOffsetType) depth-
5446 i,depth,image->client_data);
5447 if (status == MagickFalse)
5448 break;
5449 }
5450 }
5451 if (stegano_image->storage_class == PseudoClass)
5452 (void) SyncImage(stegano_image);
5453 return(stegano_image);
5454}
5455
5456/*
5457%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5458% %
5459% %
5460% %
5461% S t e r e o A n a g l y p h I m a g e %
5462% %
5463% %
5464% %
5465%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5466%
5467% StereoAnaglyphImage() combines two images and produces a single image that
5468% is the composite of a left and right image of a stereo pair. Special
5469% red-green stereo glasses are required to view this effect.
5470%
5471% The format of the StereoAnaglyphImage method is:
5472%
5473% Image *StereoImage(const Image *left_image,const Image *right_image,
5474% ExceptionInfo *exception)
5475% Image *StereoAnaglyphImage(const Image *left_image,
5476% const Image *right_image,const long x_offset,const long y_offset,
5477% ExceptionInfo *exception)
5478%
5479% A description of each parameter follows:
5480%
5481% o left_image: the left image.
5482%
5483% o right_image: the right image.
5484%
5485% o exception: return any errors or warnings in this structure.
5486%
5487% o x_offset: amount, in pixels, by which the left image is offset to the
5488% right of the right image.
5489%
5490% o y_offset: amount, in pixels, by which the left image is offset to the
5491% bottom of the right image.
5492%
5493%
5494*/
5495MagickExport Image *StereoImage(const Image *left_image,
5496 const Image *right_image,ExceptionInfo *exception)
5497{
5498 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
5499}
5500
5501MagickExport Image *StereoAnaglyphImage(const Image *left_image,
5502 const Image *right_image,const long x_offset,const long y_offset,
5503 ExceptionInfo *exception)
5504{
5505#define StereoImageTag "Stereo/Image"
5506
5507 const Image
5508 *image;
5509
5510 Image
5511 *stereo_image;
5512
5513 long
5514 y;
5515
5516 MagickBooleanType
5517 status;
5518
5519 assert(left_image != (const Image *) NULL);
5520 assert(left_image->signature == MagickSignature);
5521 if (left_image->debug != MagickFalse)
5522 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5523 left_image->filename);
5524 assert(right_image != (const Image *) NULL);
5525 assert(right_image->signature == MagickSignature);
5526 assert(exception != (ExceptionInfo *) NULL);
5527 assert(exception->signature == MagickSignature);
5528 assert(right_image != (const Image *) NULL);
5529 image=left_image;
5530 if ((left_image->columns != right_image->columns) ||
5531 (left_image->rows != right_image->rows))
5532 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
5533 /*
5534 Initialize stereo image attributes.
5535 */
5536 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
5537 MagickTrue,exception);
5538 if (stereo_image == (Image *) NULL)
5539 return((Image *) NULL);
5540 if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
5541 {
5542 InheritException(exception,&stereo_image->exception);
5543 stereo_image=DestroyImage(stereo_image);
5544 return((Image *) NULL);
5545 }
5546 /*
5547 Copy left image to red channel and right image to blue channel.
5548 */
5549 for (y=0; y < (long) stereo_image->rows; y++)
5550 {
5551 register const PixelPacket
5552 *__restrict p,
5553 *__restrict q;
5554
5555 register long
5556 x;
5557
5558 register PixelPacket
5559 *__restrict r;
5560
5561 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
5562 exception);
5563 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
5564 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
5565 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
5566 (r == (PixelPacket *) NULL))
5567 break;
5568 for (x=0; x < (long) stereo_image->columns; x++)
5569 {
5570 r->red=p->red;
5571 r->green=q->green;
5572 r->blue=q->blue;
5573 r->opacity=(Quantum) ((p->opacity+q->opacity)/2);
5574 p++;
5575 q++;
5576 r++;
5577 }
5578 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
5579 break;
5580 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
5581 (QuantumTick(y,image->rows) != MagickFalse))
5582 {
5583 status=image->progress_monitor(StereoImageTag,y,stereo_image->rows,
5584 stereo_image->client_data);
5585 if (status == MagickFalse)
5586 break;
5587 }
5588 }
5589 return(stereo_image);
5590}
5591
5592/*
5593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5594% %
5595% %
5596% %
5597% S w i r l I m a g e %
5598% %
5599% %
5600% %
5601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5602%
5603% SwirlImage() swirls the pixels about the center of the image, where
5604% degrees indicates the sweep of the arc through which each pixel is moved.
5605% You get a more dramatic effect as the degrees move from 1 to 360.
5606%
5607% The format of the SwirlImage method is:
5608%
5609% Image *SwirlImage(const Image *image,double degrees,
5610% ExceptionInfo *exception)
5611%
5612% A description of each parameter follows:
5613%
5614% o image: the image.
5615%
5616% o degrees: Define the tightness of the swirling effect.
5617%
5618% o exception: return any errors or warnings in this structure.
5619%
5620*/
5621MagickExport Image *SwirlImage(const Image *image,double degrees,
5622 ExceptionInfo *exception)
5623{
5624#define SwirlImageTag "Swirl/Image"
5625
5626 Image
5627 *swirl_image;
5628
5629 long
5630 progress,
5631 y;
5632
5633 MagickBooleanType
5634 status;
5635
5636 MagickPixelPacket
5637 zero;
5638
5639 MagickRealType
5640 radius;
5641
5642 PointInfo
5643 center,
5644 scale;
5645
5646 ResampleFilter
5647 **resample_filter;
5648
5649 CacheView
5650 *image_view,
5651 *swirl_view;
5652
5653 /*
5654 Initialize swirl image attributes.
5655 */
5656 assert(image != (const Image *) NULL);
5657 assert(image->signature == MagickSignature);
5658 if (image->debug != MagickFalse)
5659 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5660 assert(exception != (ExceptionInfo *) NULL);
5661 assert(exception->signature == MagickSignature);
5662 swirl_image=CloneImage(image,0,0,MagickTrue,exception);
5663 if (swirl_image == (Image *) NULL)
5664 return((Image *) NULL);
5665 if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
5666 {
5667 InheritException(exception,&swirl_image->exception);
5668 swirl_image=DestroyImage(swirl_image);
5669 return((Image *) NULL);
5670 }
5671 if (swirl_image->background_color.opacity != OpaqueOpacity)
5672 swirl_image->matte=MagickTrue;
5673 /*
5674 Compute scaling factor.
5675 */
5676 center.x=(double) image->columns/2.0;
5677 center.y=(double) image->rows/2.0;
5678 radius=MagickMax(center.x,center.y);
5679 scale.x=1.0;
5680 scale.y=1.0;
5681 if (image->columns > image->rows)
5682 scale.y=(double) image->columns/(double) image->rows;
5683 else
5684 if (image->columns < image->rows)
5685 scale.x=(double) image->rows/(double) image->columns;
5686 degrees=(double) DegreesToRadians(degrees);
5687 /*
5688 Swirl image.
5689 */
5690 status=MagickTrue;
5691 progress=0;
5692 GetMagickPixelPacket(swirl_image,&zero);
5693 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
5694 image_view=AcquireCacheView(image);
5695 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005696#if defined(MAGICKCORE_OPENMP_SUPPORT)
5697 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005698#endif
5699 for (y=0; y < (long) image->rows; y++)
5700 {
5701 MagickPixelPacket
5702 pixel;
5703
5704 MagickRealType
5705 distance;
5706
5707 PointInfo
5708 delta;
5709
5710 register IndexPacket
5711 *__restrict swirl_indexes;
5712
5713 register long
5714 id,
5715 x;
5716
5717 register PixelPacket
5718 *__restrict q;
5719
5720 if (status == MagickFalse)
5721 continue;
5722 q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
5723 exception);
5724 if (q == (PixelPacket *) NULL)
5725 {
5726 status=MagickFalse;
5727 continue;
5728 }
5729 swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
5730 delta.y=scale.y*(double) (y-center.y);
5731 pixel=zero;
5732 id=GetOpenMPThreadId();
5733 for (x=0; x < (long) image->columns; x++)
5734 {
5735 /*
5736 Determine if the pixel is within an ellipse.
5737 */
5738 delta.x=scale.x*(double) (x-center.x);
5739 distance=delta.x*delta.x+delta.y*delta.y;
5740 if (distance < (radius*radius))
5741 {
5742 MagickRealType
5743 cosine,
5744 factor,
5745 sine;
5746
5747 /*
5748 Swirl the pixel.
5749 */
5750 factor=1.0-sqrt((double) distance)/radius;
5751 sine=sin((double) (degrees*factor*factor));
5752 cosine=cos((double) (degrees*factor*factor));
5753 (void) ResamplePixelColor(resample_filter[id],(double) ((cosine*
5754 delta.x-sine*delta.y)/scale.x+center.x),(double) ((sine*delta.x+
5755 cosine*delta.y)/scale.y+center.y),&pixel);
5756 SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
5757 }
5758 q++;
5759 }
5760 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5761 status=MagickFalse;
5762 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5763 {
5764 MagickBooleanType
5765 proceed;
5766
cristyb5d5f722009-11-04 03:03:49 +00005767#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005768 #pragma omp critical (MagickCore_SwirlImage)
5769#endif
5770 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5771 if (proceed == MagickFalse)
5772 status=MagickFalse;
5773 }
5774 }
5775 swirl_view=DestroyCacheView(swirl_view);
5776 image_view=DestroyCacheView(image_view);
5777 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5778 if (status == MagickFalse)
5779 swirl_image=DestroyImage(swirl_image);
5780 return(swirl_image);
5781}
5782
5783/*
5784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5785% %
5786% %
5787% %
5788% T i n t I m a g e %
5789% %
5790% %
5791% %
5792%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5793%
5794% TintImage() applies a color vector to each pixel in the image. The length
5795% of the vector is 0 for black and white and at its maximum for the midtones.
5796% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5797%
5798% The format of the TintImage method is:
5799%
5800% Image *TintImage(const Image *image,const char *opacity,
5801% const PixelPacket tint,ExceptionInfo *exception)
5802%
5803% A description of each parameter follows:
5804%
5805% o image: the image.
5806%
5807% o opacity: A color value used for tinting.
5808%
5809% o tint: A color value used for tinting.
5810%
5811% o exception: return any errors or warnings in this structure.
5812%
5813*/
5814MagickExport Image *TintImage(const Image *image,const char *opacity,
5815 const PixelPacket tint,ExceptionInfo *exception)
5816{
5817#define TintImageTag "Tint/Image"
5818
5819 GeometryInfo
5820 geometry_info;
5821
5822 Image
5823 *tint_image;
5824
5825 long
5826 progress,
5827 y;
5828
5829 MagickBooleanType
5830 status;
5831
5832 MagickStatusType
5833 flags;
5834
5835 MagickPixelPacket
5836 color_vector,
5837 pixel;
5838
5839 CacheView
5840 *image_view,
5841 *tint_view;
5842
5843 /*
5844 Allocate tint image.
5845 */
5846 assert(image != (const Image *) NULL);
5847 assert(image->signature == MagickSignature);
5848 if (image->debug != MagickFalse)
5849 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5850 assert(exception != (ExceptionInfo *) NULL);
5851 assert(exception->signature == MagickSignature);
5852 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5853 if (tint_image == (Image *) NULL)
5854 return((Image *) NULL);
5855 if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5856 {
5857 InheritException(exception,&tint_image->exception);
5858 tint_image=DestroyImage(tint_image);
5859 return((Image *) NULL);
5860 }
5861 if (opacity == (const char *) NULL)
5862 return(tint_image);
5863 /*
5864 Determine RGB values of the color.
5865 */
5866 flags=ParseGeometry(opacity,&geometry_info);
5867 pixel.red=geometry_info.rho;
5868 if ((flags & SigmaValue) != 0)
5869 pixel.green=geometry_info.sigma;
5870 else
5871 pixel.green=pixel.red;
5872 if ((flags & XiValue) != 0)
5873 pixel.blue=geometry_info.xi;
5874 else
5875 pixel.blue=pixel.red;
5876 if ((flags & PsiValue) != 0)
5877 pixel.opacity=geometry_info.psi;
5878 else
5879 pixel.opacity=(MagickRealType) OpaqueOpacity;
5880 color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5881 PixelIntensity(&tint));
5882 color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5883 PixelIntensity(&tint));
5884 color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5885 PixelIntensity(&tint));
5886 /*
5887 Tint image.
5888 */
5889 status=MagickTrue;
5890 progress=0;
5891 image_view=AcquireCacheView(image);
5892 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005893#if defined(MAGICKCORE_OPENMP_SUPPORT)
5894 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005895#endif
5896 for (y=0; y < (long) image->rows; y++)
5897 {
5898 register const PixelPacket
5899 *__restrict p;
5900
5901 register long
5902 x;
5903
5904 register PixelPacket
5905 *__restrict q;
5906
5907 if (status == MagickFalse)
5908 continue;
5909 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5910 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5911 exception);
5912 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5913 {
5914 status=MagickFalse;
5915 continue;
5916 }
5917 for (x=0; x < (long) image->columns; x++)
5918 {
5919 MagickPixelPacket
5920 pixel;
5921
5922 MagickRealType
5923 weight;
5924
5925 weight=QuantumScale*p->red-0.5;
5926 pixel.red=(MagickRealType) p->red+color_vector.red*(1.0-(4.0*
5927 (weight*weight)));
5928 q->red=RoundToQuantum(pixel.red);
5929 weight=QuantumScale*p->green-0.5;
5930 pixel.green=(MagickRealType) p->green+color_vector.green*(1.0-(4.0*
5931 (weight*weight)));
5932 q->green=RoundToQuantum(pixel.green);
5933 weight=QuantumScale*p->blue-0.5;
5934 pixel.blue=(MagickRealType) p->blue+color_vector.blue*(1.0-(4.0*
5935 (weight*weight)));
5936 q->blue=RoundToQuantum(pixel.blue);
5937 q->opacity=p->opacity;
5938 p++;
5939 q++;
5940 }
5941 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5942 status=MagickFalse;
5943 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5944 {
5945 MagickBooleanType
5946 proceed;
5947
cristyb5d5f722009-11-04 03:03:49 +00005948#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005949 #pragma omp critical (MagickCore_TintImage)
5950#endif
5951 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5952 if (proceed == MagickFalse)
5953 status=MagickFalse;
5954 }
5955 }
5956 tint_view=DestroyCacheView(tint_view);
5957 image_view=DestroyCacheView(image_view);
5958 if (status == MagickFalse)
5959 tint_image=DestroyImage(tint_image);
5960 return(tint_image);
5961}
5962
5963/*
5964%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5965% %
5966% %
5967% %
5968% V i g n e t t e I m a g e %
5969% %
5970% %
5971% %
5972%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5973%
5974% VignetteImage() softens the edges of the image in vignette style.
5975%
5976% The format of the VignetteImage method is:
5977%
5978% Image *VignetteImage(const Image *image,const double radius,
5979% const double sigma,const long x,const long y,ExceptionInfo *exception)
5980%
5981% A description of each parameter follows:
5982%
5983% o image: the image.
5984%
5985% o radius: the radius of the pixel neighborhood.
5986%
5987% o sigma: the standard deviation of the Gaussian, in pixels.
5988%
5989% o x, y: Define the x and y ellipse offset.
5990%
5991% o exception: return any errors or warnings in this structure.
5992%
5993*/
5994MagickExport Image *VignetteImage(const Image *image,const double radius,
5995 const double sigma,const long x,const long y,ExceptionInfo *exception)
5996{
5997 char
5998 ellipse[MaxTextExtent];
5999
6000 DrawInfo
6001 *draw_info;
6002
6003 Image
6004 *canvas_image,
6005 *blur_image,
6006 *oval_image,
6007 *vignette_image;
6008
6009 assert(image != (Image *) NULL);
6010 assert(image->signature == MagickSignature);
6011 if (image->debug != MagickFalse)
6012 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
6013 assert(exception != (ExceptionInfo *) NULL);
6014 assert(exception->signature == MagickSignature);
6015 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
6016 if (canvas_image == (Image *) NULL)
6017 return((Image *) NULL);
6018 if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
6019 {
6020 InheritException(exception,&canvas_image->exception);
6021 canvas_image=DestroyImage(canvas_image);
6022 return((Image *) NULL);
6023 }
6024 canvas_image->matte=MagickTrue;
6025 oval_image=CloneImage(canvas_image,canvas_image->columns,
6026 canvas_image->rows,MagickTrue,exception);
6027 if (oval_image == (Image *) NULL)
6028 {
6029 canvas_image=DestroyImage(canvas_image);
6030 return((Image *) NULL);
6031 }
6032 (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
6033 (void) SetImageBackgroundColor(oval_image);
6034 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
6035 (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
6036 (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
6037 (void) FormatMagickString(ellipse,MaxTextExtent,
6038 "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,image->rows/2.0,
6039 image->columns/2.0-x,image->rows/2.0-y);
6040 draw_info->primitive=AcquireString(ellipse);
6041 (void) DrawImage(oval_image,draw_info);
6042 draw_info=DestroyDrawInfo(draw_info);
6043 blur_image=BlurImage(oval_image,radius,sigma,exception);
6044 oval_image=DestroyImage(oval_image);
6045 if (blur_image == (Image *) NULL)
6046 {
6047 canvas_image=DestroyImage(canvas_image);
6048 return((Image *) NULL);
6049 }
6050 blur_image->matte=MagickFalse;
6051 (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
6052 blur_image=DestroyImage(blur_image);
6053 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
6054 canvas_image=DestroyImage(canvas_image);
6055 return(vignette_image);
6056}
6057
6058/*
6059%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6060% %
6061% %
6062% %
6063% W a v e I m a g e %
6064% %
6065% %
6066% %
6067%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6068%
6069% WaveImage() creates a "ripple" effect in the image by shifting the pixels
6070% vertically along a sine wave whose amplitude and wavelength is specified
6071% by the given parameters.
6072%
6073% The format of the WaveImage method is:
6074%
6075% Image *WaveImage(const Image *image,const double amplitude,
6076% const double wave_length,ExceptionInfo *exception)
6077%
6078% A description of each parameter follows:
6079%
6080% o image: the image.
6081%
6082% o amplitude, wave_length: Define the amplitude and wave length of the
6083% sine wave.
6084%
6085% o exception: return any errors or warnings in this structure.
6086%
6087*/
6088MagickExport Image *WaveImage(const Image *image,const double amplitude,
6089 const double wave_length,ExceptionInfo *exception)
6090{
6091#define WaveImageTag "Wave/Image"
6092
6093 Image
6094 *wave_image;
6095
6096 long
6097 progress,
6098 y;
6099
6100 MagickBooleanType
6101 status;
6102
6103 MagickPixelPacket
6104 zero;
6105
6106 MagickRealType
6107 *sine_map;
6108
6109 register long
6110 i;
6111
6112 ResampleFilter
6113 **resample_filter;
6114
6115 CacheView
6116 *wave_view;
6117
6118 /*
6119 Initialize wave image attributes.
6120 */
6121 assert(image != (Image *) NULL);
6122 assert(image->signature == MagickSignature);
6123 if (image->debug != MagickFalse)
6124 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
6125 assert(exception != (ExceptionInfo *) NULL);
6126 assert(exception->signature == MagickSignature);
6127 wave_image=CloneImage(image,image->columns,(unsigned long) (image->rows+2.0*
6128 fabs(amplitude)),MagickTrue,exception);
6129 if (wave_image == (Image *) NULL)
6130 return((Image *) NULL);
6131 if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
6132 {
6133 InheritException(exception,&wave_image->exception);
6134 wave_image=DestroyImage(wave_image);
6135 return((Image *) NULL);
6136 }
6137 if (wave_image->background_color.opacity != OpaqueOpacity)
6138 wave_image->matte=MagickTrue;
6139 /*
6140 Allocate sine map.
6141 */
6142 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
6143 sizeof(*sine_map));
6144 if (sine_map == (MagickRealType *) NULL)
6145 {
6146 wave_image=DestroyImage(wave_image);
6147 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
6148 }
6149 for (i=0; i < (long) wave_image->columns; i++)
6150 sine_map[i]=fabs(amplitude)+amplitude*sin((2*MagickPI*i)/wave_length);
6151 /*
6152 Wave image.
6153 */
6154 status=MagickTrue;
6155 progress=0;
6156 GetMagickPixelPacket(wave_image,&zero);
6157 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
6158 wave_view=AcquireCacheView(wave_image);
cristyb5d5f722009-11-04 03:03:49 +00006159#if defined(MAGICKCORE_OPENMP_SUPPORT)
6160 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00006161#endif
6162 for (y=0; y < (long) wave_image->rows; y++)
6163 {
6164 MagickPixelPacket
6165 pixel;
6166
6167 register IndexPacket
6168 *__restrict indexes;
6169
6170 register long
6171 id,
6172 x;
6173
6174 register PixelPacket
6175 *__restrict q;
6176
6177 if (status == MagickFalse)
6178 continue;
6179 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
6180 exception);
6181 if (q == (PixelPacket *) NULL)
6182 {
6183 status=MagickFalse;
6184 continue;
6185 }
6186 indexes=GetCacheViewAuthenticIndexQueue(wave_view);
6187 pixel=zero;
6188 id=GetOpenMPThreadId();
6189 (void) SetResampleFilterVirtualPixelMethod(resample_filter[id],
6190 BackgroundVirtualPixelMethod);
6191 for (x=0; x < (long) wave_image->columns; x++)
6192 {
6193 (void) ResamplePixelColor(resample_filter[id],(double) x,(double) (y-
6194 sine_map[x]),&pixel);
6195 SetPixelPacket(wave_image,&pixel,q,indexes+x);
6196 q++;
6197 }
6198 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
6199 status=MagickFalse;
6200 if (image->progress_monitor != (MagickProgressMonitor) NULL)
6201 {
6202 MagickBooleanType
6203 proceed;
6204
cristyb5d5f722009-11-04 03:03:49 +00006205#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00006206 #pragma omp critical (MagickCore_WaveImage)
6207#endif
6208 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
6209 if (proceed == MagickFalse)
6210 status=MagickFalse;
6211 }
6212 }
6213 wave_view=DestroyCacheView(wave_view);
6214 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
6215 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
6216 if (status == MagickFalse)
6217 wave_image=DestroyImage(wave_image);
6218 return(wave_image);
6219}