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