blob: 8400d40eb01c76cdb9d798d18404212ef6907dc3 [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
cristyc4c8d132010-01-07 01:58:38 +0000453 CacheView
454 *image_view,
455 *shift_view;
456
cristy3ed852e2009-09-05 21:47:34 +0000457 Image
458 *shift_image;
459
460 long
461 progress,
462 y;
463
464 MagickBooleanType
465 status;
466
cristy3ed852e2009-09-05 21:47:34 +0000467 /*
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 {
cristyc4c8d132010-01-07 01:58:38 +0000528 quantum=GetRedSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000529 if (p->green < quantum)
cristyc4c8d132010-01-07 01:58:38 +0000530 quantum=GetGreenSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000531 if (p->blue < quantum)
cristyc4c8d132010-01-07 01:58:38 +0000532 quantum=GetBlueSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000533 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);
cristyc4c8d132010-01-07 01:58:38 +0000536 quantum=GetRedSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000537 if (p->green > quantum)
cristyc4c8d132010-01-07 01:58:38 +0000538 quantum=GetGreenSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000539 if (p->blue > quantum)
cristyc4c8d132010-01-07 01:58:38 +0000540 quantum=GetBlueSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000541 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
cristyc4c8d132010-01-07 01:58:38 +0000675 CacheView
676 *colorize_view,
677 *image_view;
678
cristy3ed852e2009-09-05 21:47:34 +0000679 GeometryInfo
680 geometry_info;
681
682 Image
683 *colorize_image;
684
685 long
686 progress,
687 y;
688
689 MagickBooleanType
690 status;
691
692 MagickPixelPacket
693 pixel;
694
695 MagickStatusType
696 flags;
697
cristy3ed852e2009-09-05 21:47:34 +0000698 /*
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
cristyc4c8d132010-01-07 01:58:38 +00001320 CacheView
1321 *image_view;
1322
cristy3ed852e2009-09-05 21:47:34 +00001323 long
1324 progress,
1325 y;
1326
1327 MagickBooleanType
1328 status;
1329
cristy3ed852e2009-09-05 21:47:34 +00001330 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);
cristy8cd5b312010-01-07 01:10:24 +00001496 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001497 }
1498 if (LocaleNCompare(symbol,"maxima",6) == 0)
1499 {
1500 double
1501 maxima,
1502 minima;
1503
1504 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristy8cd5b312010-01-07 01:10:24 +00001505 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001506 }
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);
cristy8cd5b312010-01-07 01:10:24 +00001515 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001516 }
1517 if (LocaleNCompare(symbol,"minima",6) == 0)
1518 {
1519 double
1520 maxima,
1521 minima;
1522
1523 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristy8cd5b312010-01-07 01:10:24 +00001524 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001525 }
1526 if (LocaleNCompare(symbol,"skewness",8) == 0)
1527 {
1528 double
1529 kurtosis,
1530 skewness;
1531
1532 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1533 exception);
cristy8cd5b312010-01-07 01:10:24 +00001534 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001535 }
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);
cristy8cd5b312010-01-07 01:10:24 +00001544 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",
cristy3ed852e2009-09-05 21:47:34 +00001545 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);
cristy8cd5b312010-01-07 01:10:24 +00002545 (void) FormatMagickString(numeric,MaxTextExtent,"%.15g",(double)
2546 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002547 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2548 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2549 subexpression),ConstantString(numeric));
2550 return(*beta);
2551 }
2552 case ',':
2553 {
2554 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2555 return(alpha);
2556 }
2557 case ';':
2558 {
2559 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2560 return(*beta);
2561 }
2562 default:
2563 {
2564 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2565 exception);
2566 return(gamma);
2567 }
2568 }
2569 }
2570 if (strchr("(",(int) *expression) != (char *) NULL)
2571 {
2572 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2573 subexpression[strlen(subexpression)-1]='\0';
2574 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2575 exception);
2576 return(gamma);
2577 }
2578 switch (*expression)
2579 {
2580 case '+':
2581 {
2582 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2583 exception);
2584 return(1.0*gamma);
2585 }
2586 case '-':
2587 {
2588 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2589 exception);
2590 return(-1.0*gamma);
2591 }
2592 case '~':
2593 {
2594 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2595 exception);
2596 return((MagickRealType) (~(unsigned long) (gamma+0.5)));
2597 }
2598 case 'A':
2599 case 'a':
2600 {
2601 if (LocaleNCompare(expression,"abs",3) == 0)
2602 {
2603 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2604 exception);
2605 return((MagickRealType) fabs((double) alpha));
2606 }
2607 if (LocaleNCompare(expression,"acos",4) == 0)
2608 {
2609 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2610 exception);
2611 return((MagickRealType) acos((double) alpha));
2612 }
2613 if (LocaleNCompare(expression,"asin",4) == 0)
2614 {
2615 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2616 exception);
2617 return((MagickRealType) asin((double) alpha));
2618 }
2619 if (LocaleNCompare(expression,"alt",3) == 0)
2620 {
2621 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2622 exception);
2623 return(((long) alpha) & 0x01 ? -1.0 : 1.0);
2624 }
2625 if (LocaleNCompare(expression,"atan2",5) == 0)
2626 {
2627 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2628 exception);
2629 return((MagickRealType) atan2((double) alpha,(double) *beta));
2630 }
2631 if (LocaleNCompare(expression,"atan",4) == 0)
2632 {
2633 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2634 exception);
2635 return((MagickRealType) atan((double) alpha));
2636 }
2637 if (LocaleCompare(expression,"a") == 0)
2638 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2639 break;
2640 }
2641 case 'B':
2642 case 'b':
2643 {
2644 if (LocaleCompare(expression,"b") == 0)
2645 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2646 break;
2647 }
2648 case 'C':
2649 case 'c':
2650 {
2651 if (LocaleNCompare(expression,"ceil",4) == 0)
2652 {
2653 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2654 exception);
2655 return((MagickRealType) ceil((double) alpha));
2656 }
2657 if (LocaleNCompare(expression,"cosh",4) == 0)
2658 {
2659 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2660 exception);
2661 return((MagickRealType) cosh((double) alpha));
2662 }
2663 if (LocaleNCompare(expression,"cos",3) == 0)
2664 {
2665 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2666 exception);
2667 return((MagickRealType) cos((double) alpha));
2668 }
2669 if (LocaleCompare(expression,"c") == 0)
2670 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2671 break;
2672 }
2673 case 'D':
2674 case 'd':
2675 {
2676 if (LocaleNCompare(expression,"debug",5) == 0)
2677 {
2678 const char
2679 *type;
2680
2681 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2682 exception);
2683 if (fx_info->images->colorspace == CMYKColorspace)
2684 switch (channel)
2685 {
2686 case CyanChannel: type="cyan"; break;
2687 case MagentaChannel: type="magenta"; break;
2688 case YellowChannel: type="yellow"; break;
2689 case OpacityChannel: type="opacity"; break;
2690 case BlackChannel: type="black"; break;
2691 default: type="unknown"; break;
2692 }
2693 else
2694 switch (channel)
2695 {
2696 case RedChannel: type="red"; break;
2697 case GreenChannel: type="green"; break;
2698 case BlueChannel: type="blue"; break;
2699 case OpacityChannel: type="opacity"; break;
2700 default: type="unknown"; break;
2701 }
2702 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2703 if (strlen(subexpression) > 1)
2704 subexpression[strlen(subexpression)-1]='\0';
2705 if (fx_info->file != (FILE *) NULL)
cristy8cd5b312010-01-07 01:10:24 +00002706 (void) fprintf(fx_info->file,"%s[%ld,%ld].%s: %s=%.15g\n",
cristy3ed852e2009-09-05 21:47:34 +00002707 fx_info->images->filename,x,y,type,subexpression,(double) alpha);
2708 return(0.0);
2709 }
2710 break;
2711 }
2712 case 'E':
2713 case 'e':
2714 {
2715 if (LocaleCompare(expression,"epsilon") == 0)
2716 return((MagickRealType) MagickEpsilon);
2717 if (LocaleNCompare(expression,"exp",3) == 0)
2718 {
2719 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2720 exception);
2721 return((MagickRealType) exp((double) alpha));
2722 }
2723 if (LocaleCompare(expression,"e") == 0)
2724 return((MagickRealType) 2.7182818284590452354);
2725 break;
2726 }
2727 case 'F':
2728 case 'f':
2729 {
2730 if (LocaleNCompare(expression,"floor",5) == 0)
2731 {
2732 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2733 exception);
2734 return((MagickRealType) floor((double) alpha));
2735 }
2736 break;
2737 }
2738 case 'G':
2739 case 'g':
2740 {
2741 if (LocaleCompare(expression,"g") == 0)
2742 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2743 break;
2744 }
2745 case 'H':
2746 case 'h':
2747 {
2748 if (LocaleCompare(expression,"h") == 0)
2749 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2750 if (LocaleCompare(expression,"hue") == 0)
2751 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2752 if (LocaleNCompare(expression,"hypot",5) == 0)
2753 {
2754 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2755 exception);
2756 return((MagickRealType) hypot((double) alpha,(double) *beta));
2757 }
2758 break;
2759 }
2760 case 'K':
2761 case 'k':
2762 {
2763 if (LocaleCompare(expression,"k") == 0)
2764 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2765 break;
2766 }
2767 case 'I':
2768 case 'i':
2769 {
2770 if (LocaleCompare(expression,"intensity") == 0)
2771 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2772 if (LocaleNCompare(expression,"int",3) == 0)
2773 {
2774 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2775 exception);
2776 return((MagickRealType) floor(alpha+0.5));
2777 }
2778 if (LocaleCompare(expression,"i") == 0)
2779 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2780 break;
2781 }
2782 case 'J':
2783 case 'j':
2784 {
2785 if (LocaleCompare(expression,"j") == 0)
2786 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2787 break;
2788 }
2789 case 'L':
2790 case 'l':
2791 {
2792 if (LocaleNCompare(expression,"ln",2) == 0)
2793 {
2794 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2795 exception);
2796 return((MagickRealType) log((double) alpha));
2797 }
2798 if (LocaleNCompare(expression,"logtwo",4) == 0)
2799 {
2800 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2801 exception);
2802 return((MagickRealType) log10((double) alpha))/log10(2.0);
2803 }
2804 if (LocaleNCompare(expression,"log",3) == 0)
2805 {
2806 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2807 exception);
2808 return((MagickRealType) log10((double) alpha));
2809 }
2810 if (LocaleCompare(expression,"lightness") == 0)
2811 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2812 break;
2813 }
2814 case 'M':
2815 case 'm':
2816 {
2817 if (LocaleCompare(expression,"MaxRGB") == 0)
2818 return((MagickRealType) QuantumRange);
2819 if (LocaleNCompare(expression,"maxima",6) == 0)
2820 break;
2821 if (LocaleNCompare(expression,"max",3) == 0)
2822 return(FxMax(fx_info,channel,x,y,expression+3,exception));
2823 if (LocaleNCompare(expression,"minima",6) == 0)
2824 break;
2825 if (LocaleNCompare(expression,"min",3) == 0)
2826 return(FxMin(fx_info,channel,x,y,expression+3,exception));
2827 if (LocaleNCompare(expression,"mod",3) == 0)
2828 {
2829 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2830 exception);
2831 return((MagickRealType) fmod((double) alpha,(double) *beta));
2832 }
2833 if (LocaleCompare(expression,"m") == 0)
2834 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2835 break;
2836 }
2837 case 'N':
2838 case 'n':
2839 {
2840 if (LocaleCompare(expression,"n") == 0)
2841 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2842 break;
2843 }
2844 case 'O':
2845 case 'o':
2846 {
2847 if (LocaleCompare(expression,"Opaque") == 0)
2848 return(1.0);
2849 if (LocaleCompare(expression,"o") == 0)
2850 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2851 break;
2852 }
2853 case 'P':
2854 case 'p':
2855 {
2856 if (LocaleCompare(expression,"pi") == 0)
2857 return((MagickRealType) MagickPI);
2858 if (LocaleNCompare(expression,"pow",3) == 0)
2859 {
2860 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2861 exception);
2862 return((MagickRealType) pow((double) alpha,(double) *beta));
2863 }
2864 if (LocaleCompare(expression,"p") == 0)
2865 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2866 break;
2867 }
2868 case 'Q':
2869 case 'q':
2870 {
2871 if (LocaleCompare(expression,"QuantumRange") == 0)
2872 return((MagickRealType) QuantumRange);
2873 if (LocaleCompare(expression,"QuantumScale") == 0)
2874 return((MagickRealType) QuantumScale);
2875 break;
2876 }
2877 case 'R':
2878 case 'r':
2879 {
2880 if (LocaleNCompare(expression,"rand",4) == 0)
2881 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2882 if (LocaleNCompare(expression,"round",5) == 0)
2883 {
2884 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2885 exception);
2886 if (alpha >= 0.0)
2887 return((MagickRealType) floor((double) alpha+0.5));
2888 return((MagickRealType) ceil((double) alpha-0.5));
2889 }
2890 if (LocaleCompare(expression,"r") == 0)
2891 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2892 break;
2893 }
2894 case 'S':
2895 case 's':
2896 {
2897 if (LocaleCompare(expression,"saturation") == 0)
2898 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2899 if (LocaleNCompare(expression,"sign",4) == 0)
2900 {
2901 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2902 exception);
2903 return(alpha < 0.0 ? -1.0 : 1.0);
2904 }
2905 if (LocaleNCompare(expression,"sinh",4) == 0)
2906 {
2907 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2908 exception);
2909 return((MagickRealType) sinh((double) alpha));
2910 }
2911 if (LocaleNCompare(expression,"sin",3) == 0)
2912 {
2913 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2914 exception);
2915 return((MagickRealType) sin((double) alpha));
2916 }
2917 if (LocaleNCompare(expression,"sqrt",4) == 0)
2918 {
2919 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2920 exception);
2921 return((MagickRealType) sqrt((double) alpha));
2922 }
2923 if (LocaleCompare(expression,"s") == 0)
2924 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2925 break;
2926 }
2927 case 'T':
2928 case 't':
2929 {
2930 if (LocaleNCompare(expression,"tanh",4) == 0)
2931 {
2932 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2933 exception);
2934 return((MagickRealType) tanh((double) alpha));
2935 }
2936 if (LocaleNCompare(expression,"tan",3) == 0)
2937 {
2938 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2939 exception);
2940 return((MagickRealType) tan((double) alpha));
2941 }
2942 if (LocaleCompare(expression,"Transparent") == 0)
2943 return(0.0);
2944 if (LocaleCompare(expression,"t") == 0)
2945 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2946 break;
2947 }
2948 case 'U':
2949 case 'u':
2950 {
2951 if (LocaleCompare(expression,"u") == 0)
2952 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2953 break;
2954 }
2955 case 'V':
2956 case 'v':
2957 {
2958 if (LocaleCompare(expression,"v") == 0)
2959 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2960 break;
2961 }
2962 case 'W':
2963 case 'w':
2964 {
2965 if (LocaleCompare(expression,"w") == 0)
2966 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2967 break;
2968 }
2969 case 'Y':
2970 case 'y':
2971 {
2972 if (LocaleCompare(expression,"y") == 0)
2973 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2974 break;
2975 }
2976 case 'Z':
2977 case 'z':
2978 {
2979 if (LocaleCompare(expression,"z") == 0)
2980 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2981 break;
2982 }
2983 default:
2984 break;
2985 }
2986 q=(char *) expression;
2987 alpha=strtod(expression,&q);
2988 if (q == expression)
2989 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2990 return(alpha);
2991}
2992
2993MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2994 MagickRealType *alpha,ExceptionInfo *exception)
2995{
2996 MagickBooleanType
2997 status;
2998
2999 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
3000 return(status);
3001}
3002
3003MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
3004 MagickRealType *alpha,ExceptionInfo *exception)
3005{
3006 FILE
3007 *file;
3008
3009 MagickBooleanType
3010 status;
3011
3012 file=fx_info->file;
3013 fx_info->file=(FILE *) NULL;
3014 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
3015 fx_info->file=file;
3016 return(status);
3017}
3018
3019MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
3020 const ChannelType channel,const long x,const long y,MagickRealType *alpha,
3021 ExceptionInfo *exception)
3022{
3023 MagickRealType
3024 beta;
3025
3026 beta=0.0;
3027 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
3028 exception);
3029 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
3030}
3031
3032/*
3033%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3034% %
3035% %
3036% %
3037% F x I m a g e %
3038% %
3039% %
3040% %
3041%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3042%
3043% FxImage() applies a mathematical expression to the specified image.
3044%
3045% The format of the FxImage method is:
3046%
3047% Image *FxImage(const Image *image,const char *expression,
3048% ExceptionInfo *exception)
3049% Image *FxImageChannel(const Image *image,const ChannelType channel,
3050% const char *expression,ExceptionInfo *exception)
3051%
3052% A description of each parameter follows:
3053%
3054% o image: the image.
3055%
3056% o channel: the channel.
3057%
3058% o expression: A mathematical expression.
3059%
3060% o exception: return any errors or warnings in this structure.
3061%
3062*/
3063
3064static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
3065{
3066 register long
3067 i;
3068
3069 assert(fx_info != (FxInfo **) NULL);
3070 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
3071 if (fx_info[i] != (FxInfo *) NULL)
3072 fx_info[i]=DestroyFxInfo(fx_info[i]);
3073 fx_info=(FxInfo **) RelinquishAlignedMemory(fx_info);
3074 return(fx_info);
3075}
3076
3077static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
3078 ExceptionInfo *exception)
3079{
3080 char
3081 *fx_expression;
3082
3083 FxInfo
3084 **fx_info;
3085
3086 MagickRealType
3087 alpha;
3088
3089 register long
3090 i;
3091
3092 unsigned long
3093 number_threads;
3094
3095 number_threads=GetOpenMPMaximumThreads();
3096 fx_info=(FxInfo **) AcquireAlignedMemory(number_threads,sizeof(*fx_info));
3097 if (fx_info == (FxInfo **) NULL)
3098 return((FxInfo **) NULL);
3099 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
3100 if (*expression != '@')
3101 fx_expression=ConstantString(expression);
3102 else
3103 fx_expression=FileToString(expression+1,~0,exception);
3104 for (i=0; i < (long) number_threads; i++)
3105 {
3106 fx_info[i]=AcquireFxInfo(image,fx_expression);
3107 if (fx_info[i] == (FxInfo *) NULL)
3108 return(DestroyFxThreadSet(fx_info));
3109 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
3110 }
3111 fx_expression=DestroyString(fx_expression);
3112 return(fx_info);
3113}
3114
3115MagickExport Image *FxImage(const Image *image,const char *expression,
3116 ExceptionInfo *exception)
3117{
3118 Image
3119 *fx_image;
3120
3121 fx_image=FxImageChannel(image,GrayChannel,expression,exception);
3122 return(fx_image);
3123}
3124
3125MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
3126 const char *expression,ExceptionInfo *exception)
3127{
3128#define FxImageTag "Fx/Image"
3129
cristyfa112112010-01-04 17:48:07 +00003130 CacheView
3131 *fx_view;
3132
cristy3ed852e2009-09-05 21:47:34 +00003133 FxInfo
cristyfa112112010-01-04 17:48:07 +00003134 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00003135
3136 Image
3137 *fx_image;
3138
3139 long
3140 progress,
3141 y;
3142
3143 MagickBooleanType
3144 status;
3145
3146 MagickRealType
3147 alpha;
3148
cristy3ed852e2009-09-05 21:47:34 +00003149 assert(image != (Image *) NULL);
3150 assert(image->signature == MagickSignature);
3151 if (image->debug != MagickFalse)
3152 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3153 fx_image=CloneImage(image,0,0,MagickTrue,exception);
3154 if (fx_image == (Image *) NULL)
3155 return((Image *) NULL);
3156 if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
3157 {
3158 InheritException(exception,&fx_image->exception);
3159 fx_image=DestroyImage(fx_image);
3160 return((Image *) NULL);
3161 }
3162 fx_info=AcquireFxThreadSet(image,expression,exception);
3163 if (fx_info == (FxInfo **) NULL)
3164 {
3165 fx_image=DestroyImage(fx_image);
3166 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3167 }
3168 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3169 if (status == MagickFalse)
3170 {
3171 fx_image=DestroyImage(fx_image);
3172 fx_info=DestroyFxThreadSet(fx_info);
3173 return((Image *) NULL);
3174 }
3175 /*
3176 Fx image.
3177 */
3178 status=MagickTrue;
3179 progress=0;
3180 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003181#if defined(MAGICKCORE_OPENMP_SUPPORT)
3182 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003183#endif
3184 for (y=0; y < (long) fx_image->rows; y++)
3185 {
3186 MagickRealType
3187 alpha;
3188
3189 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003190 *restrict fx_indexes;
cristy3ed852e2009-09-05 21:47:34 +00003191
3192 register long
3193 id,
3194 x;
3195
3196 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003197 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003198
3199 if (status == MagickFalse)
3200 continue;
3201 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
3202 if (q == (PixelPacket *) NULL)
3203 {
3204 status=MagickFalse;
3205 continue;
3206 }
3207 fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
3208 id=GetOpenMPThreadId();
3209 alpha=0.0;
3210 for (x=0; x < (long) fx_image->columns; x++)
3211 {
3212 if ((channel & RedChannel) != 0)
3213 {
3214 (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
3215 &alpha,exception);
3216 q->red=RoundToQuantum((MagickRealType) QuantumRange*alpha);
3217 }
3218 if ((channel & GreenChannel) != 0)
3219 {
3220 (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
3221 &alpha,exception);
3222 q->green=RoundToQuantum((MagickRealType) QuantumRange*alpha);
3223 }
3224 if ((channel & BlueChannel) != 0)
3225 {
3226 (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
3227 &alpha,exception);
3228 q->blue=RoundToQuantum((MagickRealType) QuantumRange*alpha);
3229 }
3230 if ((channel & OpacityChannel) != 0)
3231 {
3232 (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
3233 &alpha,exception);
3234 if (image->matte == MagickFalse)
3235 q->opacity=RoundToQuantum((MagickRealType) QuantumRange*alpha);
3236 else
3237 q->opacity=RoundToQuantum((MagickRealType) (QuantumRange-
3238 QuantumRange*alpha));
3239 }
3240 if (((channel & IndexChannel) != 0) &&
3241 (fx_image->colorspace == CMYKColorspace))
3242 {
3243 (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
3244 &alpha,exception);
3245 fx_indexes[x]=(IndexPacket) RoundToQuantum((MagickRealType)
3246 QuantumRange*alpha);
3247 }
3248 q++;
3249 }
3250 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3251 status=MagickFalse;
3252 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3253 {
3254 MagickBooleanType
3255 proceed;
3256
cristyb5d5f722009-11-04 03:03:49 +00003257#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003258 #pragma omp critical (MagickCore_FxImageChannel)
3259#endif
3260 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3261 if (proceed == MagickFalse)
3262 status=MagickFalse;
3263 }
3264 }
3265 fx_image->matte=fx_info[0]->matte;
3266 fx_view=DestroyCacheView(fx_view);
3267 fx_info=DestroyFxThreadSet(fx_info);
3268 if (status == MagickFalse)
3269 fx_image=DestroyImage(fx_image);
3270 return(fx_image);
3271}
3272
3273/*
3274%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3275% %
3276% %
3277% %
3278% I m p l o d e I m a g e %
3279% %
3280% %
3281% %
3282%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3283%
3284% ImplodeImage() creates a new image that is a copy of an existing
3285% one with the image pixels "implode" by the specified percentage. It
3286% allocates the memory necessary for the new Image structure and returns a
3287% pointer to the new image.
3288%
3289% The format of the ImplodeImage method is:
3290%
3291% Image *ImplodeImage(const Image *image,const double amount,
3292% ExceptionInfo *exception)
3293%
3294% A description of each parameter follows:
3295%
3296% o implode_image: Method ImplodeImage returns a pointer to the image
3297% after it is implode. A null image is returned if there is a memory
3298% shortage.
3299%
3300% o image: the image.
3301%
3302% o amount: Define the extent of the implosion.
3303%
3304% o exception: return any errors or warnings in this structure.
3305%
3306*/
3307MagickExport Image *ImplodeImage(const Image *image,const double amount,
3308 ExceptionInfo *exception)
3309{
3310#define ImplodeImageTag "Implode/Image"
3311
cristyfa112112010-01-04 17:48:07 +00003312 CacheView
3313 *image_view,
3314 *implode_view;
3315
cristy3ed852e2009-09-05 21:47:34 +00003316 Image
3317 *implode_image;
3318
3319 long
3320 progress,
3321 y;
3322
3323 MagickBooleanType
3324 status;
3325
3326 MagickPixelPacket
3327 zero;
3328
3329 MagickRealType
3330 radius;
3331
3332 PointInfo
3333 center,
3334 scale;
3335
3336 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00003337 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00003338
3339 /*
3340 Initialize implode image attributes.
3341 */
3342 assert(image != (Image *) NULL);
3343 assert(image->signature == MagickSignature);
3344 if (image->debug != MagickFalse)
3345 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3346 assert(exception != (ExceptionInfo *) NULL);
3347 assert(exception->signature == MagickSignature);
3348 implode_image=CloneImage(image,0,0,MagickTrue,exception);
3349 if (implode_image == (Image *) NULL)
3350 return((Image *) NULL);
3351 if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
3352 {
3353 InheritException(exception,&implode_image->exception);
3354 implode_image=DestroyImage(implode_image);
3355 return((Image *) NULL);
3356 }
3357 if (implode_image->background_color.opacity != OpaqueOpacity)
3358 implode_image->matte=MagickTrue;
3359 /*
3360 Compute scaling factor.
3361 */
3362 scale.x=1.0;
3363 scale.y=1.0;
3364 center.x=0.5*image->columns;
3365 center.y=0.5*image->rows;
3366 radius=center.x;
3367 if (image->columns > image->rows)
3368 scale.y=(double) image->columns/(double) image->rows;
3369 else
3370 if (image->columns < image->rows)
3371 {
3372 scale.x=(double) image->rows/(double) image->columns;
3373 radius=center.y;
3374 }
3375 /*
3376 Implode image.
3377 */
3378 status=MagickTrue;
3379 progress=0;
3380 GetMagickPixelPacket(implode_image,&zero);
3381 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
3382 image_view=AcquireCacheView(image);
3383 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003384#if defined(MAGICKCORE_OPENMP_SUPPORT)
3385 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003386#endif
3387 for (y=0; y < (long) image->rows; y++)
3388 {
3389 MagickPixelPacket
3390 pixel;
3391
3392 MagickRealType
3393 distance;
3394
3395 PointInfo
3396 delta;
3397
3398 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003399 *restrict implode_indexes;
cristy3ed852e2009-09-05 21:47:34 +00003400
3401 register long
3402 id,
3403 x;
3404
3405 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003406 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003407
3408 if (status == MagickFalse)
3409 continue;
3410 q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3411 exception);
3412 if (q == (PixelPacket *) NULL)
3413 {
3414 status=MagickFalse;
3415 continue;
3416 }
3417 implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
3418 delta.y=scale.y*(double) (y-center.y);
3419 pixel=zero;
3420 id=GetOpenMPThreadId();
3421 for (x=0; x < (long) image->columns; x++)
3422 {
3423 /*
3424 Determine if the pixel is within an ellipse.
3425 */
3426 delta.x=scale.x*(double) (x-center.x);
3427 distance=delta.x*delta.x+delta.y*delta.y;
3428 if (distance < (radius*radius))
3429 {
3430 double
3431 factor;
3432
3433 /*
3434 Implode the pixel.
3435 */
3436 factor=1.0;
3437 if (distance > 0.0)
3438 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3439 radius/2)),-amount);
3440 (void) ResamplePixelColor(resample_filter[id],(double)
3441 (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3442 scale.y+center.y),&pixel);
3443 SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
3444 }
3445 q++;
3446 }
3447 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3448 status=MagickFalse;
3449 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3450 {
3451 MagickBooleanType
3452 proceed;
3453
cristyb5d5f722009-11-04 03:03:49 +00003454#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003455 #pragma omp critical (MagickCore_ImplodeImage)
3456#endif
3457 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3458 if (proceed == MagickFalse)
3459 status=MagickFalse;
3460 }
3461 }
3462 implode_view=DestroyCacheView(implode_view);
3463 image_view=DestroyCacheView(image_view);
3464 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
3465 if (status == MagickFalse)
3466 implode_image=DestroyImage(implode_image);
3467 return(implode_image);
3468}
3469
3470/*
3471%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3472% %
3473% %
3474% %
3475% M o r p h I m a g e s %
3476% %
3477% %
3478% %
3479%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3480%
3481% The MorphImages() method requires a minimum of two images. The first
3482% image is transformed into the second by a number of intervening images
3483% as specified by frames.
3484%
3485% The format of the MorphImage method is:
3486%
3487% Image *MorphImages(const Image *image,const unsigned long number_frames,
3488% ExceptionInfo *exception)
3489%
3490% A description of each parameter follows:
3491%
3492% o image: the image.
3493%
3494% o number_frames: Define the number of in-between image to generate.
3495% The more in-between frames, the smoother the morph.
3496%
3497% o exception: return any errors or warnings in this structure.
3498%
3499*/
3500MagickExport Image *MorphImages(const Image *image,
3501 const unsigned long number_frames,ExceptionInfo *exception)
3502{
3503#define MorphImageTag "Morph/Image"
3504
3505 Image
3506 *morph_image,
3507 *morph_images;
3508
3509 long
3510 y;
3511
3512 MagickOffsetType
3513 scene;
3514
3515 MagickRealType
3516 alpha,
3517 beta;
3518
3519 register const Image
3520 *next;
3521
3522 register long
3523 i;
3524
3525 MagickBooleanType
3526 status;
3527
3528 /*
3529 Clone first frame in sequence.
3530 */
3531 assert(image != (Image *) NULL);
3532 assert(image->signature == MagickSignature);
3533 if (image->debug != MagickFalse)
3534 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3535 assert(exception != (ExceptionInfo *) NULL);
3536 assert(exception->signature == MagickSignature);
3537 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3538 if (morph_images == (Image *) NULL)
3539 return((Image *) NULL);
3540 if (GetNextImageInList(image) == (Image *) NULL)
3541 {
3542 /*
3543 Morph single image.
3544 */
3545 for (i=1; i < (long) number_frames; i++)
3546 {
3547 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3548 if (morph_image == (Image *) NULL)
3549 {
3550 morph_images=DestroyImageList(morph_images);
3551 return((Image *) NULL);
3552 }
3553 AppendImageToList(&morph_images,morph_image);
3554 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
3555 (QuantumTick(i,number_frames) != MagickFalse))
3556 {
3557 status=image->progress_monitor(MorphImageTag,i,number_frames,
3558 image->client_data);
3559 if (status == MagickFalse)
3560 break;
3561 }
3562 }
3563 return(GetFirstImageInList(morph_images));
3564 }
3565 /*
3566 Morph image sequence.
3567 */
3568 status=MagickTrue;
3569 scene=0;
3570 next=image;
3571 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3572 {
3573 for (i=0; i < (long) number_frames; i++)
3574 {
3575 CacheView
3576 *image_view,
3577 *morph_view;
3578
3579 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3580 alpha=1.0-beta;
3581 morph_image=ZoomImage(next,(unsigned long) (alpha*next->columns+beta*
3582 GetNextImageInList(next)->columns+0.5),(unsigned long) (alpha*
3583 next->rows+beta*GetNextImageInList(next)->rows+0.5),exception);
3584 if (morph_image == (Image *) NULL)
3585 {
3586 morph_images=DestroyImageList(morph_images);
3587 return((Image *) NULL);
3588 }
3589 if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
3590 {
3591 InheritException(exception,&morph_image->exception);
3592 morph_image=DestroyImage(morph_image);
3593 return((Image *) NULL);
3594 }
3595 AppendImageToList(&morph_images,morph_image);
3596 morph_images=GetLastImageInList(morph_images);
3597 morph_image=ZoomImage(GetNextImageInList(next),morph_images->columns,
3598 morph_images->rows,exception);
3599 if (morph_image == (Image *) NULL)
3600 {
3601 morph_images=DestroyImageList(morph_images);
3602 return((Image *) NULL);
3603 }
3604 image_view=AcquireCacheView(morph_image);
3605 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003606#if defined(MAGICKCORE_OPENMP_SUPPORT)
3607 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003608#endif
3609 for (y=0; y < (long) morph_images->rows; y++)
3610 {
3611 MagickBooleanType
3612 sync;
3613
3614 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003615 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003616
3617 register long
3618 x;
3619
3620 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003621 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003622
3623 if (status == MagickFalse)
3624 continue;
3625 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3626 exception);
3627 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3628 exception);
3629 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3630 {
3631 status=MagickFalse;
3632 continue;
3633 }
3634 for (x=0; x < (long) morph_images->columns; x++)
3635 {
cristyc4c8d132010-01-07 01:58:38 +00003636 q->red=RoundToQuantum(alpha*q->red+beta*GetRedSample(p));
3637 q->green=RoundToQuantum(alpha*q->green+beta*GetGreenSample(p));
3638 q->blue=RoundToQuantum(alpha*q->blue+beta*GetBlueSample(p));
3639 q->opacity=RoundToQuantum(alpha*q->opacity+beta*GetOpacitySample(p));
cristy3ed852e2009-09-05 21:47:34 +00003640 p++;
3641 q++;
3642 }
3643 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3644 if (sync == MagickFalse)
3645 status=MagickFalse;
3646 }
3647 morph_view=DestroyCacheView(morph_view);
3648 image_view=DestroyCacheView(image_view);
3649 morph_image=DestroyImage(morph_image);
3650 }
3651 if (i < (long) number_frames)
3652 break;
3653 /*
3654 Clone last frame in sequence.
3655 */
3656 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3657 if (morph_image == (Image *) NULL)
3658 {
3659 morph_images=DestroyImageList(morph_images);
3660 return((Image *) NULL);
3661 }
3662 AppendImageToList(&morph_images,morph_image);
3663 morph_images=GetLastImageInList(morph_images);
3664 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3665 {
3666 MagickBooleanType
3667 proceed;
3668
cristyb5d5f722009-11-04 03:03:49 +00003669#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003670 #pragma omp critical (MagickCore_MorphImages)
3671#endif
3672 proceed=SetImageProgress(image,MorphImageTag,scene,
3673 GetImageListLength(image));
3674 if (proceed == MagickFalse)
3675 status=MagickFalse;
3676 }
3677 scene++;
3678 }
3679 if (GetNextImageInList(next) != (Image *) NULL)
3680 {
3681 morph_images=DestroyImageList(morph_images);
3682 return((Image *) NULL);
3683 }
3684 return(GetFirstImageInList(morph_images));
3685}
3686
3687/*
3688%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3689% %
3690% %
3691% %
3692% P l a s m a I m a g e %
3693% %
3694% %
3695% %
3696%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3697%
3698% PlasmaImage() initializes an image with plasma fractal values. The image
3699% must be initialized with a base color and the random number generator
3700% seeded before this method is called.
3701%
3702% The format of the PlasmaImage method is:
3703%
3704% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
3705% unsigned long attenuate,unsigned long depth)
3706%
3707% A description of each parameter follows:
3708%
3709% o image: the image.
3710%
3711% o segment: Define the region to apply plasma fractals values.
3712%
3713% o attenuate: Define the plasmattenuation factor.
3714%
3715% o depth: Limit the plasma recursion depth.
3716%
3717*/
3718
3719static inline Quantum PlasmaPixel(RandomInfo *random_info,
3720 const MagickRealType pixel,const MagickRealType noise)
3721{
3722 Quantum
3723 plasma;
3724
3725 plasma=RoundToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
3726 noise/2.0);
3727 return(plasma);
3728}
3729
3730MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
3731 RandomInfo *random_info,const SegmentInfo *segment,unsigned long attenuate,
3732 unsigned long depth)
3733{
3734 ExceptionInfo
3735 *exception;
3736
3737 long
3738 x,
3739 x_mid,
3740 y,
3741 y_mid;
3742
3743 MagickRealType
3744 plasma;
3745
3746 PixelPacket
3747 u,
3748 v;
3749
3750 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3751 return(MagickTrue);
3752 if (depth != 0)
3753 {
3754 SegmentInfo
3755 local_info;
3756
3757 /*
3758 Divide the area into quadrants and recurse.
3759 */
3760 depth--;
3761 attenuate++;
3762 x_mid=(long) (segment->x1+segment->x2+0.5)/2;
3763 y_mid=(long) (segment->y1+segment->y2+0.5)/2;
3764 local_info=(*segment);
3765 local_info.x2=(double) x_mid;
3766 local_info.y2=(double) y_mid;
3767 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3768 local_info=(*segment);
3769 local_info.y1=(double) y_mid;
3770 local_info.x2=(double) x_mid;
3771 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3772 local_info=(*segment);
3773 local_info.x1=(double) x_mid;
3774 local_info.y2=(double) y_mid;
3775 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3776 local_info=(*segment);
3777 local_info.x1=(double) x_mid;
3778 local_info.y1=(double) y_mid;
3779 return(PlasmaImageProxy(image,random_info,&local_info,attenuate,depth));
3780 }
3781 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
3782 return(MagickFalse);
3783 x_mid=(long) (segment->x1+segment->x2+0.5)/2;
3784 y_mid=(long) (segment->y1+segment->y2+0.5)/2;
3785 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3786 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3787 return(MagickFalse);
3788 /*
3789 Average pixels and apply plasma.
3790 */
3791 exception=(&image->exception);
3792 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3793 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3794 {
3795 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003796 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003797
3798 /*
3799 Left pixel.
3800 */
3801 x=(long) (segment->x1+0.5);
3802 (void) GetOneVirtualPixel(image,x,(long) (segment->y1+0.5),&u,exception);
3803 (void) GetOneVirtualPixel(image,x,(long) (segment->y2+0.5),&v,exception);
3804 q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
3805 if (q == (PixelPacket *) NULL)
3806 return(MagickTrue);
3807 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3808 plasma);
3809 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3810 plasma);
3811 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3812 plasma);
3813 (void) SyncAuthenticPixels(image,exception);
3814 if (segment->x1 != segment->x2)
3815 {
3816 /*
3817 Right pixel.
3818 */
3819 x=(long) (segment->x2+0.5);
3820 (void) GetOneVirtualPixel(image,x,(long) (segment->y1+0.5),&u,
3821 exception);
3822 (void) GetOneVirtualPixel(image,x,(long) (segment->y2+0.5),&v,
3823 exception);
3824 q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
3825 if (q == (PixelPacket *) NULL)
3826 return(MagickTrue);
3827 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3828 plasma);
3829 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3830 2.0,plasma);
3831 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3832 plasma);
3833 (void) SyncAuthenticPixels(image,exception);
3834 }
3835 }
3836 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3837 {
3838 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3839 {
3840 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003841 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003842
3843 /*
3844 Bottom pixel.
3845 */
3846 y=(long) (segment->y2+0.5);
3847 (void) GetOneVirtualPixel(image,(long) (segment->x1+0.5),y,&u,
3848 exception);
3849 (void) GetOneVirtualPixel(image,(long) (segment->x2+0.5),y,&v,
3850 exception);
3851 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3852 if (q == (PixelPacket *) NULL)
3853 return(MagickTrue);
3854 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3855 plasma);
3856 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3857 2.0,plasma);
3858 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3859 plasma);
3860 (void) SyncAuthenticPixels(image,exception);
3861 }
3862 if (segment->y1 != segment->y2)
3863 {
3864 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003865 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003866
3867 /*
3868 Top pixel.
3869 */
3870 y=(long) (segment->y1+0.5);
3871 (void) GetOneVirtualPixel(image,(long) (segment->x1+0.5),y,&u,
3872 exception);
3873 (void) GetOneVirtualPixel(image,(long) (segment->x2+0.5),y,&v,
3874 exception);
3875 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3876 if (q == (PixelPacket *) NULL)
3877 return(MagickTrue);
3878 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3879 plasma);
3880 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3881 2.0,plasma);
3882 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3883 plasma);
3884 (void) SyncAuthenticPixels(image,exception);
3885 }
3886 }
3887 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3888 {
3889 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003890 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003891
3892 /*
3893 Middle pixel.
3894 */
3895 x=(long) (segment->x1+0.5);
3896 y=(long) (segment->y1+0.5);
3897 (void) GetOneVirtualPixel(image,x,y,&u,exception);
3898 x=(long) (segment->x2+0.5);
3899 y=(long) (segment->y2+0.5);
3900 (void) GetOneVirtualPixel(image,x,y,&v,exception);
3901 q=QueueAuthenticPixels(image,x_mid,y_mid,1,1,exception);
3902 if (q == (PixelPacket *) NULL)
3903 return(MagickTrue);
3904 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3905 plasma);
3906 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3907 plasma);
3908 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3909 plasma);
3910 (void) SyncAuthenticPixels(image,exception);
3911 }
3912 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3913 return(MagickTrue);
3914 return(MagickFalse);
3915}
3916
3917MagickExport MagickBooleanType PlasmaImage(Image *image,
3918 const SegmentInfo *segment,unsigned long attenuate,unsigned long depth)
3919{
3920 MagickBooleanType
3921 status;
3922
3923 RandomInfo
3924 *random_info;
3925
3926 if (image->debug != MagickFalse)
3927 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3928 assert(image != (Image *) NULL);
3929 assert(image->signature == MagickSignature);
3930 if (image->debug != MagickFalse)
3931 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3932 random_info=AcquireRandomInfo();
3933 status=PlasmaImageProxy(image,random_info,segment,attenuate,depth);
3934 random_info=DestroyRandomInfo(random_info);
3935 return(status);
3936}
3937
3938/*
3939%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3940% %
3941% %
3942% %
3943% P o l a r o i d I m a g e %
3944% %
3945% %
3946% %
3947%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3948%
3949% PolaroidImage() simulates a Polaroid picture.
3950%
3951% The format of the AnnotateImage method is:
3952%
3953% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3954% const double angle,ExceptionInfo exception)
3955%
3956% A description of each parameter follows:
3957%
3958% o image: the image.
3959%
3960% o draw_info: the draw info.
3961%
3962% o angle: Apply the effect along this angle.
3963%
3964% o exception: return any errors or warnings in this structure.
3965%
3966*/
3967MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3968 const double angle,ExceptionInfo *exception)
3969{
3970 const char
3971 *value;
3972
3973 long
3974 quantum;
3975
3976 Image
3977 *bend_image,
3978 *caption_image,
3979 *flop_image,
3980 *picture_image,
3981 *polaroid_image,
3982 *rotate_image,
3983 *trim_image;
3984
3985 unsigned long
3986 height;
3987
3988 /*
3989 Simulate a Polaroid picture.
3990 */
3991 assert(image != (Image *) NULL);
3992 assert(image->signature == MagickSignature);
3993 if (image->debug != MagickFalse)
3994 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3995 assert(exception != (ExceptionInfo *) NULL);
3996 assert(exception->signature == MagickSignature);
3997 quantum=(long) MagickMax(MagickMax((double) image->columns,(double)
3998 image->rows)/25.0,10.0);
3999 height=image->rows+2*quantum;
4000 caption_image=(Image *) NULL;
4001 value=GetImageProperty(image,"Caption");
4002 if (value != (const char *) NULL)
4003 {
4004 char
4005 *caption,
4006 geometry[MaxTextExtent];
4007
4008 DrawInfo
4009 *annotate_info;
4010
4011 long
4012 count;
4013
4014 MagickBooleanType
4015 status;
4016
4017 TypeMetric
4018 metrics;
4019
4020 /*
4021 Generate caption image.
4022 */
4023 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
4024 if (caption_image == (Image *) NULL)
4025 return((Image *) NULL);
4026 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
4027 caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
4028 value);
4029 (void) CloneString(&annotate_info->text,caption);
4030 count=FormatMagickCaption(caption_image,annotate_info,&metrics,&caption);
4031 status=SetImageExtent(caption_image,image->columns,(unsigned long)
4032 ((count+1)*(metrics.ascent-metrics.descent)+0.5));
4033 if (status == MagickFalse)
4034 caption_image=DestroyImage(caption_image);
4035 else
4036 {
4037 caption_image->background_color=image->border_color;
4038 (void) SetImageBackgroundColor(caption_image);
4039 (void) CloneString(&annotate_info->text,caption);
cristy8cd5b312010-01-07 01:10:24 +00004040 (void) FormatMagickString(geometry,MaxTextExtent,"+0+%.15g",
cristy3ed852e2009-09-05 21:47:34 +00004041 metrics.ascent);
4042 if (annotate_info->gravity == UndefinedGravity)
4043 (void) CloneString(&annotate_info->geometry,AcquireString(
4044 geometry));
4045 (void) AnnotateImage(caption_image,annotate_info);
4046 height+=caption_image->rows;
4047 }
4048 annotate_info=DestroyDrawInfo(annotate_info);
4049 caption=DestroyString(caption);
4050 }
4051 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4052 exception);
4053 if (picture_image == (Image *) NULL)
4054 {
4055 if (caption_image != (Image *) NULL)
4056 caption_image=DestroyImage(caption_image);
4057 return((Image *) NULL);
4058 }
4059 picture_image->background_color=image->border_color;
4060 (void) SetImageBackgroundColor(picture_image);
4061 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
4062 if (caption_image != (Image *) NULL)
4063 {
4064 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
4065 quantum,(long) (image->rows+3*quantum/2));
4066 caption_image=DestroyImage(caption_image);
4067 }
4068 (void) QueryColorDatabase("none",&picture_image->background_color,exception);
4069 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
4070 rotate_image=RotateImage(picture_image,90.0,exception);
4071 picture_image=DestroyImage(picture_image);
4072 if (rotate_image == (Image *) NULL)
4073 return((Image *) NULL);
4074 picture_image=rotate_image;
4075 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
4076 picture_image->columns,exception);
4077 picture_image=DestroyImage(picture_image);
4078 if (bend_image == (Image *) NULL)
4079 return((Image *) NULL);
4080 InheritException(&bend_image->exception,exception);
4081 picture_image=bend_image;
4082 rotate_image=RotateImage(picture_image,-90.0,exception);
4083 picture_image=DestroyImage(picture_image);
4084 if (rotate_image == (Image *) NULL)
4085 return((Image *) NULL);
4086 picture_image=rotate_image;
4087 picture_image->background_color=image->background_color;
4088 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
4089 exception);
4090 if (polaroid_image == (Image *) NULL)
4091 {
4092 picture_image=DestroyImage(picture_image);
4093 return(picture_image);
4094 }
4095 flop_image=FlopImage(polaroid_image,exception);
4096 polaroid_image=DestroyImage(polaroid_image);
4097 if (flop_image == (Image *) NULL)
4098 {
4099 picture_image=DestroyImage(picture_image);
4100 return(picture_image);
4101 }
4102 polaroid_image=flop_image;
4103 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
4104 (long) (-0.01*picture_image->columns/2.0),0L);
4105 picture_image=DestroyImage(picture_image);
4106 (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
4107 rotate_image=RotateImage(polaroid_image,angle,exception);
4108 polaroid_image=DestroyImage(polaroid_image);
4109 if (rotate_image == (Image *) NULL)
4110 return((Image *) NULL);
4111 polaroid_image=rotate_image;
4112 trim_image=TrimImage(polaroid_image,exception);
4113 polaroid_image=DestroyImage(polaroid_image);
4114 if (trim_image == (Image *) NULL)
4115 return((Image *) NULL);
4116 polaroid_image=trim_image;
4117 return(polaroid_image);
4118}
4119
4120/*
4121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4122% %
4123% %
4124% %
4125% R e c o l o r I m a g e %
4126% %
4127% %
4128% %
4129%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4130%
4131% RecolorImage() translate, scale, shear, or rotate image colors. Although
4132% you can use variable sized matrices, typically you use a 5 x 5 for an RGBA
4133% image and a 6x6 for CMYKA. Populate the last row with normalized values to
4134% translate.
4135%
4136% The format of the RecolorImage method is:
4137%
4138% Image *RecolorImage(const Image *image,const unsigned long order,
4139% const double *color_matrix,ExceptionInfo *exception)
4140%
4141% A description of each parameter follows:
4142%
4143% o image: the image.
4144%
4145% o order: the number of columns and rows in the recolor matrix.
4146%
4147% o color_matrix: An array of double representing the recolor matrix.
4148%
4149% o exception: return any errors or warnings in this structure.
4150%
4151*/
4152MagickExport Image *RecolorImage(const Image *image,const unsigned long order,
4153 const double *color_matrix,ExceptionInfo *exception)
4154{
4155#define RecolorImageTag "Recolor/Image"
4156
cristy42500932009-11-07 18:44:47 +00004157 CacheView
4158 *image_view,
4159 *recolor_view;
4160
cristy3ed852e2009-09-05 21:47:34 +00004161 Image
4162 *recolor_image;
4163
4164 long
4165 progress,
4166 y;
4167
4168 MagickBooleanType
4169 status;
4170
cristy3ed852e2009-09-05 21:47:34 +00004171 /*
4172 Initialize image attributes.
4173 */
4174 assert(image != (Image *) NULL);
4175 assert(image->signature == MagickSignature);
4176 if (image->debug != MagickFalse)
4177 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4178 assert(exception != (ExceptionInfo *) NULL);
4179 assert(exception->signature == MagickSignature);
cristy42500932009-11-07 18:44:47 +00004180 recolor_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004181 if (recolor_image == (Image *) NULL)
4182 return((Image *) NULL);
4183 if (SetImageStorageClass(recolor_image,DirectClass) == MagickFalse)
4184 {
4185 InheritException(exception,&recolor_image->exception);
4186 recolor_image=DestroyImage(recolor_image);
4187 return((Image *) NULL);
4188 }
4189 if (image->debug != MagickFalse)
4190 {
4191 char
4192 format[MaxTextExtent],
4193 *message;
4194
4195 long
4196 u,
4197 v;
4198
cristy1c01ffd2009-11-07 18:32:17 +00004199 register const double
4200 *k;
4201
cristy3ed852e2009-09-05 21:47:34 +00004202 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4203 " Recolor image with %ldx%ld color matrix:",order,order);
4204 message=AcquireString("");
4205 k=color_matrix;
4206 for (v=0; v < (long) order; v++)
4207 {
4208 *message='\0';
4209 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
4210 (void) ConcatenateString(&message,format);
4211 for (u=0; u < (long) order; u++)
4212 {
4213 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4214 (void) ConcatenateString(&message,format);
4215 }
4216 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4217 }
4218 message=DestroyString(message);
4219 }
4220 /*
4221 Recolor image.
4222 */
4223 status=MagickTrue;
4224 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00004225 image_view=AcquireCacheView(image);
4226 recolor_view=AcquireCacheView(recolor_image);
cristyb5d5f722009-11-04 03:03:49 +00004227#if defined(MAGICKCORE_OPENMP_SUPPORT)
4228 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004229#endif
4230 for (y=0; y < (long) image->rows; y++)
4231 {
cristy1c01ffd2009-11-07 18:32:17 +00004232 register const double
cristyc47d1f82009-11-26 01:44:43 +00004233 *restrict k;
cristy1c01ffd2009-11-07 18:32:17 +00004234
cristy3ed852e2009-09-05 21:47:34 +00004235 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004236 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00004237
4238 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004239 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004240
4241 register long
4242 x;
4243
4244 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004245 *restrict recolor_indexes;
cristy3ed852e2009-09-05 21:47:34 +00004246
4247 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004248 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004249
4250 if (status == MagickFalse)
4251 continue;
4252 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy42500932009-11-07 18:44:47 +00004253 q=GetCacheViewAuthenticPixels(recolor_view,0,y,recolor_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004254 exception);
4255 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4256 {
4257 status=MagickFalse;
4258 continue;
4259 }
4260 indexes=GetCacheViewAuthenticIndexQueue(image_view);
4261 recolor_indexes=GetCacheViewAuthenticIndexQueue(recolor_view);
cristy1c01ffd2009-11-07 18:32:17 +00004262 k=color_matrix;
cristyaec08b42009-11-07 20:09:08 +00004263 switch (order)
cristy3ed852e2009-09-05 21:47:34 +00004264 {
cristyaec08b42009-11-07 20:09:08 +00004265 case 0:
4266 break;
4267 case 1:
cristy3ed852e2009-09-05 21:47:34 +00004268 {
cristyaec08b42009-11-07 20:09:08 +00004269 for (x=0; x < (long) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004270 {
cristyc4c8d132010-01-07 01:58:38 +00004271 q->red=RoundToQuantum(k[0]*GetRedSample(p));
cristyaec08b42009-11-07 20:09:08 +00004272 p++;
4273 q++;
cristy3ed852e2009-09-05 21:47:34 +00004274 }
cristyaec08b42009-11-07 20:09:08 +00004275 break;
cristy3ed852e2009-09-05 21:47:34 +00004276 }
cristyaec08b42009-11-07 20:09:08 +00004277 case 2:
4278 {
4279 for (x=0; x < (long) image->columns; x++)
4280 {
cristyc4c8d132010-01-07 01:58:38 +00004281 q->red=RoundToQuantum(k[0]*p->red+k[1]*GetGreenSample(p));
4282 q->green=RoundToQuantum(k[2]*p->red+k[3]*GetGreenSample(p));
cristyaec08b42009-11-07 20:09:08 +00004283 p++;
4284 q++;
4285 }
4286 break;
4287 }
4288 case 3:
4289 {
4290 for (x=0; x < (long) image->columns; x++)
4291 {
cristyc4c8d132010-01-07 01:58:38 +00004292 q->red=RoundToQuantum(k[0]*p->red+k[1]*p->green+k[2]*GetBlueSample(p));
4293 q->green=RoundToQuantum(k[3]*p->red+k[4]*p->green+k[5]*GetBlueSample(p));
4294 q->blue=RoundToQuantum(k[6]*p->red+k[7]*p->green+k[8]*GetBlueSample(p));
cristyaec08b42009-11-07 20:09:08 +00004295 p++;
4296 q++;
4297 }
4298 break;
4299 }
4300 case 4:
4301 {
4302 for (x=0; x < (long) image->columns; x++)
4303 {
4304 q->red=RoundToQuantum(k[0]*p->red+k[1]*p->green+k[2]*p->blue+
4305 k[12]*QuantumRange);
4306 q->green=RoundToQuantum(k[4]*p->red+k[5]*p->green+k[6]*p->blue+
4307 k[13]*QuantumRange);
4308 q->blue=RoundToQuantum(k[8]*p->red+k[9]*p->green+k[10]*p->blue+
4309 k[14]*QuantumRange);
4310 p++;
4311 q++;
4312 }
4313 break;
4314 }
4315 case 5:
4316 {
4317 for (x=0; x < (long) image->columns; x++)
4318 {
4319 q->red=RoundToQuantum(k[0]*p->red+k[1]*p->green+k[2]*p->blue+
cristyc4c8d132010-01-07 01:58:38 +00004320 k[3]*(QuantumRange-GetOpacitySample(p))+k[20]*QuantumRange);
cristyaec08b42009-11-07 20:09:08 +00004321 q->green=RoundToQuantum(k[5]*p->red+k[6]*p->green+k[7]*p->blue+
cristyc4c8d132010-01-07 01:58:38 +00004322 k[8]*(QuantumRange-GetOpacitySample(p))+k[21]*QuantumRange);
cristyaec08b42009-11-07 20:09:08 +00004323 q->blue=RoundToQuantum(k[10]*p->red+k[11]*p->green+k[12]*p->blue+
cristyc4c8d132010-01-07 01:58:38 +00004324 k[13]*(QuantumRange-GetOpacitySample(p))+k[22]*QuantumRange);
cristyaec08b42009-11-07 20:09:08 +00004325 if (image->matte != MagickFalse)
4326 q->opacity=RoundToQuantum((MagickRealType) QuantumRange-(k[15]*
4327 p->red+k[16]*p->green+k[17]*p->blue+k[18]*(QuantumRange-
cristyc4c8d132010-01-07 01:58:38 +00004328 GetOpacitySample(p))+k[23]*QuantumRange));
cristyaec08b42009-11-07 20:09:08 +00004329 p++;
4330 q++;
4331 }
4332 break;
4333 }
4334 default:
4335 {
4336 for (x=0; x < (long) image->columns; x++)
4337 {
4338 q->red=RoundToQuantum(k[0]*p->red+k[1]*p->green+k[2]*p->blue+
cristyc4c8d132010-01-07 01:58:38 +00004339 k[3]*indexes[x]+k[4]*((Quantum) QuantumRange-GetOpacitySample(p))+
cristyaec08b42009-11-07 20:09:08 +00004340 k[30]*QuantumRange);
4341 q->green=RoundToQuantum(k[6]*p->red+k[7]*p->green+k[8]*p->blue+
cristyc4c8d132010-01-07 01:58:38 +00004342 k[9]*indexes[x]+k[10]*((Quantum) QuantumRange-GetOpacitySample(p))+
cristyaec08b42009-11-07 20:09:08 +00004343 k[31]*QuantumRange);
4344 q->blue=RoundToQuantum(k[12]*p->red+k[13]*p->green+k[14]*p->blue+
cristyc4c8d132010-01-07 01:58:38 +00004345 k[15]*indexes[x]+k[16]*((Quantum) QuantumRange-GetOpacitySample(p))+
cristyaec08b42009-11-07 20:09:08 +00004346 k[32]*QuantumRange);
4347 if (image->matte != MagickFalse)
4348 q->opacity=RoundToQuantum((MagickRealType) QuantumRange-(k[24]*
4349 p->red+k[25]*p->green+k[26]*p->blue+k[27]*indexes[x]+
cristyc4c8d132010-01-07 01:58:38 +00004350 k[28]*(QuantumRange-GetOpacitySample(p))+k[34]*QuantumRange));
cristyaec08b42009-11-07 20:09:08 +00004351 if (image->colorspace == CMYKColorspace)
4352 recolor_indexes[x]=RoundToQuantum(k[18]*p->red+k[19]*p->green+k[20]*
4353 p->blue+k[21]*indexes[x]+k[22]*((Quantum) QuantumRange-
cristyc4c8d132010-01-07 01:58:38 +00004354 GetOpacitySample(p))+k[33]*QuantumRange);
cristyaec08b42009-11-07 20:09:08 +00004355 p++;
4356 q++;
4357 }
4358 break;
4359 }
cristy3ed852e2009-09-05 21:47:34 +00004360 }
4361 if (SyncCacheViewAuthenticPixels(recolor_view,exception) == MagickFalse)
4362 status=MagickFalse;
4363 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4364 {
4365 MagickBooleanType
4366 proceed;
4367
cristyb5d5f722009-11-04 03:03:49 +00004368#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004369 #pragma omp critical (MagickCore_RecolorImage)
4370#endif
4371 proceed=SetImageProgress(image,RecolorImageTag,progress++,image->rows);
4372 if (proceed == MagickFalse)
4373 status=MagickFalse;
4374 }
4375 }
4376 recolor_view=DestroyCacheView(recolor_view);
4377 image_view=DestroyCacheView(image_view);
4378 if (status == MagickFalse)
4379 recolor_image=DestroyImage(recolor_image);
4380 return(recolor_image);
4381}
4382
4383/*
4384%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4385% %
4386% %
4387% %
4388% S e p i a T o n e I m a g e %
4389% %
4390% %
4391% %
4392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4393%
4394% MagickSepiaToneImage() applies a special effect to the image, similar to the
4395% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4396% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4397% threshold of 80% is a good starting point for a reasonable tone.
4398%
4399% The format of the SepiaToneImage method is:
4400%
4401% Image *SepiaToneImage(const Image *image,const double threshold,
4402% ExceptionInfo *exception)
4403%
4404% A description of each parameter follows:
4405%
4406% o image: the image.
4407%
4408% o threshold: the tone threshold.
4409%
4410% o exception: return any errors or warnings in this structure.
4411%
4412*/
4413MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4414 ExceptionInfo *exception)
4415{
4416#define SepiaToneImageTag "SepiaTone/Image"
4417
cristyc4c8d132010-01-07 01:58:38 +00004418 CacheView
4419 *image_view,
4420 *sepia_view;
4421
cristy3ed852e2009-09-05 21:47:34 +00004422 Image
4423 *sepia_image;
4424
4425 long
4426 progress,
4427 y;
4428
4429 MagickBooleanType
4430 status;
4431
cristy3ed852e2009-09-05 21:47:34 +00004432 /*
4433 Initialize sepia-toned image attributes.
4434 */
4435 assert(image != (const Image *) NULL);
4436 assert(image->signature == MagickSignature);
4437 if (image->debug != MagickFalse)
4438 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4439 assert(exception != (ExceptionInfo *) NULL);
4440 assert(exception->signature == MagickSignature);
4441 sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4442 if (sepia_image == (Image *) NULL)
4443 return((Image *) NULL);
4444 if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
4445 {
4446 InheritException(exception,&sepia_image->exception);
4447 sepia_image=DestroyImage(sepia_image);
4448 return((Image *) NULL);
4449 }
4450 /*
4451 Tone each row of the image.
4452 */
4453 status=MagickTrue;
4454 progress=0;
4455 image_view=AcquireCacheView(image);
4456 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004457#if defined(MAGICKCORE_OPENMP_SUPPORT)
4458 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004459#endif
4460 for (y=0; y < (long) image->rows; y++)
4461 {
4462 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004463 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004464
4465 register long
4466 x;
4467
4468 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004469 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004470
4471 if (status == MagickFalse)
4472 continue;
4473 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4474 q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4475 exception);
4476 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4477 {
4478 status=MagickFalse;
4479 continue;
4480 }
4481 for (x=0; x < (long) image->columns; x++)
4482 {
4483 MagickRealType
4484 intensity,
4485 tone;
4486
4487 intensity=(MagickRealType) PixelIntensityToQuantum(p);
4488 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4489 (MagickRealType) QuantumRange-threshold;
4490 q->red=RoundToQuantum(tone);
4491 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4492 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
4493 q->green=RoundToQuantum(tone);
4494 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
4495 q->blue=RoundToQuantum(tone);
4496 tone=threshold/7.0;
4497 if ((MagickRealType) q->green < tone)
4498 q->green=RoundToQuantum(tone);
4499 if ((MagickRealType) q->blue < tone)
4500 q->blue=RoundToQuantum(tone);
4501 p++;
4502 q++;
4503 }
4504 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4505 status=MagickFalse;
4506 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4507 {
4508 MagickBooleanType
4509 proceed;
4510
cristyb5d5f722009-11-04 03:03:49 +00004511#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004512 #pragma omp critical (MagickCore_SepiaToneImage)
4513#endif
4514 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4515 image->rows);
4516 if (proceed == MagickFalse)
4517 status=MagickFalse;
4518 }
4519 }
4520 sepia_view=DestroyCacheView(sepia_view);
4521 image_view=DestroyCacheView(image_view);
4522 (void) NormalizeImage(sepia_image);
4523 (void) ContrastImage(sepia_image,MagickTrue);
4524 if (status == MagickFalse)
4525 sepia_image=DestroyImage(sepia_image);
4526 return(sepia_image);
4527}
4528
4529/*
4530%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4531% %
4532% %
4533% %
4534% S h a d o w I m a g e %
4535% %
4536% %
4537% %
4538%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4539%
4540% ShadowImage() simulates a shadow from the specified image and returns it.
4541%
4542% The format of the ShadowImage method is:
4543%
4544% Image *ShadowImage(const Image *image,const double opacity,
4545% const double sigma,const long x_offset,const long y_offset,
4546% ExceptionInfo *exception)
4547%
4548% A description of each parameter follows:
4549%
4550% o image: the image.
4551%
4552% o opacity: percentage transparency.
4553%
4554% o sigma: the standard deviation of the Gaussian, in pixels.
4555%
4556% o x_offset: the shadow x-offset.
4557%
4558% o y_offset: the shadow y-offset.
4559%
4560% o exception: return any errors or warnings in this structure.
4561%
4562*/
4563MagickExport Image *ShadowImage(const Image *image,const double opacity,
4564 const double sigma,const long x_offset,const long y_offset,
4565 ExceptionInfo *exception)
4566{
4567#define ShadowImageTag "Shadow/Image"
4568
cristyc4c8d132010-01-07 01:58:38 +00004569 CacheView
4570 *image_view;
4571
cristy3ed852e2009-09-05 21:47:34 +00004572 Image
4573 *border_image,
4574 *clone_image,
4575 *shadow_image;
4576
4577 long
4578 progress,
4579 y;
4580
4581 MagickBooleanType
4582 status;
4583
4584 RectangleInfo
4585 border_info;
4586
cristy3ed852e2009-09-05 21:47:34 +00004587 assert(image != (Image *) NULL);
4588 assert(image->signature == MagickSignature);
4589 if (image->debug != MagickFalse)
4590 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4591 assert(exception != (ExceptionInfo *) NULL);
4592 assert(exception->signature == MagickSignature);
4593 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4594 if (clone_image == (Image *) NULL)
4595 return((Image *) NULL);
4596 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4597 clone_image->compose=OverCompositeOp;
4598 border_info.width=(unsigned long) (2.0*sigma+0.5);
4599 border_info.height=(unsigned long) (2.0*sigma+0.5);
4600 border_info.x=0;
4601 border_info.y=0;
4602 (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4603 border_image=BorderImage(clone_image,&border_info,exception);
4604 clone_image=DestroyImage(clone_image);
4605 if (border_image == (Image *) NULL)
4606 return((Image *) NULL);
4607 if (border_image->matte == MagickFalse)
4608 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4609 /*
4610 Shadow image.
4611 */
4612 status=MagickTrue;
4613 progress=0;
4614 image_view=AcquireCacheView(border_image);
cristyb5d5f722009-11-04 03:03:49 +00004615#if defined(MAGICKCORE_OPENMP_SUPPORT)
4616 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004617#endif
4618 for (y=0; y < (long) border_image->rows; y++)
4619 {
4620 register long
4621 x;
4622
4623 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004624 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004625
4626 if (status == MagickFalse)
4627 continue;
4628 q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4629 exception);
4630 if (q == (PixelPacket *) NULL)
4631 {
4632 status=MagickFalse;
4633 continue;
4634 }
4635 for (x=0; x < (long) border_image->columns; x++)
4636 {
4637 q->red=border_image->background_color.red;
4638 q->green=border_image->background_color.green;
4639 q->blue=border_image->background_color.blue;
4640 if (border_image->matte == MagickFalse)
4641 q->opacity=border_image->background_color.opacity;
4642 else
4643 q->opacity=RoundToQuantum((MagickRealType) (QuantumRange-(QuantumRange-
4644 q->opacity)*opacity/100.0));
4645 q++;
4646 }
4647 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4648 status=MagickFalse;
4649 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4650 {
4651 MagickBooleanType
4652 proceed;
4653
cristyb5d5f722009-11-04 03:03:49 +00004654#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004655 #pragma omp critical (MagickCore_ShadowImage)
4656#endif
4657 proceed=SetImageProgress(image,ShadowImageTag,progress++,
4658 border_image->rows);
4659 if (proceed == MagickFalse)
4660 status=MagickFalse;
4661 }
4662 }
4663 image_view=DestroyCacheView(image_view);
4664 shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
4665 border_image=DestroyImage(border_image);
4666 if (shadow_image == (Image *) NULL)
4667 return((Image *) NULL);
4668 if (shadow_image->page.width == 0)
4669 shadow_image->page.width=shadow_image->columns;
4670 if (shadow_image->page.height == 0)
4671 shadow_image->page.height=shadow_image->rows;
4672 shadow_image->page.width+=x_offset-(long) border_info.width;
4673 shadow_image->page.height+=y_offset-(long) border_info.height;
4674 shadow_image->page.x+=x_offset-(long) border_info.width;
4675 shadow_image->page.y+=y_offset-(long) border_info.height;
4676 return(shadow_image);
4677}
4678
4679/*
4680%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4681% %
4682% %
4683% %
4684% S k e t c h I m a g e %
4685% %
4686% %
4687% %
4688%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4689%
4690% SketchImage() simulates a pencil sketch. We convolve the image with a
4691% Gaussian operator of the given radius and standard deviation (sigma). For
4692% reasonable results, radius should be larger than sigma. Use a radius of 0
4693% and SketchImage() selects a suitable radius for you. Angle gives the angle
4694% of the sketch.
4695%
4696% The format of the SketchImage method is:
4697%
4698% Image *SketchImage(const Image *image,const double radius,
4699% const double sigma,const double angle,ExceptionInfo *exception)
4700%
4701% A description of each parameter follows:
4702%
4703% o image: the image.
4704%
4705% o radius: the radius of the Gaussian, in pixels, not counting
4706% the center pixel.
4707%
4708% o sigma: the standard deviation of the Gaussian, in pixels.
4709%
4710% o angle: Apply the effect along this angle.
4711%
4712% o exception: return any errors or warnings in this structure.
4713%
4714*/
4715MagickExport Image *SketchImage(const Image *image,const double radius,
4716 const double sigma,const double angle,ExceptionInfo *exception)
4717{
cristyfa112112010-01-04 17:48:07 +00004718 CacheView
4719 *random_view;
4720
cristy3ed852e2009-09-05 21:47:34 +00004721 Image
4722 *blend_image,
4723 *blur_image,
4724 *dodge_image,
4725 *random_image,
4726 *sketch_image;
4727
4728 long
4729 y;
4730
4731 MagickBooleanType
4732 status;
4733
4734 MagickPixelPacket
4735 zero;
4736
4737 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004738 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004739
4740 /*
4741 Sketch image.
4742 */
4743 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4744 MagickTrue,exception);
4745 if (random_image == (Image *) NULL)
4746 return((Image *) NULL);
4747 status=MagickTrue;
4748 GetMagickPixelPacket(random_image,&zero);
cristy1b784432009-12-19 02:20:40 +00004749 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004750 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004751#if defined(MAGICKCORE_OPENMP_SUPPORT)
4752 #pragma omp parallel for schedule(dynamic,4) shared(status)
4753#endif
cristy3ed852e2009-09-05 21:47:34 +00004754 for (y=0; y < (long) random_image->rows; y++)
4755 {
4756 MagickPixelPacket
4757 pixel;
4758
4759 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004760 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00004761
4762 register long
cristy1b784432009-12-19 02:20:40 +00004763 id,
cristy3ed852e2009-09-05 21:47:34 +00004764 x;
4765
4766 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004767 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004768
cristy1b784432009-12-19 02:20:40 +00004769 if (status == MagickFalse)
4770 continue;
cristy3ed852e2009-09-05 21:47:34 +00004771 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4772 exception);
4773 if (q == (PixelPacket *) NULL)
4774 {
4775 status=MagickFalse;
4776 continue;
4777 }
4778 indexes=GetCacheViewAuthenticIndexQueue(random_view);
4779 pixel=zero;
cristy1b784432009-12-19 02:20:40 +00004780 id=GetOpenMPThreadId();
cristy3ed852e2009-09-05 21:47:34 +00004781 for (x=0; x < (long) random_image->columns; x++)
4782 {
4783 pixel.red=(MagickRealType) (QuantumRange*
cristy1b784432009-12-19 02:20:40 +00004784 GetPseudoRandomValue(random_info[id]));
cristy3ed852e2009-09-05 21:47:34 +00004785 pixel.green=pixel.red;
4786 pixel.blue=pixel.red;
4787 if (image->colorspace == CMYKColorspace)
4788 pixel.index=pixel.red;
4789 SetPixelPacket(random_image,&pixel,q,indexes+x);
4790 q++;
4791 }
4792 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4793 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004794 }
4795 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004796 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004797 if (status == MagickFalse)
4798 {
4799 random_image=DestroyImage(random_image);
4800 return(random_image);
4801 }
4802 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4803 random_image=DestroyImage(random_image);
4804 if (blur_image == (Image *) NULL)
4805 return((Image *) NULL);
4806 dodge_image=EdgeImage(blur_image,radius,exception);
4807 blur_image=DestroyImage(blur_image);
4808 if (dodge_image == (Image *) NULL)
4809 return((Image *) NULL);
4810 (void) NormalizeImage(dodge_image);
4811 (void) NegateImage(dodge_image,MagickFalse);
4812 (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4813 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4814 if (sketch_image == (Image *) NULL)
4815 {
4816 dodge_image=DestroyImage(dodge_image);
4817 return((Image *) NULL);
4818 }
4819 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4820 dodge_image=DestroyImage(dodge_image);
4821 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4822 if (blend_image == (Image *) NULL)
4823 {
4824 sketch_image=DestroyImage(sketch_image);
4825 return((Image *) NULL);
4826 }
4827 (void) SetImageArtifact(blend_image,"compose:args","20x80");
4828 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4829 blend_image=DestroyImage(blend_image);
4830 return(sketch_image);
4831}
4832
4833/*
4834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4835% %
4836% %
4837% %
4838% S o l a r i z e I m a g e %
4839% %
4840% %
4841% %
4842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4843%
4844% SolarizeImage() applies a special effect to the image, similar to the effect
4845% achieved in a photo darkroom by selectively exposing areas of photo
4846% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4847% measure of the extent of the solarization.
4848%
4849% The format of the SolarizeImage method is:
4850%
4851% MagickBooleanType SolarizeImage(Image *image,const double threshold)
4852%
4853% A description of each parameter follows:
4854%
4855% o image: the image.
4856%
4857% o threshold: Define the extent of the solarization.
4858%
4859*/
4860MagickExport MagickBooleanType SolarizeImage(Image *image,
4861 const double threshold)
4862{
4863#define SolarizeImageTag "Solarize/Image"
4864
cristyc4c8d132010-01-07 01:58:38 +00004865 CacheView
4866 *image_view;
4867
cristy3ed852e2009-09-05 21:47:34 +00004868 ExceptionInfo
4869 *exception;
4870
4871 long
4872 progress,
4873 y;
4874
4875 MagickBooleanType
4876 status;
4877
cristy3ed852e2009-09-05 21:47:34 +00004878 assert(image != (Image *) NULL);
4879 assert(image->signature == MagickSignature);
4880 if (image->debug != MagickFalse)
4881 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4882 if (image->storage_class == PseudoClass)
4883 {
4884 register long
4885 i;
4886
4887 /*
4888 Solarize colormap.
4889 */
4890 for (i=0; i < (long) image->colors; i++)
4891 {
4892 if ((MagickRealType) image->colormap[i].red > threshold)
4893 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4894 if ((MagickRealType) image->colormap[i].green > threshold)
4895 image->colormap[i].green=(Quantum) QuantumRange-
4896 image->colormap[i].green;
4897 if ((MagickRealType) image->colormap[i].blue > threshold)
4898 image->colormap[i].blue=(Quantum) QuantumRange-
4899 image->colormap[i].blue;
4900 }
4901 }
4902 /*
4903 Solarize image.
4904 */
4905 status=MagickTrue;
4906 progress=0;
4907 exception=(&image->exception);
4908 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004909#if defined(MAGICKCORE_OPENMP_SUPPORT)
4910 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004911#endif
4912 for (y=0; y < (long) image->rows; y++)
4913 {
4914 register long
4915 x;
4916
4917 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004918 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004919
4920 if (status == MagickFalse)
4921 continue;
4922 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4923 exception);
4924 if (q == (PixelPacket *) NULL)
4925 {
4926 status=MagickFalse;
4927 continue;
4928 }
4929 for (x=0; x < (long) image->columns; x++)
4930 {
4931 if ((MagickRealType) q->red > threshold)
4932 q->red=(Quantum) QuantumRange-q->red;
4933 if ((MagickRealType) q->green > threshold)
4934 q->green=(Quantum) QuantumRange-q->green;
4935 if ((MagickRealType) q->blue > threshold)
4936 q->blue=(Quantum) QuantumRange-q->blue;
4937 q++;
4938 }
4939 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4940 status=MagickFalse;
4941 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4942 {
4943 MagickBooleanType
4944 proceed;
4945
cristyb5d5f722009-11-04 03:03:49 +00004946#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004947 #pragma omp critical (MagickCore_SolarizeImage)
4948#endif
4949 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4950 if (proceed == MagickFalse)
4951 status=MagickFalse;
4952 }
4953 }
4954 image_view=DestroyCacheView(image_view);
4955 return(status);
4956}
4957
4958/*
4959%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4960% %
4961% %
4962% %
4963% S t e g a n o I m a g e %
4964% %
4965% %
4966% %
4967%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4968%
4969% SteganoImage() hides a digital watermark within the image. Recover
4970% the hidden watermark later to prove that the authenticity of an image.
4971% Offset defines the start position within the image to hide the watermark.
4972%
4973% The format of the SteganoImage method is:
4974%
4975% Image *SteganoImage(const Image *image,Image *watermark,
4976% ExceptionInfo *exception)
4977%
4978% A description of each parameter follows:
4979%
4980% o image: the image.
4981%
4982% o watermark: the watermark image.
4983%
4984% o exception: return any errors or warnings in this structure.
4985%
4986*/
4987MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4988 ExceptionInfo *exception)
4989{
4990#define GetBit(alpha,i) ((((unsigned long) (alpha) >> (unsigned long) \
4991 (i)) & 0x01) != 0)
4992#define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) ? (unsigned long) (alpha) \
4993 | (1UL << (unsigned long) (i)) : (unsigned long) (alpha) & \
4994 ~(1UL << (unsigned long) (i)))
4995#define SteganoImageTag "Stegano/Image"
4996
4997 Image
4998 *stegano_image;
4999
5000 int
5001 c;
5002
5003 long
5004 i,
5005 j,
5006 k,
5007 y;
5008
5009 MagickBooleanType
5010 status;
5011
5012 PixelPacket
5013 pixel;
5014
5015 register long
5016 x;
5017
5018 register PixelPacket
5019 *q;
5020
5021 unsigned long
5022 depth;
5023
5024 /*
5025 Initialize steganographic image attributes.
5026 */
5027 assert(image != (const Image *) NULL);
5028 assert(image->signature == MagickSignature);
5029 if (image->debug != MagickFalse)
5030 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5031 assert(watermark != (const Image *) NULL);
5032 assert(watermark->signature == MagickSignature);
5033 assert(exception != (ExceptionInfo *) NULL);
5034 assert(exception->signature == MagickSignature);
5035 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
5036 if (stegano_image == (Image *) NULL)
5037 return((Image *) NULL);
5038 if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
5039 {
5040 InheritException(exception,&stegano_image->exception);
5041 stegano_image=DestroyImage(stegano_image);
5042 return((Image *) NULL);
5043 }
5044 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
5045 /*
5046 Hide watermark in low-order bits of image.
5047 */
5048 c=0;
5049 i=0;
5050 j=0;
5051 depth=stegano_image->depth;
5052 k=image->offset;
5053 for (i=(long) depth-1; (i >= 0) && (j < (long) depth); i--)
5054 {
5055 for (y=0; (y < (long) watermark->rows) && (j < (long) depth); y++)
5056 {
5057 for (x=0; (x < (long) watermark->columns) && (j < (long) depth); x++)
5058 {
5059 (void) GetOneVirtualPixel(watermark,x,y,&pixel,exception);
5060 if ((k/(long) stegano_image->columns) >= (long) stegano_image->rows)
5061 break;
5062 q=GetAuthenticPixels(stegano_image,k % (long) stegano_image->columns,
5063 k/(long) stegano_image->columns,1,1,exception);
5064 if (q == (PixelPacket *) NULL)
5065 break;
5066 switch (c)
5067 {
5068 case 0:
5069 {
5070 SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5071 break;
5072 }
5073 case 1:
5074 {
5075 SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5076 break;
5077 }
5078 case 2:
5079 {
5080 SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5081 break;
5082 }
5083 }
5084 if (SyncAuthenticPixels(stegano_image,exception) == MagickFalse)
5085 break;
5086 c++;
5087 if (c == 3)
5088 c=0;
5089 k++;
5090 if (k == (long) (stegano_image->columns*stegano_image->columns))
5091 k=0;
5092 if (k == image->offset)
5093 j++;
5094 }
5095 }
5096 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
5097 (QuantumTick((MagickOffsetType) depth-i,depth) != MagickFalse))
5098 {
5099 status=image->progress_monitor(SteganoImageTag,(MagickOffsetType) depth-
5100 i,depth,image->client_data);
5101 if (status == MagickFalse)
5102 break;
5103 }
5104 }
5105 if (stegano_image->storage_class == PseudoClass)
5106 (void) SyncImage(stegano_image);
5107 return(stegano_image);
5108}
5109
5110/*
5111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5112% %
5113% %
5114% %
5115% S t e r e o A n a g l y p h I m a g e %
5116% %
5117% %
5118% %
5119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5120%
5121% StereoAnaglyphImage() combines two images and produces a single image that
5122% is the composite of a left and right image of a stereo pair. Special
5123% red-green stereo glasses are required to view this effect.
5124%
5125% The format of the StereoAnaglyphImage method is:
5126%
5127% Image *StereoImage(const Image *left_image,const Image *right_image,
5128% ExceptionInfo *exception)
5129% Image *StereoAnaglyphImage(const Image *left_image,
5130% const Image *right_image,const long x_offset,const long y_offset,
5131% ExceptionInfo *exception)
5132%
5133% A description of each parameter follows:
5134%
5135% o left_image: the left image.
5136%
5137% o right_image: the right image.
5138%
5139% o exception: return any errors or warnings in this structure.
5140%
5141% o x_offset: amount, in pixels, by which the left image is offset to the
5142% right of the right image.
5143%
5144% o y_offset: amount, in pixels, by which the left image is offset to the
5145% bottom of the right image.
5146%
5147%
5148*/
5149MagickExport Image *StereoImage(const Image *left_image,
5150 const Image *right_image,ExceptionInfo *exception)
5151{
5152 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
5153}
5154
5155MagickExport Image *StereoAnaglyphImage(const Image *left_image,
5156 const Image *right_image,const long x_offset,const long y_offset,
5157 ExceptionInfo *exception)
5158{
5159#define StereoImageTag "Stereo/Image"
5160
5161 const Image
5162 *image;
5163
5164 Image
5165 *stereo_image;
5166
5167 long
5168 y;
5169
5170 MagickBooleanType
5171 status;
5172
5173 assert(left_image != (const Image *) NULL);
5174 assert(left_image->signature == MagickSignature);
5175 if (left_image->debug != MagickFalse)
5176 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5177 left_image->filename);
5178 assert(right_image != (const Image *) NULL);
5179 assert(right_image->signature == MagickSignature);
5180 assert(exception != (ExceptionInfo *) NULL);
5181 assert(exception->signature == MagickSignature);
5182 assert(right_image != (const Image *) NULL);
5183 image=left_image;
5184 if ((left_image->columns != right_image->columns) ||
5185 (left_image->rows != right_image->rows))
5186 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
5187 /*
5188 Initialize stereo image attributes.
5189 */
5190 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
5191 MagickTrue,exception);
5192 if (stereo_image == (Image *) NULL)
5193 return((Image *) NULL);
5194 if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
5195 {
5196 InheritException(exception,&stereo_image->exception);
5197 stereo_image=DestroyImage(stereo_image);
5198 return((Image *) NULL);
5199 }
5200 /*
5201 Copy left image to red channel and right image to blue channel.
5202 */
5203 for (y=0; y < (long) stereo_image->rows; y++)
5204 {
5205 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005206 *restrict p,
5207 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005208
5209 register long
5210 x;
5211
5212 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005213 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00005214
5215 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
5216 exception);
5217 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
5218 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
5219 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
5220 (r == (PixelPacket *) NULL))
5221 break;
5222 for (x=0; x < (long) stereo_image->columns; x++)
5223 {
cristyc4c8d132010-01-07 01:58:38 +00005224 r->red=GetRedSample(p);
cristy3ed852e2009-09-05 21:47:34 +00005225 r->green=q->green;
5226 r->blue=q->blue;
5227 r->opacity=(Quantum) ((p->opacity+q->opacity)/2);
5228 p++;
5229 q++;
5230 r++;
5231 }
5232 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
5233 break;
5234 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
5235 (QuantumTick(y,image->rows) != MagickFalse))
5236 {
5237 status=image->progress_monitor(StereoImageTag,y,stereo_image->rows,
5238 stereo_image->client_data);
5239 if (status == MagickFalse)
5240 break;
5241 }
5242 }
5243 return(stereo_image);
5244}
5245
5246/*
5247%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5248% %
5249% %
5250% %
5251% S w i r l I m a g e %
5252% %
5253% %
5254% %
5255%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5256%
5257% SwirlImage() swirls the pixels about the center of the image, where
5258% degrees indicates the sweep of the arc through which each pixel is moved.
5259% You get a more dramatic effect as the degrees move from 1 to 360.
5260%
5261% The format of the SwirlImage method is:
5262%
5263% Image *SwirlImage(const Image *image,double degrees,
5264% ExceptionInfo *exception)
5265%
5266% A description of each parameter follows:
5267%
5268% o image: the image.
5269%
5270% o degrees: Define the tightness of the swirling effect.
5271%
5272% o exception: return any errors or warnings in this structure.
5273%
5274*/
5275MagickExport Image *SwirlImage(const Image *image,double degrees,
5276 ExceptionInfo *exception)
5277{
5278#define SwirlImageTag "Swirl/Image"
5279
cristyfa112112010-01-04 17:48:07 +00005280 CacheView
5281 *image_view,
5282 *swirl_view;
5283
cristy3ed852e2009-09-05 21:47:34 +00005284 Image
5285 *swirl_image;
5286
5287 long
5288 progress,
5289 y;
5290
5291 MagickBooleanType
5292 status;
5293
5294 MagickPixelPacket
5295 zero;
5296
5297 MagickRealType
5298 radius;
5299
5300 PointInfo
5301 center,
5302 scale;
5303
5304 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00005305 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00005306
5307 /*
5308 Initialize swirl image attributes.
5309 */
5310 assert(image != (const Image *) NULL);
5311 assert(image->signature == MagickSignature);
5312 if (image->debug != MagickFalse)
5313 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5314 assert(exception != (ExceptionInfo *) NULL);
5315 assert(exception->signature == MagickSignature);
5316 swirl_image=CloneImage(image,0,0,MagickTrue,exception);
5317 if (swirl_image == (Image *) NULL)
5318 return((Image *) NULL);
5319 if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
5320 {
5321 InheritException(exception,&swirl_image->exception);
5322 swirl_image=DestroyImage(swirl_image);
5323 return((Image *) NULL);
5324 }
5325 if (swirl_image->background_color.opacity != OpaqueOpacity)
5326 swirl_image->matte=MagickTrue;
5327 /*
5328 Compute scaling factor.
5329 */
5330 center.x=(double) image->columns/2.0;
5331 center.y=(double) image->rows/2.0;
5332 radius=MagickMax(center.x,center.y);
5333 scale.x=1.0;
5334 scale.y=1.0;
5335 if (image->columns > image->rows)
5336 scale.y=(double) image->columns/(double) image->rows;
5337 else
5338 if (image->columns < image->rows)
5339 scale.x=(double) image->rows/(double) image->columns;
5340 degrees=(double) DegreesToRadians(degrees);
5341 /*
5342 Swirl image.
5343 */
5344 status=MagickTrue;
5345 progress=0;
5346 GetMagickPixelPacket(swirl_image,&zero);
5347 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
5348 image_view=AcquireCacheView(image);
5349 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005350#if defined(MAGICKCORE_OPENMP_SUPPORT)
5351 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005352#endif
5353 for (y=0; y < (long) image->rows; y++)
5354 {
5355 MagickPixelPacket
5356 pixel;
5357
5358 MagickRealType
5359 distance;
5360
5361 PointInfo
5362 delta;
5363
5364 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00005365 *restrict swirl_indexes;
cristy3ed852e2009-09-05 21:47:34 +00005366
5367 register long
5368 id,
5369 x;
5370
5371 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005372 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005373
5374 if (status == MagickFalse)
5375 continue;
5376 q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
5377 exception);
5378 if (q == (PixelPacket *) NULL)
5379 {
5380 status=MagickFalse;
5381 continue;
5382 }
5383 swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
5384 delta.y=scale.y*(double) (y-center.y);
5385 pixel=zero;
5386 id=GetOpenMPThreadId();
5387 for (x=0; x < (long) image->columns; x++)
5388 {
5389 /*
5390 Determine if the pixel is within an ellipse.
5391 */
5392 delta.x=scale.x*(double) (x-center.x);
5393 distance=delta.x*delta.x+delta.y*delta.y;
5394 if (distance < (radius*radius))
5395 {
5396 MagickRealType
5397 cosine,
5398 factor,
5399 sine;
5400
5401 /*
5402 Swirl the pixel.
5403 */
5404 factor=1.0-sqrt((double) distance)/radius;
5405 sine=sin((double) (degrees*factor*factor));
5406 cosine=cos((double) (degrees*factor*factor));
5407 (void) ResamplePixelColor(resample_filter[id],(double) ((cosine*
5408 delta.x-sine*delta.y)/scale.x+center.x),(double) ((sine*delta.x+
5409 cosine*delta.y)/scale.y+center.y),&pixel);
5410 SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
5411 }
5412 q++;
5413 }
5414 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5415 status=MagickFalse;
5416 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5417 {
5418 MagickBooleanType
5419 proceed;
5420
cristyb5d5f722009-11-04 03:03:49 +00005421#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005422 #pragma omp critical (MagickCore_SwirlImage)
5423#endif
5424 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5425 if (proceed == MagickFalse)
5426 status=MagickFalse;
5427 }
5428 }
5429 swirl_view=DestroyCacheView(swirl_view);
5430 image_view=DestroyCacheView(image_view);
5431 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5432 if (status == MagickFalse)
5433 swirl_image=DestroyImage(swirl_image);
5434 return(swirl_image);
5435}
5436
5437/*
5438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5439% %
5440% %
5441% %
5442% T i n t I m a g e %
5443% %
5444% %
5445% %
5446%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5447%
5448% TintImage() applies a color vector to each pixel in the image. The length
5449% of the vector is 0 for black and white and at its maximum for the midtones.
5450% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5451%
5452% The format of the TintImage method is:
5453%
5454% Image *TintImage(const Image *image,const char *opacity,
5455% const PixelPacket tint,ExceptionInfo *exception)
5456%
5457% A description of each parameter follows:
5458%
5459% o image: the image.
5460%
5461% o opacity: A color value used for tinting.
5462%
5463% o tint: A color value used for tinting.
5464%
5465% o exception: return any errors or warnings in this structure.
5466%
5467*/
5468MagickExport Image *TintImage(const Image *image,const char *opacity,
5469 const PixelPacket tint,ExceptionInfo *exception)
5470{
5471#define TintImageTag "Tint/Image"
5472
cristyc4c8d132010-01-07 01:58:38 +00005473 CacheView
5474 *image_view,
5475 *tint_view;
5476
cristy3ed852e2009-09-05 21:47:34 +00005477 GeometryInfo
5478 geometry_info;
5479
5480 Image
5481 *tint_image;
5482
5483 long
5484 progress,
5485 y;
5486
5487 MagickBooleanType
5488 status;
5489
5490 MagickStatusType
5491 flags;
5492
5493 MagickPixelPacket
5494 color_vector,
5495 pixel;
5496
cristy3ed852e2009-09-05 21:47:34 +00005497 /*
5498 Allocate tint image.
5499 */
5500 assert(image != (const Image *) NULL);
5501 assert(image->signature == MagickSignature);
5502 if (image->debug != MagickFalse)
5503 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5504 assert(exception != (ExceptionInfo *) NULL);
5505 assert(exception->signature == MagickSignature);
5506 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5507 if (tint_image == (Image *) NULL)
5508 return((Image *) NULL);
5509 if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5510 {
5511 InheritException(exception,&tint_image->exception);
5512 tint_image=DestroyImage(tint_image);
5513 return((Image *) NULL);
5514 }
5515 if (opacity == (const char *) NULL)
5516 return(tint_image);
5517 /*
5518 Determine RGB values of the color.
5519 */
5520 flags=ParseGeometry(opacity,&geometry_info);
5521 pixel.red=geometry_info.rho;
5522 if ((flags & SigmaValue) != 0)
5523 pixel.green=geometry_info.sigma;
5524 else
5525 pixel.green=pixel.red;
5526 if ((flags & XiValue) != 0)
5527 pixel.blue=geometry_info.xi;
5528 else
5529 pixel.blue=pixel.red;
5530 if ((flags & PsiValue) != 0)
5531 pixel.opacity=geometry_info.psi;
5532 else
5533 pixel.opacity=(MagickRealType) OpaqueOpacity;
5534 color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5535 PixelIntensity(&tint));
5536 color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5537 PixelIntensity(&tint));
5538 color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5539 PixelIntensity(&tint));
5540 /*
5541 Tint image.
5542 */
5543 status=MagickTrue;
5544 progress=0;
5545 image_view=AcquireCacheView(image);
5546 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005547#if defined(MAGICKCORE_OPENMP_SUPPORT)
5548 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005549#endif
5550 for (y=0; y < (long) image->rows; y++)
5551 {
5552 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005553 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005554
5555 register long
5556 x;
5557
5558 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005559 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005560
5561 if (status == MagickFalse)
5562 continue;
5563 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5564 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5565 exception);
5566 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5567 {
5568 status=MagickFalse;
5569 continue;
5570 }
5571 for (x=0; x < (long) image->columns; x++)
5572 {
5573 MagickPixelPacket
5574 pixel;
5575
5576 MagickRealType
5577 weight;
5578
5579 weight=QuantumScale*p->red-0.5;
5580 pixel.red=(MagickRealType) p->red+color_vector.red*(1.0-(4.0*
5581 (weight*weight)));
5582 q->red=RoundToQuantum(pixel.red);
5583 weight=QuantumScale*p->green-0.5;
5584 pixel.green=(MagickRealType) p->green+color_vector.green*(1.0-(4.0*
5585 (weight*weight)));
5586 q->green=RoundToQuantum(pixel.green);
5587 weight=QuantumScale*p->blue-0.5;
5588 pixel.blue=(MagickRealType) p->blue+color_vector.blue*(1.0-(4.0*
5589 (weight*weight)));
5590 q->blue=RoundToQuantum(pixel.blue);
cristyc4c8d132010-01-07 01:58:38 +00005591 SetOpacitySample(q,GetOpacitySample(p));
cristy3ed852e2009-09-05 21:47:34 +00005592 p++;
5593 q++;
5594 }
5595 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5596 status=MagickFalse;
5597 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5598 {
5599 MagickBooleanType
5600 proceed;
5601
cristyb5d5f722009-11-04 03:03:49 +00005602#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005603 #pragma omp critical (MagickCore_TintImage)
5604#endif
5605 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5606 if (proceed == MagickFalse)
5607 status=MagickFalse;
5608 }
5609 }
5610 tint_view=DestroyCacheView(tint_view);
5611 image_view=DestroyCacheView(image_view);
5612 if (status == MagickFalse)
5613 tint_image=DestroyImage(tint_image);
5614 return(tint_image);
5615}
5616
5617/*
5618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5619% %
5620% %
5621% %
5622% V i g n e t t e I m a g e %
5623% %
5624% %
5625% %
5626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5627%
5628% VignetteImage() softens the edges of the image in vignette style.
5629%
5630% The format of the VignetteImage method is:
5631%
5632% Image *VignetteImage(const Image *image,const double radius,
5633% const double sigma,const long x,const long y,ExceptionInfo *exception)
5634%
5635% A description of each parameter follows:
5636%
5637% o image: the image.
5638%
5639% o radius: the radius of the pixel neighborhood.
5640%
5641% o sigma: the standard deviation of the Gaussian, in pixels.
5642%
5643% o x, y: Define the x and y ellipse offset.
5644%
5645% o exception: return any errors or warnings in this structure.
5646%
5647*/
5648MagickExport Image *VignetteImage(const Image *image,const double radius,
5649 const double sigma,const long x,const long y,ExceptionInfo *exception)
5650{
5651 char
5652 ellipse[MaxTextExtent];
5653
5654 DrawInfo
5655 *draw_info;
5656
5657 Image
5658 *canvas_image,
5659 *blur_image,
5660 *oval_image,
5661 *vignette_image;
5662
5663 assert(image != (Image *) NULL);
5664 assert(image->signature == MagickSignature);
5665 if (image->debug != MagickFalse)
5666 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5667 assert(exception != (ExceptionInfo *) NULL);
5668 assert(exception->signature == MagickSignature);
5669 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5670 if (canvas_image == (Image *) NULL)
5671 return((Image *) NULL);
5672 if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
5673 {
5674 InheritException(exception,&canvas_image->exception);
5675 canvas_image=DestroyImage(canvas_image);
5676 return((Image *) NULL);
5677 }
5678 canvas_image->matte=MagickTrue;
5679 oval_image=CloneImage(canvas_image,canvas_image->columns,
5680 canvas_image->rows,MagickTrue,exception);
5681 if (oval_image == (Image *) NULL)
5682 {
5683 canvas_image=DestroyImage(canvas_image);
5684 return((Image *) NULL);
5685 }
5686 (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5687 (void) SetImageBackgroundColor(oval_image);
5688 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5689 (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5690 (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
5691 (void) FormatMagickString(ellipse,MaxTextExtent,
cristy8cd5b312010-01-07 01:10:24 +00005692 "ellipse %.15g,%.15g,%.15g,%.15g,0.0,360.0",image->columns/2.0,
5693 image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005694 draw_info->primitive=AcquireString(ellipse);
5695 (void) DrawImage(oval_image,draw_info);
5696 draw_info=DestroyDrawInfo(draw_info);
5697 blur_image=BlurImage(oval_image,radius,sigma,exception);
5698 oval_image=DestroyImage(oval_image);
5699 if (blur_image == (Image *) NULL)
5700 {
5701 canvas_image=DestroyImage(canvas_image);
5702 return((Image *) NULL);
5703 }
5704 blur_image->matte=MagickFalse;
5705 (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5706 blur_image=DestroyImage(blur_image);
5707 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5708 canvas_image=DestroyImage(canvas_image);
5709 return(vignette_image);
5710}
5711
5712/*
5713%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5714% %
5715% %
5716% %
5717% W a v e I m a g e %
5718% %
5719% %
5720% %
5721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5722%
5723% WaveImage() creates a "ripple" effect in the image by shifting the pixels
5724% vertically along a sine wave whose amplitude and wavelength is specified
5725% by the given parameters.
5726%
5727% The format of the WaveImage method is:
5728%
5729% Image *WaveImage(const Image *image,const double amplitude,
5730% const double wave_length,ExceptionInfo *exception)
5731%
5732% A description of each parameter follows:
5733%
5734% o image: the image.
5735%
5736% o amplitude, wave_length: Define the amplitude and wave length of the
5737% sine wave.
5738%
5739% o exception: return any errors or warnings in this structure.
5740%
5741*/
5742MagickExport Image *WaveImage(const Image *image,const double amplitude,
5743 const double wave_length,ExceptionInfo *exception)
5744{
5745#define WaveImageTag "Wave/Image"
5746
cristyfa112112010-01-04 17:48:07 +00005747 CacheView
5748 *wave_view;
5749
cristy3ed852e2009-09-05 21:47:34 +00005750 Image
5751 *wave_image;
5752
5753 long
5754 progress,
5755 y;
5756
5757 MagickBooleanType
5758 status;
5759
5760 MagickPixelPacket
5761 zero;
5762
5763 MagickRealType
5764 *sine_map;
5765
5766 register long
5767 i;
5768
5769 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00005770 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00005771
5772 /*
5773 Initialize wave image attributes.
5774 */
5775 assert(image != (Image *) NULL);
5776 assert(image->signature == MagickSignature);
5777 if (image->debug != MagickFalse)
5778 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5779 assert(exception != (ExceptionInfo *) NULL);
5780 assert(exception->signature == MagickSignature);
5781 wave_image=CloneImage(image,image->columns,(unsigned long) (image->rows+2.0*
5782 fabs(amplitude)),MagickTrue,exception);
5783 if (wave_image == (Image *) NULL)
5784 return((Image *) NULL);
5785 if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
5786 {
5787 InheritException(exception,&wave_image->exception);
5788 wave_image=DestroyImage(wave_image);
5789 return((Image *) NULL);
5790 }
5791 if (wave_image->background_color.opacity != OpaqueOpacity)
5792 wave_image->matte=MagickTrue;
5793 /*
5794 Allocate sine map.
5795 */
5796 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5797 sizeof(*sine_map));
5798 if (sine_map == (MagickRealType *) NULL)
5799 {
5800 wave_image=DestroyImage(wave_image);
5801 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5802 }
5803 for (i=0; i < (long) wave_image->columns; i++)
5804 sine_map[i]=fabs(amplitude)+amplitude*sin((2*MagickPI*i)/wave_length);
5805 /*
5806 Wave image.
5807 */
5808 status=MagickTrue;
5809 progress=0;
5810 GetMagickPixelPacket(wave_image,&zero);
5811 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
5812 wave_view=AcquireCacheView(wave_image);
cristyb5d5f722009-11-04 03:03:49 +00005813#if defined(MAGICKCORE_OPENMP_SUPPORT)
5814 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005815#endif
5816 for (y=0; y < (long) wave_image->rows; y++)
5817 {
5818 MagickPixelPacket
5819 pixel;
5820
5821 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00005822 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00005823
5824 register long
5825 id,
5826 x;
5827
5828 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005829 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005830
5831 if (status == MagickFalse)
5832 continue;
5833 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5834 exception);
5835 if (q == (PixelPacket *) NULL)
5836 {
5837 status=MagickFalse;
5838 continue;
5839 }
5840 indexes=GetCacheViewAuthenticIndexQueue(wave_view);
5841 pixel=zero;
5842 id=GetOpenMPThreadId();
5843 (void) SetResampleFilterVirtualPixelMethod(resample_filter[id],
5844 BackgroundVirtualPixelMethod);
5845 for (x=0; x < (long) wave_image->columns; x++)
5846 {
5847 (void) ResamplePixelColor(resample_filter[id],(double) x,(double) (y-
5848 sine_map[x]),&pixel);
5849 SetPixelPacket(wave_image,&pixel,q,indexes+x);
5850 q++;
5851 }
5852 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5853 status=MagickFalse;
5854 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5855 {
5856 MagickBooleanType
5857 proceed;
5858
cristyb5d5f722009-11-04 03:03:49 +00005859#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005860 #pragma omp critical (MagickCore_WaveImage)
5861#endif
5862 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5863 if (proceed == MagickFalse)
5864 status=MagickFalse;
5865 }
5866 }
5867 wave_view=DestroyCacheView(wave_view);
5868 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5869 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5870 if (status == MagickFalse)
5871 wave_image=DestroyImage(wave_image);
5872 return(wave_image);
5873}