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