blob: 1ae84ef2887cee50cfdbe5e20898903c3242c77a [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"
cristye7f51092010-01-17 00:39:37 +000067#include "magick/magick.h"
cristy3ed852e2009-09-05 21:47:34 +000068#include "magick/memory_.h"
69#include "magick/monitor.h"
70#include "magick/monitor-private.h"
71#include "magick/option.h"
72#include "magick/pixel-private.h"
73#include "magick/property.h"
74#include "magick/quantum.h"
cristyce70c172010-01-07 17:15:30 +000075#include "magick/quantum-private.h"
cristy3ed852e2009-09-05 21:47:34 +000076#include "magick/random_.h"
77#include "magick/random-private.h"
78#include "magick/resample.h"
79#include "magick/resample-private.h"
80#include "magick/resize.h"
81#include "magick/shear.h"
82#include "magick/splay-tree.h"
83#include "magick/statistic.h"
84#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000085#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000086#include "magick/thread-private.h"
87#include "magick/transform.h"
88#include "magick/utility.h"
89
90/*
91 Define declarations.
92*/
93#define LeftShiftOperator 0xf5
94#define RightShiftOperator 0xf6
95#define LessThanEqualOperator 0xf7
96#define GreaterThanEqualOperator 0xf8
97#define EqualOperator 0xf9
98#define NotEqualOperator 0xfa
99#define LogicalAndOperator 0xfb
100#define LogicalOrOperator 0xfc
101
102struct _FxInfo
103{
104 const Image
105 *images;
106
107 MagickBooleanType
108 matte;
109
110 char
111 *expression;
112
113 FILE
114 *file;
115
116 SplayTreeInfo
117 *colors,
118 *symbols;
119
120 ResampleFilter
121 **resample_filter;
122
123 RandomInfo
124 *random_info;
125
126 ExceptionInfo
127 *exception;
128};
129
130/*
131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
132% %
133% %
134% %
135+ A c q u i r e F x I n f o %
136% %
137% %
138% %
139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140%
141% AcquireFxInfo() allocates the FxInfo structure.
142%
143% The format of the AcquireFxInfo method is:
144%
145% FxInfo *AcquireFxInfo(Image *image,const char *expression)
146% A description of each parameter follows:
147%
148% o image: the image.
149%
150% o expression: the expression.
151%
152*/
153MagickExport FxInfo *AcquireFxInfo(const Image *image,const char *expression)
154{
155 char
156 fx_op[2];
157
158 FxInfo
159 *fx_info;
160
cristybb503372010-05-27 20:51:26 +0000161 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000162 i;
163
cristy90823212009-12-12 20:48:33 +0000164 fx_info=(FxInfo *) AcquireAlignedMemory(1,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +0000165 if (fx_info == (FxInfo *) NULL)
166 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
167 (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
168 fx_info->exception=AcquireExceptionInfo();
169 fx_info->images=image;
170 fx_info->matte=image->matte;
171 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
172 RelinquishMagickMemory);
173 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
174 RelinquishMagickMemory);
175 fx_info->resample_filter=(ResampleFilter **) AcquireQuantumMemory(
176 GetImageListLength(fx_info->images),sizeof(*fx_info->resample_filter));
177 if (fx_info->resample_filter == (ResampleFilter **) NULL)
178 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +0000179 for (i=0; i < (ssize_t) GetImageListLength(fx_info->images); i++)
cristy3ed852e2009-09-05 21:47:34 +0000180 {
181 fx_info->resample_filter[i]=AcquireResampleFilter(GetImageFromList(
182 fx_info->images,i),fx_info->exception);
183 SetResampleFilter(fx_info->resample_filter[i],PointFilter,1.0);
184 }
185 fx_info->random_info=AcquireRandomInfo();
186 fx_info->expression=ConstantString(expression);
187 fx_info->file=stderr;
188 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
189 if ((strstr(fx_info->expression,"e+") != (char *) NULL) ||
190 (strstr(fx_info->expression,"e-") != (char *) NULL))
191 {
192 /*
193 Convert scientific notation.
194 */
195 (void) SubstituteString(&fx_info->expression,"0e+","0*10^");
196 (void) SubstituteString(&fx_info->expression,"1e+","1*10^");
197 (void) SubstituteString(&fx_info->expression,"2e+","2*10^");
198 (void) SubstituteString(&fx_info->expression,"3e+","3*10^");
199 (void) SubstituteString(&fx_info->expression,"4e+","4*10^");
200 (void) SubstituteString(&fx_info->expression,"5e+","5*10^");
201 (void) SubstituteString(&fx_info->expression,"6e+","6*10^");
202 (void) SubstituteString(&fx_info->expression,"7e+","7*10^");
203 (void) SubstituteString(&fx_info->expression,"8e+","8*10^");
204 (void) SubstituteString(&fx_info->expression,"9e+","9*10^");
205 (void) SubstituteString(&fx_info->expression,"0e-","0*10^-");
206 (void) SubstituteString(&fx_info->expression,"1e-","1*10^-");
207 (void) SubstituteString(&fx_info->expression,"2e-","2*10^-");
208 (void) SubstituteString(&fx_info->expression,"3e-","3*10^-");
209 (void) SubstituteString(&fx_info->expression,"4e-","4*10^-");
210 (void) SubstituteString(&fx_info->expression,"5e-","5*10^-");
211 (void) SubstituteString(&fx_info->expression,"6e-","6*10^-");
212 (void) SubstituteString(&fx_info->expression,"7e-","7*10^-");
213 (void) SubstituteString(&fx_info->expression,"8e-","8*10^-");
214 (void) SubstituteString(&fx_info->expression,"9e-","9*10^-");
215 }
216 /*
217 Convert complex to simple operators.
218 */
219 fx_op[1]='\0';
220 *fx_op=(char) LeftShiftOperator;
221 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
222 *fx_op=(char) RightShiftOperator;
223 (void) SubstituteString(&fx_info->expression,">>",fx_op);
224 *fx_op=(char) LessThanEqualOperator;
225 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
226 *fx_op=(char) GreaterThanEqualOperator;
227 (void) SubstituteString(&fx_info->expression,">=",fx_op);
228 *fx_op=(char) EqualOperator;
229 (void) SubstituteString(&fx_info->expression,"==",fx_op);
230 *fx_op=(char) NotEqualOperator;
231 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
232 *fx_op=(char) LogicalAndOperator;
233 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
234 *fx_op=(char) LogicalOrOperator;
235 (void) SubstituteString(&fx_info->expression,"||",fx_op);
236 return(fx_info);
237}
238
239/*
240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241% %
242% %
243% %
244% A d d N o i s e I m a g e %
245% %
246% %
247% %
248%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
249%
250% AddNoiseImage() adds random noise to the image.
251%
252% The format of the AddNoiseImage method is:
253%
254% Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
255% ExceptionInfo *exception)
256% Image *AddNoiseImageChannel(const Image *image,const ChannelType channel,
257% const NoiseType noise_type,ExceptionInfo *exception)
258%
259% A description of each parameter follows:
260%
261% o image: the image.
262%
263% o channel: the channel type.
264%
265% o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
266% Impulse, Laplacian, or Poisson.
267%
268% o exception: return any errors or warnings in this structure.
269%
270*/
271MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
272 ExceptionInfo *exception)
273{
274 Image
275 *noise_image;
276
277 noise_image=AddNoiseImageChannel(image,DefaultChannels,noise_type,exception);
278 return(noise_image);
279}
280
281MagickExport Image *AddNoiseImageChannel(const Image *image,
282 const ChannelType channel,const NoiseType noise_type,ExceptionInfo *exception)
283{
284#define AddNoiseImageTag "AddNoise/Image"
285
cristyfa112112010-01-04 17:48:07 +0000286 CacheView
287 *image_view,
288 *noise_view;
289
cristy3ed852e2009-09-05 21:47:34 +0000290 const char
291 *option;
292
293 Image
294 *noise_image;
295
cristy3ed852e2009-09-05 21:47:34 +0000296 MagickBooleanType
297 status;
298
cristybb503372010-05-27 20:51:26 +0000299 MagickOffsetType
300 progress;
301
cristy3ed852e2009-09-05 21:47:34 +0000302 MagickRealType
303 attenuate;
304
305 RandomInfo
cristyfa112112010-01-04 17:48:07 +0000306 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +0000307
cristybb503372010-05-27 20:51:26 +0000308 ssize_t
309 y;
310
cristy3ed852e2009-09-05 21:47:34 +0000311 /*
312 Initialize noise image attributes.
313 */
314 assert(image != (const Image *) NULL);
315 assert(image->signature == MagickSignature);
316 if (image->debug != MagickFalse)
317 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
318 assert(exception != (ExceptionInfo *) NULL);
319 assert(exception->signature == MagickSignature);
320 noise_image=CloneImage(image,0,0,MagickTrue,exception);
321 if (noise_image == (Image *) NULL)
322 return((Image *) NULL);
323 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
324 {
325 InheritException(exception,&noise_image->exception);
326 noise_image=DestroyImage(noise_image);
327 return((Image *) NULL);
328 }
329 /*
330 Add noise in each row.
331 */
332 attenuate=1.0;
333 option=GetImageArtifact(image,"attenuate");
334 if (option != (char *) NULL)
cristyf2f27272009-12-17 14:48:46 +0000335 attenuate=StringToDouble(option);
cristy3ed852e2009-09-05 21:47:34 +0000336 status=MagickTrue;
337 progress=0;
338 random_info=AcquireRandomInfoThreadSet();
339 image_view=AcquireCacheView(image);
340 noise_view=AcquireCacheView(noise_image);
cristy319a1e72010-02-21 15:13:11 +0000341#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000342 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000343#endif
cristybb503372010-05-27 20:51:26 +0000344 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000345 {
cristy6ebe97c2010-07-03 01:17:28 +0000346 int
347 id;
348
cristy3ed852e2009-09-05 21:47:34 +0000349 MagickBooleanType
350 sync;
351
352 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000353 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000354
355 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000356 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000357
358 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000359 *restrict noise_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000360
cristybb503372010-05-27 20:51:26 +0000361 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000362 x;
363
364 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000365 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000366
367 if (status == MagickFalse)
368 continue;
369 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
370 q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
371 exception);
372 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
373 {
374 status=MagickFalse;
375 continue;
376 }
377 indexes=GetCacheViewVirtualIndexQueue(image_view);
378 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
379 id=GetOpenMPThreadId();
cristybb503372010-05-27 20:51:26 +0000380 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000381 {
382 if ((channel & RedChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000383 q->red=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000384 p->red,noise_type,attenuate));
385 if ((channel & GreenChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000386 q->green=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000387 p->green,noise_type,attenuate));
388 if ((channel & BlueChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000389 q->blue=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000390 p->blue,noise_type,attenuate));
391 if ((channel & OpacityChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000392 q->opacity=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000393 p->opacity,noise_type,attenuate));
394 if (((channel & IndexChannel) != 0) &&
395 (image->colorspace == CMYKColorspace))
cristyce70c172010-01-07 17:15:30 +0000396 noise_indexes[x]=(IndexPacket) ClampToQuantum(GenerateDifferentialNoise(
cristy3ed852e2009-09-05 21:47:34 +0000397 random_info[id],indexes[x],noise_type,attenuate));
398 p++;
399 q++;
400 }
401 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
402 if (sync == MagickFalse)
403 status=MagickFalse;
404 if (image->progress_monitor != (MagickProgressMonitor) NULL)
405 {
406 MagickBooleanType
407 proceed;
408
cristyb5d5f722009-11-04 03:03:49 +0000409#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy319a1e72010-02-21 15:13:11 +0000410 #pragma omp critical (MagickCore_AddNoiseImage)
cristy3ed852e2009-09-05 21:47:34 +0000411#endif
412 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
413 image->rows);
414 if (proceed == MagickFalse)
415 status=MagickFalse;
416 }
417 }
418 noise_view=DestroyCacheView(noise_view);
419 image_view=DestroyCacheView(image_view);
420 random_info=DestroyRandomInfoThreadSet(random_info);
421 if (status == MagickFalse)
422 noise_image=DestroyImage(noise_image);
423 return(noise_image);
424}
425
426/*
427%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
428% %
429% %
430% %
431% B l u e S h i f t I m a g e %
432% %
433% %
434% %
435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
436%
437% BlueShiftImage() mutes the colors of the image to simulate a scene at
438% nighttime in the moonlight.
439%
440% The format of the BlueShiftImage method is:
441%
442% Image *BlueShiftImage(const Image *image,const double factor,
443% ExceptionInfo *exception)
444%
445% A description of each parameter follows:
446%
447% o image: the image.
448%
449% o factor: the shift factor.
450%
451% o exception: return any errors or warnings in this structure.
452%
453*/
454MagickExport Image *BlueShiftImage(const Image *image,const double factor,
455 ExceptionInfo *exception)
456{
457#define BlueShiftImageTag "BlueShift/Image"
458
cristyc4c8d132010-01-07 01:58:38 +0000459 CacheView
460 *image_view,
461 *shift_view;
462
cristy3ed852e2009-09-05 21:47:34 +0000463 Image
464 *shift_image;
465
cristy3ed852e2009-09-05 21:47:34 +0000466 MagickBooleanType
467 status;
468
cristybb503372010-05-27 20:51:26 +0000469 MagickOffsetType
470 progress;
471
472 ssize_t
473 y;
474
cristy3ed852e2009-09-05 21:47:34 +0000475 /*
476 Allocate blue shift image.
477 */
478 assert(image != (const Image *) NULL);
479 assert(image->signature == MagickSignature);
480 if (image->debug != MagickFalse)
481 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
482 assert(exception != (ExceptionInfo *) NULL);
483 assert(exception->signature == MagickSignature);
484 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,
485 exception);
486 if (shift_image == (Image *) NULL)
487 return((Image *) NULL);
488 if (SetImageStorageClass(shift_image,DirectClass) == MagickFalse)
489 {
490 InheritException(exception,&shift_image->exception);
491 shift_image=DestroyImage(shift_image);
492 return((Image *) NULL);
493 }
494 /*
495 Blue-shift DirectClass image.
496 */
497 status=MagickTrue;
498 progress=0;
499 image_view=AcquireCacheView(image);
500 shift_view=AcquireCacheView(shift_image);
cristy319a1e72010-02-21 15:13:11 +0000501#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000502 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000503#endif
cristybb503372010-05-27 20:51:26 +0000504 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000505 {
506 MagickBooleanType
507 sync;
508
509 MagickPixelPacket
510 pixel;
511
512 Quantum
513 quantum;
514
515 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000516 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000517
cristybb503372010-05-27 20:51:26 +0000518 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000519 x;
520
521 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000522 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000523
524 if (status == MagickFalse)
525 continue;
526 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
527 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
528 exception);
529 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
530 {
531 status=MagickFalse;
532 continue;
533 }
cristybb503372010-05-27 20:51:26 +0000534 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000535 {
cristyce70c172010-01-07 17:15:30 +0000536 quantum=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000537 if (p->green < quantum)
cristyce70c172010-01-07 17:15:30 +0000538 quantum=GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000539 if (p->blue < quantum)
cristyce70c172010-01-07 17:15:30 +0000540 quantum=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000541 pixel.red=0.5*(p->red+factor*quantum);
542 pixel.green=0.5*(p->green+factor*quantum);
543 pixel.blue=0.5*(p->blue+factor*quantum);
cristyce70c172010-01-07 17:15:30 +0000544 quantum=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000545 if (p->green > quantum)
cristyce70c172010-01-07 17:15:30 +0000546 quantum=GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000547 if (p->blue > quantum)
cristyce70c172010-01-07 17:15:30 +0000548 quantum=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000549 pixel.red=0.5*(pixel.red+factor*quantum);
550 pixel.green=0.5*(pixel.green+factor*quantum);
551 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristyce70c172010-01-07 17:15:30 +0000552 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
553 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
554 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +0000555 p++;
556 q++;
557 }
558 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
559 if (sync == MagickFalse)
560 status=MagickFalse;
561 if (image->progress_monitor != (MagickProgressMonitor) NULL)
562 {
563 MagickBooleanType
564 proceed;
565
cristy319a1e72010-02-21 15:13:11 +0000566#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000567 #pragma omp critical (MagickCore_BlueShiftImage)
568#endif
569 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
570 image->rows);
571 if (proceed == MagickFalse)
572 status=MagickFalse;
573 }
574 }
575 image_view=DestroyCacheView(image_view);
576 shift_view=DestroyCacheView(shift_view);
577 if (status == MagickFalse)
578 shift_image=DestroyImage(shift_image);
579 return(shift_image);
580}
581
582/*
583%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
584% %
585% %
586% %
587% C h a r c o a l I m a g e %
588% %
589% %
590% %
591%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
592%
593% CharcoalImage() creates a new image that is a copy of an existing one with
594% the edge highlighted. It allocates the memory necessary for the new Image
595% structure and returns a pointer to the new image.
596%
597% The format of the CharcoalImage method is:
598%
599% Image *CharcoalImage(const Image *image,const double radius,
600% const double sigma,ExceptionInfo *exception)
601%
602% A description of each parameter follows:
603%
604% o image: the image.
605%
606% o radius: the radius of the pixel neighborhood.
607%
608% o sigma: the standard deviation of the Gaussian, in pixels.
609%
610% o exception: return any errors or warnings in this structure.
611%
612*/
613MagickExport Image *CharcoalImage(const Image *image,const double radius,
614 const double sigma,ExceptionInfo *exception)
615{
616 Image
617 *charcoal_image,
618 *clone_image,
619 *edge_image;
620
621 assert(image != (Image *) NULL);
622 assert(image->signature == MagickSignature);
623 if (image->debug != MagickFalse)
624 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
625 assert(exception != (ExceptionInfo *) NULL);
626 assert(exception->signature == MagickSignature);
627 clone_image=CloneImage(image,0,0,MagickTrue,exception);
628 if (clone_image == (Image *) NULL)
629 return((Image *) NULL);
630 (void) SetImageType(clone_image,GrayscaleType);
631 edge_image=EdgeImage(clone_image,radius,exception);
632 clone_image=DestroyImage(clone_image);
633 if (edge_image == (Image *) NULL)
634 return((Image *) NULL);
635 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
636 edge_image=DestroyImage(edge_image);
637 if (charcoal_image == (Image *) NULL)
638 return((Image *) NULL);
639 (void) NormalizeImage(charcoal_image);
640 (void) NegateImage(charcoal_image,MagickFalse);
641 (void) SetImageType(charcoal_image,GrayscaleType);
642 return(charcoal_image);
643}
644
645/*
646%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
647% %
648% %
649% %
650% C o l o r i z e I m a g e %
651% %
652% %
653% %
654%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
655%
656% ColorizeImage() blends the fill color with each pixel in the image.
657% A percentage blend is specified with opacity. Control the application
658% of different color components by specifying a different percentage for
659% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
660%
661% The format of the ColorizeImage method is:
662%
663% Image *ColorizeImage(const Image *image,const char *opacity,
664% const PixelPacket colorize,ExceptionInfo *exception)
665%
666% A description of each parameter follows:
667%
668% o image: the image.
669%
670% o opacity: A character string indicating the level of opacity as a
671% percentage.
672%
673% o colorize: A color value.
674%
675% o exception: return any errors or warnings in this structure.
676%
677*/
678MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
679 const PixelPacket colorize,ExceptionInfo *exception)
680{
681#define ColorizeImageTag "Colorize/Image"
682
cristyc4c8d132010-01-07 01:58:38 +0000683 CacheView
684 *colorize_view,
685 *image_view;
686
cristy3ed852e2009-09-05 21:47:34 +0000687 GeometryInfo
688 geometry_info;
689
690 Image
691 *colorize_image;
692
cristy3ed852e2009-09-05 21:47:34 +0000693 MagickBooleanType
694 status;
695
cristybb503372010-05-27 20:51:26 +0000696 MagickOffsetType
697 progress;
698
cristy3ed852e2009-09-05 21:47:34 +0000699 MagickPixelPacket
700 pixel;
701
702 MagickStatusType
703 flags;
704
cristybb503372010-05-27 20:51:26 +0000705 ssize_t
706 y;
707
cristy3ed852e2009-09-05 21:47:34 +0000708 /*
709 Allocate colorized image.
710 */
711 assert(image != (const Image *) NULL);
712 assert(image->signature == MagickSignature);
713 if (image->debug != MagickFalse)
714 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
715 assert(exception != (ExceptionInfo *) NULL);
716 assert(exception->signature == MagickSignature);
717 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
718 exception);
719 if (colorize_image == (Image *) NULL)
720 return((Image *) NULL);
721 if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
722 {
723 InheritException(exception,&colorize_image->exception);
724 colorize_image=DestroyImage(colorize_image);
725 return((Image *) NULL);
726 }
727 if (opacity == (const char *) NULL)
728 return(colorize_image);
729 /*
730 Determine RGB values of the pen color.
731 */
732 flags=ParseGeometry(opacity,&geometry_info);
733 pixel.red=geometry_info.rho;
734 pixel.green=geometry_info.rho;
735 pixel.blue=geometry_info.rho;
736 pixel.opacity=(MagickRealType) OpaqueOpacity;
737 if ((flags & SigmaValue) != 0)
738 pixel.green=geometry_info.sigma;
739 if ((flags & XiValue) != 0)
740 pixel.blue=geometry_info.xi;
741 if ((flags & PsiValue) != 0)
742 pixel.opacity=geometry_info.psi;
743 /*
744 Colorize DirectClass image.
745 */
746 status=MagickTrue;
747 progress=0;
748 image_view=AcquireCacheView(image);
749 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000750#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000751 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000752#endif
cristybb503372010-05-27 20:51:26 +0000753 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000754 {
755 MagickBooleanType
756 sync;
757
758 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000759 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000760
cristybb503372010-05-27 20:51:26 +0000761 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000762 x;
763
764 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000765 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000766
767 if (status == MagickFalse)
768 continue;
769 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
770 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
771 exception);
772 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
773 {
774 status=MagickFalse;
775 continue;
776 }
cristybb503372010-05-27 20:51:26 +0000777 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000778 {
779 q->red=(Quantum) ((p->red*(100.0-pixel.red)+
780 colorize.red*pixel.red)/100.0);
781 q->green=(Quantum) ((p->green*(100.0-pixel.green)+
782 colorize.green*pixel.green)/100.0);
783 q->blue=(Quantum) ((p->blue*(100.0-pixel.blue)+
784 colorize.blue*pixel.blue)/100.0);
785 q->opacity=(Quantum) ((p->opacity*(100.0-pixel.opacity)+
786 colorize.opacity*pixel.opacity)/100.0);
787 p++;
788 q++;
789 }
790 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
791 if (sync == MagickFalse)
792 status=MagickFalse;
793 if (image->progress_monitor != (MagickProgressMonitor) NULL)
794 {
795 MagickBooleanType
796 proceed;
797
cristy319a1e72010-02-21 15:13:11 +0000798#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000799 #pragma omp critical (MagickCore_ColorizeImage)
800#endif
801 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
802 if (proceed == MagickFalse)
803 status=MagickFalse;
804 }
805 }
806 image_view=DestroyCacheView(image_view);
807 colorize_view=DestroyCacheView(colorize_view);
808 if (status == MagickFalse)
809 colorize_image=DestroyImage(colorize_image);
810 return(colorize_image);
811}
812
813/*
814%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
815% %
816% %
817% %
cristye6365592010-04-02 17:31:23 +0000818% C o l o r M a t r i x I m a g e %
819% %
820% %
821% %
822%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
823%
824% ColorMatrixImage() applies color transformation to an image. This method
825% permits saturation changes, hue rotation, luminance to alpha, and various
826% other effects. Although variable-sized transformation matrices can be used,
827% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
828% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
829% except offsets are in column 6 rather than 5 (in support of CMYKA images)
830% and offsets are normalized (divide Flash offset by 255).
831%
832% The format of the ColorMatrixImage method is:
833%
834% Image *ColorMatrixImage(const Image *image,
835% const KernelInfo *color_matrix,ExceptionInfo *exception)
836%
837% A description of each parameter follows:
838%
839% o image: the image.
840%
841% o color_matrix: the color matrix.
842%
843% o exception: return any errors or warnings in this structure.
844%
845*/
846MagickExport Image *ColorMatrixImage(const Image *image,
847 const KernelInfo *color_matrix,ExceptionInfo *exception)
848{
849#define ColorMatrixImageTag "ColorMatrix/Image"
850
851 CacheView
852 *color_view,
853 *image_view;
854
855 double
856 ColorMatrix[6][6] =
857 {
858 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
859 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
860 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
861 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
862 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
863 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
864 };
865
866 Image
867 *color_image;
868
cristye6365592010-04-02 17:31:23 +0000869 MagickBooleanType
870 status;
871
cristybb503372010-05-27 20:51:26 +0000872 MagickOffsetType
873 progress;
874
875 register ssize_t
cristye6365592010-04-02 17:31:23 +0000876 i;
877
cristybb503372010-05-27 20:51:26 +0000878 ssize_t
879 u,
880 v,
881 y;
882
cristye6365592010-04-02 17:31:23 +0000883 /*
884 Create color matrix.
885 */
886 assert(image != (Image *) NULL);
887 assert(image->signature == MagickSignature);
888 if (image->debug != MagickFalse)
889 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
890 assert(exception != (ExceptionInfo *) NULL);
891 assert(exception->signature == MagickSignature);
892 i=0;
cristybb503372010-05-27 20:51:26 +0000893 for (v=0; v < (ssize_t) color_matrix->height; v++)
894 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000895 {
896 if ((v < 6) && (u < 6))
897 ColorMatrix[v][u]=color_matrix->values[i];
898 i++;
899 }
900 /*
901 Initialize color image.
902 */
cristy12550e62010-06-07 12:46:40 +0000903 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000904 if (color_image == (Image *) NULL)
905 return((Image *) NULL);
906 if (SetImageStorageClass(color_image,DirectClass) == MagickFalse)
907 {
908 InheritException(exception,&color_image->exception);
909 color_image=DestroyImage(color_image);
910 return((Image *) NULL);
911 }
912 if (image->debug != MagickFalse)
913 {
914 char
915 format[MaxTextExtent],
916 *message;
917
918 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
919 " ColorMatrix image with color matrix:");
920 message=AcquireString("");
921 for (v=0; v < 6; v++)
922 {
923 *message='\0';
cristye8c25f92010-06-03 00:53:06 +0000924 (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000925 (void) ConcatenateString(&message,format);
926 for (u=0; u < 6; u++)
927 {
928 (void) FormatMagickString(format,MaxTextExtent,"%+f ",
929 ColorMatrix[v][u]);
930 (void) ConcatenateString(&message,format);
931 }
932 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
933 }
934 message=DestroyString(message);
935 }
936 /*
937 ColorMatrix image.
938 */
939 status=MagickTrue;
940 progress=0;
941 image_view=AcquireCacheView(image);
942 color_view=AcquireCacheView(color_image);
943#if defined(MAGICKCORE_OPENMP_SUPPORT)
944 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
945#endif
cristybb503372010-05-27 20:51:26 +0000946 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000947 {
948 MagickRealType
949 pixel;
950
951 register const IndexPacket
952 *restrict indexes;
953
954 register const PixelPacket
955 *restrict p;
956
cristybb503372010-05-27 20:51:26 +0000957 register ssize_t
cristye6365592010-04-02 17:31:23 +0000958 x;
959
960 register IndexPacket
961 *restrict color_indexes;
962
963 register PixelPacket
964 *restrict q;
965
966 if (status == MagickFalse)
967 continue;
968 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
969 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
970 exception);
971 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
972 {
973 status=MagickFalse;
974 continue;
975 }
976 indexes=GetCacheViewVirtualIndexQueue(image_view);
977 color_indexes=GetCacheViewAuthenticIndexQueue(color_view);
cristybb503372010-05-27 20:51:26 +0000978 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +0000979 {
cristybb503372010-05-27 20:51:26 +0000980 register ssize_t
cristye6365592010-04-02 17:31:23 +0000981 v;
982
cristybb503372010-05-27 20:51:26 +0000983 size_t
cristye6365592010-04-02 17:31:23 +0000984 height;
985
986 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +0000987 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +0000988 {
989 pixel=ColorMatrix[v][0]*p->red+ColorMatrix[v][1]*p->green+
990 ColorMatrix[v][2]*p->blue;
991 if (image->matte != MagickFalse)
992 pixel+=ColorMatrix[v][3]*(QuantumRange-p->opacity);
993 if (image->colorspace == CMYKColorspace)
994 pixel+=ColorMatrix[v][4]*indexes[x];
995 pixel+=QuantumRange*ColorMatrix[v][5];
996 switch (v)
997 {
998 case 0: q->red=ClampToQuantum(pixel); break;
999 case 1: q->green=ClampToQuantum(pixel); break;
1000 case 2: q->blue=ClampToQuantum(pixel); break;
1001 case 3:
1002 {
1003 if (image->matte != MagickFalse)
1004 q->opacity=ClampToQuantum(QuantumRange-pixel);
1005 break;
1006 }
1007 case 4:
1008 {
1009 if (image->colorspace == CMYKColorspace)
1010 color_indexes[x]=ClampToQuantum(pixel);
1011 break;
1012 }
1013 }
1014 }
1015 p++;
1016 q++;
1017 }
1018 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1019 status=MagickFalse;
1020 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1021 {
1022 MagickBooleanType
1023 proceed;
1024
1025#if defined(MAGICKCORE_OPENMP_SUPPORT)
1026 #pragma omp critical (MagickCore_ColorMatrixImage)
1027#endif
1028 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1029 image->rows);
1030 if (proceed == MagickFalse)
1031 status=MagickFalse;
1032 }
1033 }
1034 color_view=DestroyCacheView(color_view);
1035 image_view=DestroyCacheView(image_view);
1036 if (status == MagickFalse)
1037 color_image=DestroyImage(color_image);
1038 return(color_image);
1039}
1040
1041/*
1042%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1043% %
1044% %
1045% %
cristy3ed852e2009-09-05 21:47:34 +00001046+ D e s t r o y F x I n f o %
1047% %
1048% %
1049% %
1050%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1051%
1052% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1053%
1054% The format of the DestroyFxInfo method is:
1055%
1056% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1057%
1058% A description of each parameter follows:
1059%
1060% o fx_info: the fx info.
1061%
1062*/
1063MagickExport FxInfo *DestroyFxInfo(FxInfo *fx_info)
1064{
cristybb503372010-05-27 20:51:26 +00001065 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001066 i;
1067
1068 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1069 fx_info->expression=DestroyString(fx_info->expression);
1070 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1071 fx_info->colors=DestroySplayTree(fx_info->colors);
cristybb503372010-05-27 20:51:26 +00001072 for (i=0; i < (ssize_t) GetImageListLength(fx_info->images); i++)
cristy3ed852e2009-09-05 21:47:34 +00001073 fx_info->resample_filter[i]=DestroyResampleFilter(
1074 fx_info->resample_filter[i]);
1075 fx_info->resample_filter=(ResampleFilter **) RelinquishMagickMemory(
1076 fx_info->resample_filter);
1077 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1078 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1079 return(fx_info);
1080}
1081
1082/*
1083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1084% %
1085% %
1086% %
cristy3ed852e2009-09-05 21:47:34 +00001087+ 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 %
1088% %
1089% %
1090% %
1091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1092%
1093% FxEvaluateChannelExpression() evaluates an expression and returns the
1094% results.
1095%
1096% The format of the FxEvaluateExpression method is:
1097%
1098% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristybb503372010-05-27 20:51:26 +00001099% const ChannelType channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001100% MagickRealType *alpha,Exceptioninfo *exception)
1101% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1102% MagickRealType *alpha,Exceptioninfo *exception)
1103%
1104% A description of each parameter follows:
1105%
1106% o fx_info: the fx info.
1107%
1108% o channel: the channel.
1109%
1110% o x,y: the pixel position.
1111%
1112% o alpha: the result.
1113%
1114% o exception: return any errors or warnings in this structure.
1115%
1116*/
1117
cristy351842f2010-03-07 15:27:38 +00001118static inline double MagickMax(const double x,const double y)
1119{
1120 if (x > y)
1121 return(x);
1122 return(y);
1123}
1124
1125static inline double MagickMin(const double x,const double y)
1126{
1127 if (x < y)
1128 return(x);
1129 return(y);
1130}
1131
cristy3ed852e2009-09-05 21:47:34 +00001132static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
1133 ChannelType channel,const char *symbol,ExceptionInfo *exception)
1134{
1135 char
1136 key[MaxTextExtent],
1137 statistic[MaxTextExtent];
1138
1139 const char
1140 *value;
1141
1142 register const char
1143 *p;
1144
1145 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1146 if (*p == '.')
1147 switch (*++p) /* e.g. depth.r */
1148 {
1149 case 'r': channel=RedChannel; break;
1150 case 'g': channel=GreenChannel; break;
1151 case 'b': channel=BlueChannel; break;
1152 case 'c': channel=CyanChannel; break;
1153 case 'm': channel=MagentaChannel; break;
1154 case 'y': channel=YellowChannel; break;
1155 case 'k': channel=BlackChannel; break;
1156 default: break;
1157 }
cristye8c25f92010-06-03 00:53:06 +00001158 (void) FormatMagickString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
1159 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001160 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1161 if (value != (const char *) NULL)
cristyf2f27272009-12-17 14:48:46 +00001162 return(QuantumScale*StringToDouble(value));
cristy3ed852e2009-09-05 21:47:34 +00001163 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1164 if (LocaleNCompare(symbol,"depth",5) == 0)
1165 {
cristybb503372010-05-27 20:51:26 +00001166 size_t
cristy3ed852e2009-09-05 21:47:34 +00001167 depth;
1168
1169 depth=GetImageChannelDepth(image,channel,exception);
cristye8c25f92010-06-03 00:53:06 +00001170 (void) FormatMagickString(statistic,MaxTextExtent,"%.20g",(double)
1171 depth);
cristy3ed852e2009-09-05 21:47:34 +00001172 }
1173 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1174 {
1175 double
1176 kurtosis,
1177 skewness;
1178
1179 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1180 exception);
cristye7f51092010-01-17 00:39:37 +00001181 (void) FormatMagickString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001182 }
1183 if (LocaleNCompare(symbol,"maxima",6) == 0)
1184 {
1185 double
1186 maxima,
1187 minima;
1188
1189 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristye7f51092010-01-17 00:39:37 +00001190 (void) FormatMagickString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001191 }
1192 if (LocaleNCompare(symbol,"mean",4) == 0)
1193 {
1194 double
1195 mean,
1196 standard_deviation;
1197
1198 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1199 exception);
cristye7f51092010-01-17 00:39:37 +00001200 (void) FormatMagickString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001201 }
1202 if (LocaleNCompare(symbol,"minima",6) == 0)
1203 {
1204 double
1205 maxima,
1206 minima;
1207
1208 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristye7f51092010-01-17 00:39:37 +00001209 (void) FormatMagickString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001210 }
1211 if (LocaleNCompare(symbol,"skewness",8) == 0)
1212 {
1213 double
1214 kurtosis,
1215 skewness;
1216
1217 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1218 exception);
cristye7f51092010-01-17 00:39:37 +00001219 (void) FormatMagickString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001220 }
1221 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1222 {
1223 double
1224 mean,
1225 standard_deviation;
1226
1227 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1228 exception);
cristye7f51092010-01-17 00:39:37 +00001229 (void) FormatMagickString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001230 standard_deviation);
1231 }
1232 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1233 ConstantString(statistic));
cristyf2f27272009-12-17 14:48:46 +00001234 return(QuantumScale*StringToDouble(statistic));
cristy3ed852e2009-09-05 21:47:34 +00001235}
1236
1237static MagickRealType
cristye85007d2010-06-06 22:51:36 +00001238 FxEvaluateSubexpression(FxInfo *,const ChannelType,const ssize_t,
1239 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001240
1241static inline MagickRealType FxMax(FxInfo *fx_info,const ChannelType channel,
cristye85007d2010-06-06 22:51:36 +00001242 const ssize_t x,const ssize_t y,const char *expression,
1243 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001244{
1245 MagickRealType
1246 alpha,
1247 beta;
1248
1249 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1250 return((MagickRealType) MagickMax((double) alpha,(double) beta));
1251}
1252
1253static inline MagickRealType FxMin(FxInfo *fx_info,ChannelType channel,
cristye85007d2010-06-06 22:51:36 +00001254 const ssize_t x,const ssize_t y,const char *expression,
1255 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001256{
1257 MagickRealType
1258 alpha,
1259 beta;
1260
1261 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1262 return((MagickRealType) MagickMin((double) alpha,(double) beta));
1263}
1264
1265static inline const char *FxSubexpression(const char *expression,
1266 ExceptionInfo *exception)
1267{
1268 const char
1269 *subexpression;
1270
cristybb503372010-05-27 20:51:26 +00001271 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001272 level;
1273
1274 level=0;
1275 subexpression=expression;
1276 while ((*subexpression != '\0') &&
1277 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1278 {
1279 if (strchr("(",(int) *subexpression) != (char *) NULL)
1280 level++;
1281 else
1282 if (strchr(")",(int) *subexpression) != (char *) NULL)
1283 level--;
1284 subexpression++;
1285 }
1286 if (*subexpression == '\0')
1287 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1288 "UnbalancedParenthesis","`%s'",expression);
1289 return(subexpression);
1290}
1291
1292static MagickRealType FxGetSymbol(FxInfo *fx_info,const ChannelType channel,
cristye85007d2010-06-06 22:51:36 +00001293 const ssize_t x,const ssize_t y,const char *expression,
1294 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001295{
1296 char
1297 *q,
1298 subexpression[MaxTextExtent],
1299 symbol[MaxTextExtent];
1300
1301 const char
1302 *p,
1303 *value;
1304
1305 Image
1306 *image;
1307
1308 MagickPixelPacket
1309 pixel;
1310
1311 MagickRealType
1312 alpha,
1313 beta;
1314
1315 PointInfo
1316 point;
1317
cristybb503372010-05-27 20:51:26 +00001318 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001319 i;
1320
1321 size_t
1322 length;
1323
cristybb503372010-05-27 20:51:26 +00001324 size_t
cristy3ed852e2009-09-05 21:47:34 +00001325 level;
1326
1327 p=expression;
1328 i=GetImageIndexInList(fx_info->images);
1329 level=0;
1330 point.x=(double) x;
1331 point.y=(double) y;
1332 if (isalpha((int) *(p+1)) == 0)
1333 {
1334 if (strchr("suv",(int) *p) != (char *) NULL)
1335 {
1336 switch (*p)
1337 {
1338 case 's':
1339 default:
1340 {
1341 i=GetImageIndexInList(fx_info->images);
1342 break;
1343 }
1344 case 'u': i=0; break;
1345 case 'v': i=1; break;
1346 }
1347 p++;
1348 if (*p == '[')
1349 {
1350 level++;
1351 q=subexpression;
1352 for (p++; *p != '\0'; )
1353 {
1354 if (*p == '[')
1355 level++;
1356 else
1357 if (*p == ']')
1358 {
1359 level--;
1360 if (level == 0)
1361 break;
1362 }
1363 *q++=(*p++);
1364 }
1365 *q='\0';
1366 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1367 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001368 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001369 p++;
1370 }
1371 if (*p == '.')
1372 p++;
1373 }
1374 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1375 {
1376 p++;
1377 if (*p == '{')
1378 {
1379 level++;
1380 q=subexpression;
1381 for (p++; *p != '\0'; )
1382 {
1383 if (*p == '{')
1384 level++;
1385 else
1386 if (*p == '}')
1387 {
1388 level--;
1389 if (level == 0)
1390 break;
1391 }
1392 *q++=(*p++);
1393 }
1394 *q='\0';
1395 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1396 &beta,exception);
1397 point.x=alpha;
1398 point.y=beta;
1399 p++;
1400 }
1401 else
1402 if (*p == '[')
1403 {
1404 level++;
1405 q=subexpression;
1406 for (p++; *p != '\0'; )
1407 {
1408 if (*p == '[')
1409 level++;
1410 else
1411 if (*p == ']')
1412 {
1413 level--;
1414 if (level == 0)
1415 break;
1416 }
1417 *q++=(*p++);
1418 }
1419 *q='\0';
1420 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1421 &beta,exception);
1422 point.x+=alpha;
1423 point.y+=beta;
1424 p++;
1425 }
1426 if (*p == '.')
1427 p++;
1428 }
1429 }
1430 length=GetImageListLength(fx_info->images);
1431 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001432 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001433 i%=length;
1434 image=GetImageFromList(fx_info->images,i);
1435 if (image == (Image *) NULL)
1436 {
1437 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1438 "NoSuchImage","`%s'",expression);
1439 return(0.0);
1440 }
1441 (void) ResamplePixelColor(fx_info->resample_filter[i],point.x,point.y,&pixel);
1442 if ((strlen(p) > 2) &&
1443 (LocaleCompare(p,"intensity") != 0) &&
1444 (LocaleCompare(p,"luminance") != 0) &&
1445 (LocaleCompare(p,"hue") != 0) &&
1446 (LocaleCompare(p,"saturation") != 0) &&
1447 (LocaleCompare(p,"lightness") != 0))
1448 {
1449 char
1450 name[MaxTextExtent];
1451
1452 (void) CopyMagickString(name,p,MaxTextExtent);
1453 for (q=name+(strlen(name)-1); q > name; q--)
1454 {
1455 if (*q == ')')
1456 break;
1457 if (*q == '.')
1458 {
1459 *q='\0';
1460 break;
1461 }
1462 }
1463 if ((strlen(name) > 2) &&
1464 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1465 {
1466 MagickPixelPacket
1467 *color;
1468
1469 color=(MagickPixelPacket *) GetValueFromSplayTree(fx_info->colors,
1470 name);
1471 if (color != (MagickPixelPacket *) NULL)
1472 {
1473 pixel=(*color);
1474 p+=strlen(name);
1475 }
1476 else
1477 if (QueryMagickColor(name,&pixel,fx_info->exception) != MagickFalse)
1478 {
1479 (void) AddValueToSplayTree(fx_info->colors,ConstantString(name),
1480 CloneMagickPixelPacket(&pixel));
1481 p+=strlen(name);
1482 }
1483 }
1484 }
1485 (void) CopyMagickString(symbol,p,MaxTextExtent);
1486 StripString(symbol);
1487 if (*symbol == '\0')
1488 {
1489 switch (channel)
1490 {
1491 case RedChannel: return(QuantumScale*pixel.red);
1492 case GreenChannel: return(QuantumScale*pixel.green);
1493 case BlueChannel: return(QuantumScale*pixel.blue);
1494 case OpacityChannel:
1495 {
1496 if (pixel.matte == MagickFalse)
1497 {
1498 fx_info->matte=MagickFalse;
1499 return(1.0);
1500 }
cristy46f08202010-01-10 04:04:21 +00001501 return((MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel)));
cristy3ed852e2009-09-05 21:47:34 +00001502 }
1503 case IndexChannel:
1504 {
1505 if (image->colorspace != CMYKColorspace)
1506 {
1507 (void) ThrowMagickException(exception,GetMagickModule(),
1508 OptionError,"ColorSeparatedImageRequired","`%s'",
1509 image->filename);
1510 return(0.0);
1511 }
1512 return(QuantumScale*pixel.index);
1513 }
1514 default:
1515 break;
1516 }
1517 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1518 "UnableToParseExpression","`%s'",p);
1519 return(0.0);
1520 }
1521 switch (*symbol)
1522 {
1523 case 'A':
1524 case 'a':
1525 {
1526 if (LocaleCompare(symbol,"a") == 0)
cristy46f08202010-01-10 04:04:21 +00001527 return((MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel)));
cristy3ed852e2009-09-05 21:47:34 +00001528 break;
1529 }
1530 case 'B':
1531 case 'b':
1532 {
1533 if (LocaleCompare(symbol,"b") == 0)
1534 return(QuantumScale*pixel.blue);
1535 break;
1536 }
1537 case 'C':
1538 case 'c':
1539 {
1540 if (LocaleNCompare(symbol,"channel",7) == 0)
1541 {
1542 GeometryInfo
1543 channel_info;
1544
1545 MagickStatusType
1546 flags;
1547
1548 flags=ParseGeometry(symbol+7,&channel_info);
1549 if (image->colorspace == CMYKColorspace)
1550 switch (channel)
1551 {
1552 case CyanChannel:
1553 {
1554 if ((flags & RhoValue) == 0)
1555 return(0.0);
1556 return(channel_info.rho);
1557 }
1558 case MagentaChannel:
1559 {
1560 if ((flags & SigmaValue) == 0)
1561 return(0.0);
1562 return(channel_info.sigma);
1563 }
1564 case YellowChannel:
1565 {
1566 if ((flags & XiValue) == 0)
1567 return(0.0);
1568 return(channel_info.xi);
1569 }
1570 case BlackChannel:
1571 {
1572 if ((flags & PsiValue) == 0)
1573 return(0.0);
1574 return(channel_info.psi);
1575 }
1576 case OpacityChannel:
1577 {
1578 if ((flags & ChiValue) == 0)
1579 return(0.0);
1580 return(channel_info.chi);
1581 }
1582 default:
1583 return(0.0);
1584 }
1585 switch (channel)
1586 {
1587 case RedChannel:
1588 {
1589 if ((flags & RhoValue) == 0)
1590 return(0.0);
1591 return(channel_info.rho);
1592 }
1593 case GreenChannel:
1594 {
1595 if ((flags & SigmaValue) == 0)
1596 return(0.0);
1597 return(channel_info.sigma);
1598 }
1599 case BlueChannel:
1600 {
1601 if ((flags & XiValue) == 0)
1602 return(0.0);
1603 return(channel_info.xi);
1604 }
1605 case OpacityChannel:
1606 {
1607 if ((flags & PsiValue) == 0)
1608 return(0.0);
1609 return(channel_info.psi);
1610 }
1611 case IndexChannel:
1612 {
1613 if ((flags & ChiValue) == 0)
1614 return(0.0);
1615 return(channel_info.chi);
1616 }
1617 default:
1618 return(0.0);
1619 }
1620 return(0.0);
1621 }
1622 if (LocaleCompare(symbol,"c") == 0)
1623 return(QuantumScale*pixel.red);
1624 break;
1625 }
1626 case 'D':
1627 case 'd':
1628 {
1629 if (LocaleNCompare(symbol,"depth",5) == 0)
1630 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1631 break;
1632 }
1633 case 'G':
1634 case 'g':
1635 {
1636 if (LocaleCompare(symbol,"g") == 0)
1637 return(QuantumScale*pixel.green);
1638 break;
1639 }
1640 case 'K':
1641 case 'k':
1642 {
1643 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1644 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1645 if (LocaleCompare(symbol,"k") == 0)
1646 {
1647 if (image->colorspace != CMYKColorspace)
1648 {
1649 (void) ThrowMagickException(exception,GetMagickModule(),
1650 OptionError,"ColorSeparatedImageRequired","`%s'",
1651 image->filename);
1652 return(0.0);
1653 }
1654 return(QuantumScale*pixel.index);
1655 }
1656 break;
1657 }
1658 case 'H':
1659 case 'h':
1660 {
1661 if (LocaleCompare(symbol,"h") == 0)
1662 return((MagickRealType) image->rows);
1663 if (LocaleCompare(symbol,"hue") == 0)
1664 {
1665 double
1666 hue,
1667 lightness,
1668 saturation;
1669
cristyce70c172010-01-07 17:15:30 +00001670 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1671 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001672 return(hue);
1673 }
1674 break;
1675 }
1676 case 'I':
1677 case 'i':
1678 {
1679 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1680 (LocaleCompare(symbol,"image.minima") == 0) ||
1681 (LocaleCompare(symbol,"image.maxima") == 0) ||
1682 (LocaleCompare(symbol,"image.mean") == 0) ||
1683 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1684 (LocaleCompare(symbol,"image.skewness") == 0) ||
1685 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1686 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1687 if (LocaleCompare(symbol,"image.resolution.x") == 0)
1688 return(image->x_resolution);
1689 if (LocaleCompare(symbol,"image.resolution.y") == 0)
1690 return(image->y_resolution);
1691 if (LocaleCompare(symbol,"intensity") == 0)
1692 return(QuantumScale*MagickPixelIntensityToQuantum(&pixel));
1693 if (LocaleCompare(symbol,"i") == 0)
1694 return((MagickRealType) x);
1695 break;
1696 }
1697 case 'J':
1698 case 'j':
1699 {
1700 if (LocaleCompare(symbol,"j") == 0)
1701 return((MagickRealType) y);
1702 break;
1703 }
1704 case 'L':
1705 case 'l':
1706 {
1707 if (LocaleCompare(symbol,"lightness") == 0)
1708 {
1709 double
1710 hue,
1711 lightness,
1712 saturation;
1713
cristyce70c172010-01-07 17:15:30 +00001714 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1715 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001716 return(lightness);
1717 }
1718 if (LocaleCompare(symbol,"luminance") == 0)
1719 {
1720 double
1721 luminence;
1722
1723 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1724 return(QuantumScale*luminence);
1725 }
1726 break;
1727 }
1728 case 'M':
1729 case 'm':
1730 {
1731 if (LocaleNCompare(symbol,"maxima",6) == 0)
1732 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1733 if (LocaleNCompare(symbol,"mean",4) == 0)
1734 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1735 if (LocaleNCompare(symbol,"minima",6) == 0)
1736 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1737 if (LocaleCompare(symbol,"m") == 0)
1738 return(QuantumScale*pixel.blue);
1739 break;
1740 }
1741 case 'N':
1742 case 'n':
1743 {
1744 if (LocaleCompare(symbol,"n") == 0)
1745 return((MagickRealType) GetImageListLength(fx_info->images));
1746 break;
1747 }
1748 case 'O':
1749 case 'o':
1750 {
1751 if (LocaleCompare(symbol,"o") == 0)
1752 return(QuantumScale*pixel.opacity);
1753 break;
1754 }
1755 case 'P':
1756 case 'p':
1757 {
1758 if (LocaleCompare(symbol,"page.height") == 0)
1759 return((MagickRealType) image->page.height);
1760 if (LocaleCompare(symbol,"page.width") == 0)
1761 return((MagickRealType) image->page.width);
1762 if (LocaleCompare(symbol,"page.x") == 0)
1763 return((MagickRealType) image->page.x);
1764 if (LocaleCompare(symbol,"page.y") == 0)
1765 return((MagickRealType) image->page.y);
1766 break;
1767 }
1768 case 'R':
1769 case 'r':
1770 {
1771 if (LocaleCompare(symbol,"resolution.x") == 0)
1772 return(image->x_resolution);
1773 if (LocaleCompare(symbol,"resolution.y") == 0)
1774 return(image->y_resolution);
1775 if (LocaleCompare(symbol,"r") == 0)
1776 return(QuantumScale*pixel.red);
1777 break;
1778 }
1779 case 'S':
1780 case 's':
1781 {
1782 if (LocaleCompare(symbol,"saturation") == 0)
1783 {
1784 double
1785 hue,
1786 lightness,
1787 saturation;
1788
cristyce70c172010-01-07 17:15:30 +00001789 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1790 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001791 return(saturation);
1792 }
1793 if (LocaleNCompare(symbol,"skewness",8) == 0)
1794 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1795 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1796 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1797 break;
1798 }
1799 case 'T':
1800 case 't':
1801 {
1802 if (LocaleCompare(symbol,"t") == 0)
1803 return((MagickRealType) fx_info->images->scene);
1804 break;
1805 }
1806 case 'W':
1807 case 'w':
1808 {
1809 if (LocaleCompare(symbol,"w") == 0)
1810 return((MagickRealType) image->columns);
1811 break;
1812 }
1813 case 'Y':
1814 case 'y':
1815 {
1816 if (LocaleCompare(symbol,"y") == 0)
1817 return(QuantumScale*pixel.green);
1818 break;
1819 }
1820 case 'Z':
1821 case 'z':
1822 {
1823 if (LocaleCompare(symbol,"z") == 0)
1824 {
1825 MagickRealType
1826 depth;
1827
1828 depth=(MagickRealType) GetImageChannelDepth(image,channel,
1829 fx_info->exception);
1830 return(depth);
1831 }
1832 break;
1833 }
1834 default:
1835 break;
1836 }
1837 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1838 if (value != (const char *) NULL)
cristyf2f27272009-12-17 14:48:46 +00001839 return((MagickRealType) StringToDouble(value));
cristy3ed852e2009-09-05 21:47:34 +00001840 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1841 "UnableToParseExpression","`%s'",symbol);
1842 return(0.0);
1843}
1844
1845static const char *FxOperatorPrecedence(const char *expression,
1846 ExceptionInfo *exception)
1847{
1848 typedef enum
1849 {
1850 UndefinedPrecedence,
1851 NullPrecedence,
1852 BitwiseComplementPrecedence,
1853 ExponentPrecedence,
1854 MultiplyPrecedence,
1855 AdditionPrecedence,
1856 ShiftPrecedence,
1857 RelationalPrecedence,
1858 EquivalencyPrecedence,
1859 BitwiseAndPrecedence,
1860 BitwiseOrPrecedence,
1861 LogicalAndPrecedence,
1862 LogicalOrPrecedence,
1863 TernaryPrecedence,
1864 AssignmentPrecedence,
1865 CommaPrecedence,
1866 SeparatorPrecedence
1867 } FxPrecedence;
1868
1869 FxPrecedence
1870 precedence,
1871 target;
1872
1873 register const char
1874 *subexpression;
1875
1876 register int
1877 c;
1878
cristybb503372010-05-27 20:51:26 +00001879 size_t
cristy3ed852e2009-09-05 21:47:34 +00001880 level;
1881
1882 c=0;
1883 level=0;
1884 subexpression=(const char *) NULL;
1885 target=NullPrecedence;
1886 while (*expression != '\0')
1887 {
1888 precedence=UndefinedPrecedence;
1889 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1890 {
1891 expression++;
1892 continue;
1893 }
cristy488fa882010-03-01 22:34:24 +00001894 switch (*expression)
1895 {
1896 case 'A':
1897 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001898 {
cristy488fa882010-03-01 22:34:24 +00001899 if (LocaleNCompare(expression,"atan2",5) == 0)
1900 {
1901 expression+=5;
1902 break;
1903 }
1904 break;
cristy3ed852e2009-09-05 21:47:34 +00001905 }
cristy488fa882010-03-01 22:34:24 +00001906 case 'J':
1907 case 'j':
1908 {
1909 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1910 (LocaleNCompare(expression,"j1",2) == 0))
1911 {
1912 expression+=2;
1913 break;
1914 }
1915 break;
1916 }
cristy2def9322010-06-18 23:59:37 +00001917 case '#':
1918 {
1919 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1920 expression++;
1921 break;
1922 }
cristy488fa882010-03-01 22:34:24 +00001923 default:
1924 break;
1925 }
cristy3ed852e2009-09-05 21:47:34 +00001926 if ((c == (int) '{') || (c == (int) '['))
1927 level++;
1928 else
1929 if ((c == (int) '}') || (c == (int) ']'))
1930 level--;
1931 if (level == 0)
1932 switch ((unsigned char) *expression)
1933 {
1934 case '~':
1935 case '!':
1936 {
1937 precedence=BitwiseComplementPrecedence;
1938 break;
1939 }
1940 case '^':
1941 {
1942 precedence=ExponentPrecedence;
1943 break;
1944 }
1945 default:
1946 {
1947 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1948 (strchr(")",c) != (char *) NULL))) &&
1949 (((islower((int) ((char) *expression)) != 0) ||
1950 (strchr("(",(int) *expression) != (char *) NULL)) ||
1951 ((isdigit((int) ((char) c)) == 0) &&
1952 (isdigit((int) ((char) *expression)) != 0))) &&
1953 (strchr("xy",(int) *expression) == (char *) NULL))
1954 precedence=MultiplyPrecedence;
1955 break;
1956 }
1957 case '*':
1958 case '/':
1959 case '%':
1960 {
1961 precedence=MultiplyPrecedence;
1962 break;
1963 }
1964 case '+':
1965 case '-':
1966 {
1967 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1968 (isalpha(c) != 0))
1969 precedence=AdditionPrecedence;
1970 break;
1971 }
1972 case LeftShiftOperator:
1973 case RightShiftOperator:
1974 {
1975 precedence=ShiftPrecedence;
1976 break;
1977 }
1978 case '<':
1979 case LessThanEqualOperator:
1980 case GreaterThanEqualOperator:
1981 case '>':
1982 {
1983 precedence=RelationalPrecedence;
1984 break;
1985 }
1986 case EqualOperator:
1987 case NotEqualOperator:
1988 {
1989 precedence=EquivalencyPrecedence;
1990 break;
1991 }
1992 case '&':
1993 {
1994 precedence=BitwiseAndPrecedence;
1995 break;
1996 }
1997 case '|':
1998 {
1999 precedence=BitwiseOrPrecedence;
2000 break;
2001 }
2002 case LogicalAndOperator:
2003 {
2004 precedence=LogicalAndPrecedence;
2005 break;
2006 }
2007 case LogicalOrOperator:
2008 {
2009 precedence=LogicalOrPrecedence;
2010 break;
2011 }
2012 case ':':
2013 case '?':
2014 {
2015 precedence=TernaryPrecedence;
2016 break;
2017 }
2018 case '=':
2019 {
2020 precedence=AssignmentPrecedence;
2021 break;
2022 }
2023 case ',':
2024 {
2025 precedence=CommaPrecedence;
2026 break;
2027 }
2028 case ';':
2029 {
2030 precedence=SeparatorPrecedence;
2031 break;
2032 }
2033 }
2034 if ((precedence == BitwiseComplementPrecedence) ||
2035 (precedence == TernaryPrecedence) ||
2036 (precedence == AssignmentPrecedence))
2037 {
2038 if (precedence > target)
2039 {
2040 /*
2041 Right-to-left associativity.
2042 */
2043 target=precedence;
2044 subexpression=expression;
2045 }
2046 }
2047 else
2048 if (precedence >= target)
2049 {
2050 /*
2051 Left-to-right associativity.
2052 */
2053 target=precedence;
2054 subexpression=expression;
2055 }
2056 if (strchr("(",(int) *expression) != (char *) NULL)
2057 expression=FxSubexpression(expression,exception);
2058 c=(int) (*expression++);
2059 }
2060 return(subexpression);
2061}
2062
2063static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristye85007d2010-06-06 22:51:36 +00002064 const ChannelType channel,const ssize_t x,const ssize_t y,
2065 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002066{
2067 char
2068 *q,
2069 subexpression[MaxTextExtent];
2070
2071 MagickRealType
2072 alpha,
2073 gamma;
2074
2075 register const char
2076 *p;
2077
2078 *beta=0.0;
2079 if (exception->severity != UndefinedException)
2080 return(0.0);
2081 while (isspace((int) *expression) != 0)
2082 expression++;
2083 if (*expression == '\0')
2084 {
2085 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2086 "MissingExpression","`%s'",expression);
2087 return(0.0);
2088 }
cristy66322f02010-05-17 11:40:48 +00002089 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002090 p=FxOperatorPrecedence(expression,exception);
2091 if (p != (const char *) NULL)
2092 {
2093 (void) CopyMagickString(subexpression,expression,(size_t)
2094 (p-expression+1));
2095 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2096 exception);
2097 switch ((unsigned char) *p)
2098 {
2099 case '~':
2100 {
2101 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002102 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002103 return(*beta);
2104 }
2105 case '!':
2106 {
2107 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2108 return(*beta == 0.0 ? 1.0 : 0.0);
2109 }
2110 case '^':
2111 {
2112 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2113 channel,x,y,++p,beta,exception));
2114 return(*beta);
2115 }
2116 case '*':
2117 {
2118 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2119 return(alpha*(*beta));
2120 }
2121 case '/':
2122 {
2123 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2124 if (*beta == 0.0)
2125 {
2126 if (exception->severity == UndefinedException)
2127 (void) ThrowMagickException(exception,GetMagickModule(),
2128 OptionError,"DivideByZero","`%s'",expression);
2129 return(0.0);
2130 }
2131 return(alpha/(*beta));
2132 }
2133 case '%':
2134 {
2135 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2136 *beta=fabs(floor(((double) *beta)+0.5));
2137 if (*beta == 0.0)
2138 {
2139 (void) ThrowMagickException(exception,GetMagickModule(),
2140 OptionError,"DivideByZero","`%s'",expression);
2141 return(0.0);
2142 }
2143 return(fmod((double) alpha,(double) *beta));
2144 }
2145 case '+':
2146 {
2147 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2148 return(alpha+(*beta));
2149 }
2150 case '-':
2151 {
2152 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2153 return(alpha-(*beta));
2154 }
2155 case LeftShiftOperator:
2156 {
2157 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002158 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002159 (gamma+0.5));
2160 return(*beta);
2161 }
2162 case RightShiftOperator:
2163 {
2164 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002165 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002166 (gamma+0.5));
2167 return(*beta);
2168 }
2169 case '<':
2170 {
2171 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2172 return(alpha < *beta ? 1.0 : 0.0);
2173 }
2174 case LessThanEqualOperator:
2175 {
2176 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2177 return(alpha <= *beta ? 1.0 : 0.0);
2178 }
2179 case '>':
2180 {
2181 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2182 return(alpha > *beta ? 1.0 : 0.0);
2183 }
2184 case GreaterThanEqualOperator:
2185 {
2186 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2187 return(alpha >= *beta ? 1.0 : 0.0);
2188 }
2189 case EqualOperator:
2190 {
2191 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2192 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2193 }
2194 case NotEqualOperator:
2195 {
2196 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2197 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2198 }
2199 case '&':
2200 {
2201 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002202 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002203 (gamma+0.5));
2204 return(*beta);
2205 }
2206 case '|':
2207 {
2208 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002209 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002210 (gamma+0.5));
2211 return(*beta);
2212 }
2213 case LogicalAndOperator:
2214 {
2215 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2216 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2217 return(*beta);
2218 }
2219 case LogicalOrOperator:
2220 {
2221 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2222 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2223 return(*beta);
2224 }
2225 case '?':
2226 {
2227 MagickRealType
2228 gamma;
2229
2230 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2231 q=subexpression;
2232 p=StringToken(":",&q);
2233 if (q == (char *) NULL)
2234 {
2235 (void) ThrowMagickException(exception,GetMagickModule(),
2236 OptionError,"UnableToParseExpression","`%s'",subexpression);
2237 return(0.0);
2238 }
2239 if (fabs((double) alpha) > MagickEpsilon)
2240 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2241 else
2242 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2243 return(gamma);
2244 }
2245 case '=':
2246 {
2247 char
2248 numeric[MaxTextExtent];
2249
2250 q=subexpression;
2251 while (isalpha((int) ((unsigned char) *q)) != 0)
2252 q++;
2253 if (*q != '\0')
2254 {
2255 (void) ThrowMagickException(exception,GetMagickModule(),
2256 OptionError,"UnableToParseExpression","`%s'",subexpression);
2257 return(0.0);
2258 }
2259 ClearMagickException(exception);
2260 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristye7f51092010-01-17 00:39:37 +00002261 (void) FormatMagickString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002262 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002263 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2264 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2265 subexpression),ConstantString(numeric));
2266 return(*beta);
2267 }
2268 case ',':
2269 {
2270 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2271 return(alpha);
2272 }
2273 case ';':
2274 {
2275 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2276 return(*beta);
2277 }
2278 default:
2279 {
2280 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2281 exception);
2282 return(gamma);
2283 }
2284 }
2285 }
2286 if (strchr("(",(int) *expression) != (char *) NULL)
2287 {
2288 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2289 subexpression[strlen(subexpression)-1]='\0';
2290 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2291 exception);
2292 return(gamma);
2293 }
2294 switch (*expression)
2295 {
2296 case '+':
2297 {
2298 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2299 exception);
2300 return(1.0*gamma);
2301 }
2302 case '-':
2303 {
2304 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2305 exception);
2306 return(-1.0*gamma);
2307 }
2308 case '~':
2309 {
2310 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2311 exception);
cristybb503372010-05-27 20:51:26 +00002312 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002313 }
2314 case 'A':
2315 case 'a':
2316 {
2317 if (LocaleNCompare(expression,"abs",3) == 0)
2318 {
2319 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2320 exception);
2321 return((MagickRealType) fabs((double) alpha));
2322 }
2323 if (LocaleNCompare(expression,"acos",4) == 0)
2324 {
2325 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2326 exception);
2327 return((MagickRealType) acos((double) alpha));
2328 }
cristy43c22f42010-03-30 12:34:07 +00002329#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002330 if (LocaleNCompare(expression,"airy",4) == 0)
2331 {
2332 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2333 exception);
2334 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002335 return(1.0);
2336 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002337 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002338 }
cristy43c22f42010-03-30 12:34:07 +00002339#endif
cristy3ed852e2009-09-05 21:47:34 +00002340 if (LocaleNCompare(expression,"asin",4) == 0)
2341 {
2342 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2343 exception);
2344 return((MagickRealType) asin((double) alpha));
2345 }
2346 if (LocaleNCompare(expression,"alt",3) == 0)
2347 {
2348 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2349 exception);
cristybb503372010-05-27 20:51:26 +00002350 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002351 }
2352 if (LocaleNCompare(expression,"atan2",5) == 0)
2353 {
2354 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2355 exception);
2356 return((MagickRealType) atan2((double) alpha,(double) *beta));
2357 }
2358 if (LocaleNCompare(expression,"atan",4) == 0)
2359 {
2360 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2361 exception);
2362 return((MagickRealType) atan((double) alpha));
2363 }
2364 if (LocaleCompare(expression,"a") == 0)
2365 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2366 break;
2367 }
2368 case 'B':
2369 case 'b':
2370 {
2371 if (LocaleCompare(expression,"b") == 0)
2372 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2373 break;
2374 }
2375 case 'C':
2376 case 'c':
2377 {
2378 if (LocaleNCompare(expression,"ceil",4) == 0)
2379 {
2380 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2381 exception);
2382 return((MagickRealType) ceil((double) alpha));
2383 }
2384 if (LocaleNCompare(expression,"cosh",4) == 0)
2385 {
2386 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2387 exception);
2388 return((MagickRealType) cosh((double) alpha));
2389 }
2390 if (LocaleNCompare(expression,"cos",3) == 0)
2391 {
2392 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2393 exception);
2394 return((MagickRealType) cos((double) alpha));
2395 }
2396 if (LocaleCompare(expression,"c") == 0)
2397 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2398 break;
2399 }
2400 case 'D':
2401 case 'd':
2402 {
2403 if (LocaleNCompare(expression,"debug",5) == 0)
2404 {
2405 const char
2406 *type;
2407
2408 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2409 exception);
2410 if (fx_info->images->colorspace == CMYKColorspace)
2411 switch (channel)
2412 {
2413 case CyanChannel: type="cyan"; break;
2414 case MagentaChannel: type="magenta"; break;
2415 case YellowChannel: type="yellow"; break;
2416 case OpacityChannel: type="opacity"; break;
2417 case BlackChannel: type="black"; break;
2418 default: type="unknown"; break;
2419 }
2420 else
2421 switch (channel)
2422 {
2423 case RedChannel: type="red"; break;
2424 case GreenChannel: type="green"; break;
2425 case BlueChannel: type="blue"; break;
2426 case OpacityChannel: type="opacity"; break;
2427 default: type="unknown"; break;
2428 }
2429 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2430 if (strlen(subexpression) > 1)
2431 subexpression[strlen(subexpression)-1]='\0';
2432 if (fx_info->file != (FILE *) NULL)
cristye8c25f92010-06-03 00:53:06 +00002433 (void) fprintf(fx_info->file,"%s[%.20g,%.20g].%s: %s=%.*g\n",
2434 fx_info->images->filename,(double) x,(double) y,type,
2435 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002436 return(0.0);
2437 }
2438 break;
2439 }
2440 case 'E':
2441 case 'e':
2442 {
2443 if (LocaleCompare(expression,"epsilon") == 0)
2444 return((MagickRealType) MagickEpsilon);
2445 if (LocaleNCompare(expression,"exp",3) == 0)
2446 {
2447 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2448 exception);
2449 return((MagickRealType) exp((double) alpha));
2450 }
2451 if (LocaleCompare(expression,"e") == 0)
2452 return((MagickRealType) 2.7182818284590452354);
2453 break;
2454 }
2455 case 'F':
2456 case 'f':
2457 {
2458 if (LocaleNCompare(expression,"floor",5) == 0)
2459 {
2460 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2461 exception);
2462 return((MagickRealType) floor((double) alpha));
2463 }
2464 break;
2465 }
2466 case 'G':
2467 case 'g':
2468 {
2469 if (LocaleCompare(expression,"g") == 0)
2470 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2471 break;
2472 }
2473 case 'H':
2474 case 'h':
2475 {
2476 if (LocaleCompare(expression,"h") == 0)
2477 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2478 if (LocaleCompare(expression,"hue") == 0)
2479 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2480 if (LocaleNCompare(expression,"hypot",5) == 0)
2481 {
2482 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2483 exception);
2484 return((MagickRealType) hypot((double) alpha,(double) *beta));
2485 }
2486 break;
2487 }
2488 case 'K':
2489 case 'k':
2490 {
2491 if (LocaleCompare(expression,"k") == 0)
2492 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2493 break;
2494 }
2495 case 'I':
2496 case 'i':
2497 {
2498 if (LocaleCompare(expression,"intensity") == 0)
2499 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2500 if (LocaleNCompare(expression,"int",3) == 0)
2501 {
2502 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2503 exception);
2504 return((MagickRealType) floor(alpha+0.5));
2505 }
2506 if (LocaleCompare(expression,"i") == 0)
2507 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2508 break;
2509 }
2510 case 'J':
2511 case 'j':
2512 {
2513 if (LocaleCompare(expression,"j") == 0)
2514 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002515#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002516 if (LocaleNCompare(expression,"j0",2) == 0)
2517 {
2518 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2519 exception);
2520 return((MagickRealType) j0((double) alpha));
2521 }
cristy161b9262010-03-20 19:34:32 +00002522#endif
2523#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002524 if (LocaleNCompare(expression,"j1",2) == 0)
2525 {
2526 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2527 exception);
2528 return((MagickRealType) j1((double) alpha));
2529 }
cristy161b9262010-03-20 19:34:32 +00002530#endif
cristyaa018fa2010-04-08 23:03:54 +00002531#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002532 if (LocaleNCompare(expression,"jinc",4) == 0)
2533 {
2534 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2535 exception);
cristy0946a822010-03-12 17:14:58 +00002536 if (alpha == 0.0)
2537 return(1.0);
cristy69928f92010-03-12 13:27:09 +00002538 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/
cristyfce2f7b2010-03-12 00:29:49 +00002539 (MagickPI*alpha));
2540 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002541 }
cristyaa018fa2010-04-08 23:03:54 +00002542#endif
cristy3ed852e2009-09-05 21:47:34 +00002543 break;
2544 }
2545 case 'L':
2546 case 'l':
2547 {
2548 if (LocaleNCompare(expression,"ln",2) == 0)
2549 {
2550 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2551 exception);
2552 return((MagickRealType) log((double) alpha));
2553 }
2554 if (LocaleNCompare(expression,"logtwo",4) == 0)
2555 {
2556 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2557 exception);
2558 return((MagickRealType) log10((double) alpha))/log10(2.0);
2559 }
2560 if (LocaleNCompare(expression,"log",3) == 0)
2561 {
2562 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2563 exception);
2564 return((MagickRealType) log10((double) alpha));
2565 }
2566 if (LocaleCompare(expression,"lightness") == 0)
2567 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2568 break;
2569 }
2570 case 'M':
2571 case 'm':
2572 {
2573 if (LocaleCompare(expression,"MaxRGB") == 0)
2574 return((MagickRealType) QuantumRange);
2575 if (LocaleNCompare(expression,"maxima",6) == 0)
2576 break;
2577 if (LocaleNCompare(expression,"max",3) == 0)
2578 return(FxMax(fx_info,channel,x,y,expression+3,exception));
2579 if (LocaleNCompare(expression,"minima",6) == 0)
2580 break;
2581 if (LocaleNCompare(expression,"min",3) == 0)
2582 return(FxMin(fx_info,channel,x,y,expression+3,exception));
2583 if (LocaleNCompare(expression,"mod",3) == 0)
2584 {
2585 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2586 exception);
2587 return((MagickRealType) fmod((double) alpha,(double) *beta));
2588 }
2589 if (LocaleCompare(expression,"m") == 0)
2590 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2591 break;
2592 }
2593 case 'N':
2594 case 'n':
2595 {
2596 if (LocaleCompare(expression,"n") == 0)
2597 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2598 break;
2599 }
2600 case 'O':
2601 case 'o':
2602 {
2603 if (LocaleCompare(expression,"Opaque") == 0)
2604 return(1.0);
2605 if (LocaleCompare(expression,"o") == 0)
2606 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2607 break;
2608 }
2609 case 'P':
2610 case 'p':
2611 {
2612 if (LocaleCompare(expression,"pi") == 0)
2613 return((MagickRealType) MagickPI);
2614 if (LocaleNCompare(expression,"pow",3) == 0)
2615 {
2616 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2617 exception);
2618 return((MagickRealType) pow((double) alpha,(double) *beta));
2619 }
2620 if (LocaleCompare(expression,"p") == 0)
2621 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2622 break;
2623 }
2624 case 'Q':
2625 case 'q':
2626 {
2627 if (LocaleCompare(expression,"QuantumRange") == 0)
2628 return((MagickRealType) QuantumRange);
2629 if (LocaleCompare(expression,"QuantumScale") == 0)
2630 return((MagickRealType) QuantumScale);
2631 break;
2632 }
2633 case 'R':
2634 case 'r':
2635 {
2636 if (LocaleNCompare(expression,"rand",4) == 0)
2637 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2638 if (LocaleNCompare(expression,"round",5) == 0)
2639 {
2640 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2641 exception);
2642 if (alpha >= 0.0)
2643 return((MagickRealType) floor((double) alpha+0.5));
2644 return((MagickRealType) ceil((double) alpha-0.5));
2645 }
2646 if (LocaleCompare(expression,"r") == 0)
2647 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2648 break;
2649 }
2650 case 'S':
2651 case 's':
2652 {
2653 if (LocaleCompare(expression,"saturation") == 0)
2654 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2655 if (LocaleNCompare(expression,"sign",4) == 0)
2656 {
2657 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2658 exception);
2659 return(alpha < 0.0 ? -1.0 : 1.0);
2660 }
cristya6a09e72010-03-02 14:51:02 +00002661 if (LocaleNCompare(expression,"sinc",4) == 0)
2662 {
2663 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2664 exception);
2665 if (alpha == 0)
2666 return(1.0);
2667 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2668 (MagickPI*alpha));
2669 return(gamma);
2670 }
cristy3ed852e2009-09-05 21:47:34 +00002671 if (LocaleNCompare(expression,"sinh",4) == 0)
2672 {
2673 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2674 exception);
2675 return((MagickRealType) sinh((double) alpha));
2676 }
2677 if (LocaleNCompare(expression,"sin",3) == 0)
2678 {
2679 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2680 exception);
2681 return((MagickRealType) sin((double) alpha));
2682 }
2683 if (LocaleNCompare(expression,"sqrt",4) == 0)
2684 {
2685 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2686 exception);
2687 return((MagickRealType) sqrt((double) alpha));
2688 }
2689 if (LocaleCompare(expression,"s") == 0)
2690 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2691 break;
2692 }
2693 case 'T':
2694 case 't':
2695 {
2696 if (LocaleNCompare(expression,"tanh",4) == 0)
2697 {
2698 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2699 exception);
2700 return((MagickRealType) tanh((double) alpha));
2701 }
2702 if (LocaleNCompare(expression,"tan",3) == 0)
2703 {
2704 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2705 exception);
2706 return((MagickRealType) tan((double) alpha));
2707 }
2708 if (LocaleCompare(expression,"Transparent") == 0)
2709 return(0.0);
2710 if (LocaleCompare(expression,"t") == 0)
2711 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2712 break;
2713 }
2714 case 'U':
2715 case 'u':
2716 {
2717 if (LocaleCompare(expression,"u") == 0)
2718 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2719 break;
2720 }
2721 case 'V':
2722 case 'v':
2723 {
2724 if (LocaleCompare(expression,"v") == 0)
2725 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2726 break;
2727 }
2728 case 'W':
2729 case 'w':
2730 {
2731 if (LocaleCompare(expression,"w") == 0)
2732 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2733 break;
2734 }
2735 case 'Y':
2736 case 'y':
2737 {
2738 if (LocaleCompare(expression,"y") == 0)
2739 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2740 break;
2741 }
2742 case 'Z':
2743 case 'z':
2744 {
2745 if (LocaleCompare(expression,"z") == 0)
2746 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2747 break;
2748 }
2749 default:
2750 break;
2751 }
2752 q=(char *) expression;
2753 alpha=strtod(expression,&q);
2754 if (q == expression)
2755 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2756 return(alpha);
2757}
2758
2759MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2760 MagickRealType *alpha,ExceptionInfo *exception)
2761{
2762 MagickBooleanType
2763 status;
2764
2765 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2766 return(status);
2767}
2768
2769MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2770 MagickRealType *alpha,ExceptionInfo *exception)
2771{
2772 FILE
2773 *file;
2774
2775 MagickBooleanType
2776 status;
2777
2778 file=fx_info->file;
2779 fx_info->file=(FILE *) NULL;
2780 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2781 fx_info->file=file;
2782 return(status);
2783}
2784
2785MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristye85007d2010-06-06 22:51:36 +00002786 const ChannelType channel,const ssize_t x,const ssize_t y,
2787 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002788{
2789 MagickRealType
2790 beta;
2791
2792 beta=0.0;
2793 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2794 exception);
2795 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2796}
2797
2798/*
2799%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2800% %
2801% %
2802% %
2803% F x I m a g e %
2804% %
2805% %
2806% %
2807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2808%
2809% FxImage() applies a mathematical expression to the specified image.
2810%
2811% The format of the FxImage method is:
2812%
2813% Image *FxImage(const Image *image,const char *expression,
2814% ExceptionInfo *exception)
2815% Image *FxImageChannel(const Image *image,const ChannelType channel,
2816% const char *expression,ExceptionInfo *exception)
2817%
2818% A description of each parameter follows:
2819%
2820% o image: the image.
2821%
2822% o channel: the channel.
2823%
2824% o expression: A mathematical expression.
2825%
2826% o exception: return any errors or warnings in this structure.
2827%
2828*/
2829
2830static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2831{
cristybb503372010-05-27 20:51:26 +00002832 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002833 i;
2834
2835 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002836 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002837 if (fx_info[i] != (FxInfo *) NULL)
2838 fx_info[i]=DestroyFxInfo(fx_info[i]);
2839 fx_info=(FxInfo **) RelinquishAlignedMemory(fx_info);
2840 return(fx_info);
2841}
2842
2843static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2844 ExceptionInfo *exception)
2845{
2846 char
2847 *fx_expression;
2848
2849 FxInfo
2850 **fx_info;
2851
2852 MagickRealType
2853 alpha;
2854
cristybb503372010-05-27 20:51:26 +00002855 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002856 i;
2857
cristybb503372010-05-27 20:51:26 +00002858 size_t
cristy3ed852e2009-09-05 21:47:34 +00002859 number_threads;
2860
2861 number_threads=GetOpenMPMaximumThreads();
2862 fx_info=(FxInfo **) AcquireAlignedMemory(number_threads,sizeof(*fx_info));
2863 if (fx_info == (FxInfo **) NULL)
2864 return((FxInfo **) NULL);
2865 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2866 if (*expression != '@')
2867 fx_expression=ConstantString(expression);
2868 else
2869 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00002870 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002871 {
2872 fx_info[i]=AcquireFxInfo(image,fx_expression);
2873 if (fx_info[i] == (FxInfo *) NULL)
2874 return(DestroyFxThreadSet(fx_info));
2875 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2876 }
2877 fx_expression=DestroyString(fx_expression);
2878 return(fx_info);
2879}
2880
2881MagickExport Image *FxImage(const Image *image,const char *expression,
2882 ExceptionInfo *exception)
2883{
2884 Image
2885 *fx_image;
2886
2887 fx_image=FxImageChannel(image,GrayChannel,expression,exception);
2888 return(fx_image);
2889}
2890
2891MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
2892 const char *expression,ExceptionInfo *exception)
2893{
2894#define FxImageTag "Fx/Image"
2895
cristyfa112112010-01-04 17:48:07 +00002896 CacheView
2897 *fx_view;
2898
cristy3ed852e2009-09-05 21:47:34 +00002899 FxInfo
cristyfa112112010-01-04 17:48:07 +00002900 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00002901
2902 Image
2903 *fx_image;
2904
cristy3ed852e2009-09-05 21:47:34 +00002905 MagickBooleanType
2906 status;
2907
cristybb503372010-05-27 20:51:26 +00002908 MagickOffsetType
2909 progress;
2910
cristy3ed852e2009-09-05 21:47:34 +00002911 MagickRealType
2912 alpha;
2913
cristybb503372010-05-27 20:51:26 +00002914 ssize_t
2915 y;
2916
cristy3ed852e2009-09-05 21:47:34 +00002917 assert(image != (Image *) NULL);
2918 assert(image->signature == MagickSignature);
2919 if (image->debug != MagickFalse)
2920 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2921 fx_image=CloneImage(image,0,0,MagickTrue,exception);
2922 if (fx_image == (Image *) NULL)
2923 return((Image *) NULL);
2924 if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
2925 {
2926 InheritException(exception,&fx_image->exception);
2927 fx_image=DestroyImage(fx_image);
2928 return((Image *) NULL);
2929 }
2930 fx_info=AcquireFxThreadSet(image,expression,exception);
2931 if (fx_info == (FxInfo **) NULL)
2932 {
2933 fx_image=DestroyImage(fx_image);
2934 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2935 }
2936 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
2937 if (status == MagickFalse)
2938 {
2939 fx_image=DestroyImage(fx_image);
2940 fx_info=DestroyFxThreadSet(fx_info);
2941 return((Image *) NULL);
2942 }
2943 /*
2944 Fx image.
2945 */
2946 status=MagickTrue;
2947 progress=0;
2948 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00002949#if defined(MAGICKCORE_OPENMP_SUPPORT)
2950 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002951#endif
cristybb503372010-05-27 20:51:26 +00002952 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002953 {
cristy6ebe97c2010-07-03 01:17:28 +00002954 int
2955 id;
2956
cristy3ed852e2009-09-05 21:47:34 +00002957 MagickRealType
2958 alpha;
2959
2960 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002961 *restrict fx_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002962
cristybb503372010-05-27 20:51:26 +00002963 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002964 x;
2965
2966 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002967 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002968
2969 if (status == MagickFalse)
2970 continue;
2971 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
2972 if (q == (PixelPacket *) NULL)
2973 {
2974 status=MagickFalse;
2975 continue;
2976 }
2977 fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
2978 id=GetOpenMPThreadId();
2979 alpha=0.0;
cristybb503372010-05-27 20:51:26 +00002980 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002981 {
2982 if ((channel & RedChannel) != 0)
2983 {
2984 (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
2985 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00002986 q->red=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00002987 }
2988 if ((channel & GreenChannel) != 0)
2989 {
2990 (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
2991 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00002992 q->green=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00002993 }
2994 if ((channel & BlueChannel) != 0)
2995 {
2996 (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
2997 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00002998 q->blue=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00002999 }
3000 if ((channel & OpacityChannel) != 0)
3001 {
3002 (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
3003 &alpha,exception);
3004 if (image->matte == MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003005 q->opacity=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00003006 else
cristyce70c172010-01-07 17:15:30 +00003007 q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
cristy3ed852e2009-09-05 21:47:34 +00003008 QuantumRange*alpha));
3009 }
3010 if (((channel & IndexChannel) != 0) &&
3011 (fx_image->colorspace == CMYKColorspace))
3012 {
3013 (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
3014 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00003015 fx_indexes[x]=(IndexPacket) ClampToQuantum((MagickRealType)
cristy3ed852e2009-09-05 21:47:34 +00003016 QuantumRange*alpha);
3017 }
3018 q++;
3019 }
3020 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3021 status=MagickFalse;
3022 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3023 {
3024 MagickBooleanType
3025 proceed;
3026
cristyb5d5f722009-11-04 03:03:49 +00003027#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003028 #pragma omp critical (MagickCore_FxImageChannel)
3029#endif
3030 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3031 if (proceed == MagickFalse)
3032 status=MagickFalse;
3033 }
3034 }
3035 fx_image->matte=fx_info[0]->matte;
3036 fx_view=DestroyCacheView(fx_view);
3037 fx_info=DestroyFxThreadSet(fx_info);
3038 if (status == MagickFalse)
3039 fx_image=DestroyImage(fx_image);
3040 return(fx_image);
3041}
3042
3043/*
3044%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3045% %
3046% %
3047% %
3048% I m p l o d e I m a g e %
3049% %
3050% %
3051% %
3052%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3053%
3054% ImplodeImage() creates a new image that is a copy of an existing
3055% one with the image pixels "implode" by the specified percentage. It
3056% allocates the memory necessary for the new Image structure and returns a
3057% pointer to the new image.
3058%
3059% The format of the ImplodeImage method is:
3060%
3061% Image *ImplodeImage(const Image *image,const double amount,
3062% ExceptionInfo *exception)
3063%
3064% A description of each parameter follows:
3065%
3066% o implode_image: Method ImplodeImage returns a pointer to the image
3067% after it is implode. A null image is returned if there is a memory
3068% shortage.
3069%
3070% o image: the image.
3071%
3072% o amount: Define the extent of the implosion.
3073%
3074% o exception: return any errors or warnings in this structure.
3075%
3076*/
3077MagickExport Image *ImplodeImage(const Image *image,const double amount,
3078 ExceptionInfo *exception)
3079{
3080#define ImplodeImageTag "Implode/Image"
3081
cristyfa112112010-01-04 17:48:07 +00003082 CacheView
3083 *image_view,
3084 *implode_view;
3085
cristy3ed852e2009-09-05 21:47:34 +00003086 Image
3087 *implode_image;
3088
cristy3ed852e2009-09-05 21:47:34 +00003089 MagickBooleanType
3090 status;
3091
cristybb503372010-05-27 20:51:26 +00003092 MagickOffsetType
3093 progress;
3094
cristy3ed852e2009-09-05 21:47:34 +00003095 MagickPixelPacket
3096 zero;
3097
3098 MagickRealType
3099 radius;
3100
3101 PointInfo
3102 center,
3103 scale;
3104
3105 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00003106 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00003107
cristybb503372010-05-27 20:51:26 +00003108 ssize_t
3109 y;
3110
cristy3ed852e2009-09-05 21:47:34 +00003111 /*
3112 Initialize implode image attributes.
3113 */
3114 assert(image != (Image *) NULL);
3115 assert(image->signature == MagickSignature);
3116 if (image->debug != MagickFalse)
3117 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3118 assert(exception != (ExceptionInfo *) NULL);
3119 assert(exception->signature == MagickSignature);
3120 implode_image=CloneImage(image,0,0,MagickTrue,exception);
3121 if (implode_image == (Image *) NULL)
3122 return((Image *) NULL);
3123 if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
3124 {
3125 InheritException(exception,&implode_image->exception);
3126 implode_image=DestroyImage(implode_image);
3127 return((Image *) NULL);
3128 }
3129 if (implode_image->background_color.opacity != OpaqueOpacity)
3130 implode_image->matte=MagickTrue;
3131 /*
3132 Compute scaling factor.
3133 */
3134 scale.x=1.0;
3135 scale.y=1.0;
3136 center.x=0.5*image->columns;
3137 center.y=0.5*image->rows;
3138 radius=center.x;
3139 if (image->columns > image->rows)
3140 scale.y=(double) image->columns/(double) image->rows;
3141 else
3142 if (image->columns < image->rows)
3143 {
3144 scale.x=(double) image->rows/(double) image->columns;
3145 radius=center.y;
3146 }
3147 /*
3148 Implode image.
3149 */
3150 status=MagickTrue;
3151 progress=0;
3152 GetMagickPixelPacket(implode_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00003153 resample_filter=AcquireResampleFilterThreadSet(image,
3154 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003155 image_view=AcquireCacheView(image);
3156 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003157#if defined(MAGICKCORE_OPENMP_SUPPORT)
3158 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003159#endif
cristybb503372010-05-27 20:51:26 +00003160 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003161 {
cristy6ebe97c2010-07-03 01:17:28 +00003162 int
3163 id;
3164
cristy3ed852e2009-09-05 21:47:34 +00003165 MagickPixelPacket
3166 pixel;
3167
3168 MagickRealType
3169 distance;
3170
3171 PointInfo
3172 delta;
3173
3174 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003175 *restrict implode_indexes;
cristy3ed852e2009-09-05 21:47:34 +00003176
cristybb503372010-05-27 20:51:26 +00003177 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003178 x;
3179
3180 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003181 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003182
3183 if (status == MagickFalse)
3184 continue;
3185 q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3186 exception);
3187 if (q == (PixelPacket *) NULL)
3188 {
3189 status=MagickFalse;
3190 continue;
3191 }
3192 implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
3193 delta.y=scale.y*(double) (y-center.y);
3194 pixel=zero;
3195 id=GetOpenMPThreadId();
cristybb503372010-05-27 20:51:26 +00003196 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003197 {
3198 /*
3199 Determine if the pixel is within an ellipse.
3200 */
3201 delta.x=scale.x*(double) (x-center.x);
3202 distance=delta.x*delta.x+delta.y*delta.y;
3203 if (distance < (radius*radius))
3204 {
3205 double
3206 factor;
3207
3208 /*
3209 Implode the pixel.
3210 */
3211 factor=1.0;
3212 if (distance > 0.0)
3213 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3214 radius/2)),-amount);
3215 (void) ResamplePixelColor(resample_filter[id],(double)
3216 (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3217 scale.y+center.y),&pixel);
3218 SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
3219 }
3220 q++;
3221 }
3222 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3223 status=MagickFalse;
3224 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3225 {
3226 MagickBooleanType
3227 proceed;
3228
cristyb5d5f722009-11-04 03:03:49 +00003229#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003230 #pragma omp critical (MagickCore_ImplodeImage)
3231#endif
3232 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3233 if (proceed == MagickFalse)
3234 status=MagickFalse;
3235 }
3236 }
3237 implode_view=DestroyCacheView(implode_view);
3238 image_view=DestroyCacheView(image_view);
3239 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
3240 if (status == MagickFalse)
3241 implode_image=DestroyImage(implode_image);
3242 return(implode_image);
3243}
3244
3245/*
3246%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3247% %
3248% %
3249% %
3250% M o r p h I m a g e s %
3251% %
3252% %
3253% %
3254%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3255%
3256% The MorphImages() method requires a minimum of two images. The first
3257% image is transformed into the second by a number of intervening images
3258% as specified by frames.
3259%
3260% The format of the MorphImage method is:
3261%
cristybb503372010-05-27 20:51:26 +00003262% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003263% ExceptionInfo *exception)
3264%
3265% A description of each parameter follows:
3266%
3267% o image: the image.
3268%
3269% o number_frames: Define the number of in-between image to generate.
3270% The more in-between frames, the smoother the morph.
3271%
3272% o exception: return any errors or warnings in this structure.
3273%
3274*/
3275MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003276 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003277{
3278#define MorphImageTag "Morph/Image"
3279
3280 Image
3281 *morph_image,
3282 *morph_images;
3283
cristybb503372010-05-27 20:51:26 +00003284 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003285 y;
3286
3287 MagickOffsetType
3288 scene;
3289
3290 MagickRealType
3291 alpha,
3292 beta;
3293
3294 register const Image
3295 *next;
3296
cristybb503372010-05-27 20:51:26 +00003297 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003298 i;
3299
3300 MagickBooleanType
3301 status;
3302
3303 /*
3304 Clone first frame in sequence.
3305 */
3306 assert(image != (Image *) NULL);
3307 assert(image->signature == MagickSignature);
3308 if (image->debug != MagickFalse)
3309 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3310 assert(exception != (ExceptionInfo *) NULL);
3311 assert(exception->signature == MagickSignature);
3312 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3313 if (morph_images == (Image *) NULL)
3314 return((Image *) NULL);
3315 if (GetNextImageInList(image) == (Image *) NULL)
3316 {
3317 /*
3318 Morph single image.
3319 */
cristybb503372010-05-27 20:51:26 +00003320 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003321 {
3322 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3323 if (morph_image == (Image *) NULL)
3324 {
3325 morph_images=DestroyImageList(morph_images);
3326 return((Image *) NULL);
3327 }
3328 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003329 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003330 {
cristy8b27a6d2010-02-14 03:31:15 +00003331 MagickBooleanType
3332 proceed;
3333
cristybb503372010-05-27 20:51:26 +00003334 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3335 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003336 if (proceed == MagickFalse)
3337 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003338 }
3339 }
3340 return(GetFirstImageInList(morph_images));
3341 }
3342 /*
3343 Morph image sequence.
3344 */
3345 status=MagickTrue;
3346 scene=0;
3347 next=image;
3348 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3349 {
cristybb503372010-05-27 20:51:26 +00003350 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003351 {
3352 CacheView
3353 *image_view,
3354 *morph_view;
3355
3356 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3357 alpha=1.0-beta;
cristybb503372010-05-27 20:51:26 +00003358 morph_image=ZoomImage(next,(size_t) (alpha*next->columns+beta*
3359 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
cristy3ed852e2009-09-05 21:47:34 +00003360 next->rows+beta*GetNextImageInList(next)->rows+0.5),exception);
3361 if (morph_image == (Image *) NULL)
3362 {
3363 morph_images=DestroyImageList(morph_images);
3364 return((Image *) NULL);
3365 }
3366 if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
3367 {
3368 InheritException(exception,&morph_image->exception);
3369 morph_image=DestroyImage(morph_image);
3370 return((Image *) NULL);
3371 }
3372 AppendImageToList(&morph_images,morph_image);
3373 morph_images=GetLastImageInList(morph_images);
3374 morph_image=ZoomImage(GetNextImageInList(next),morph_images->columns,
3375 morph_images->rows,exception);
3376 if (morph_image == (Image *) NULL)
3377 {
3378 morph_images=DestroyImageList(morph_images);
3379 return((Image *) NULL);
3380 }
3381 image_view=AcquireCacheView(morph_image);
3382 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003383#if defined(MAGICKCORE_OPENMP_SUPPORT)
3384 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003385#endif
cristybb503372010-05-27 20:51:26 +00003386 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003387 {
3388 MagickBooleanType
3389 sync;
3390
3391 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003392 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003393
cristybb503372010-05-27 20:51:26 +00003394 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003395 x;
3396
3397 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003398 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003399
3400 if (status == MagickFalse)
3401 continue;
3402 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3403 exception);
3404 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3405 exception);
3406 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3407 {
3408 status=MagickFalse;
3409 continue;
3410 }
cristybb503372010-05-27 20:51:26 +00003411 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003412 {
cristyce70c172010-01-07 17:15:30 +00003413 q->red=ClampToQuantum(alpha*q->red+beta*GetRedPixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00003414 q->green=ClampToQuantum(alpha*q->green+beta*
3415 GetGreenPixelComponent(p));
cristyce70c172010-01-07 17:15:30 +00003416 q->blue=ClampToQuantum(alpha*q->blue+beta*GetBluePixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00003417 q->opacity=ClampToQuantum(alpha*q->opacity+beta*
3418 GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00003419 p++;
3420 q++;
3421 }
3422 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3423 if (sync == MagickFalse)
3424 status=MagickFalse;
3425 }
3426 morph_view=DestroyCacheView(morph_view);
3427 image_view=DestroyCacheView(image_view);
3428 morph_image=DestroyImage(morph_image);
3429 }
cristybb503372010-05-27 20:51:26 +00003430 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003431 break;
3432 /*
3433 Clone last frame in sequence.
3434 */
3435 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3436 if (morph_image == (Image *) NULL)
3437 {
3438 morph_images=DestroyImageList(morph_images);
3439 return((Image *) NULL);
3440 }
3441 AppendImageToList(&morph_images,morph_image);
3442 morph_images=GetLastImageInList(morph_images);
3443 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3444 {
3445 MagickBooleanType
3446 proceed;
3447
cristyb5d5f722009-11-04 03:03:49 +00003448#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003449 #pragma omp critical (MagickCore_MorphImages)
3450#endif
3451 proceed=SetImageProgress(image,MorphImageTag,scene,
3452 GetImageListLength(image));
3453 if (proceed == MagickFalse)
3454 status=MagickFalse;
3455 }
3456 scene++;
3457 }
3458 if (GetNextImageInList(next) != (Image *) NULL)
3459 {
3460 morph_images=DestroyImageList(morph_images);
3461 return((Image *) NULL);
3462 }
3463 return(GetFirstImageInList(morph_images));
3464}
3465
3466/*
3467%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3468% %
3469% %
3470% %
3471% P l a s m a I m a g e %
3472% %
3473% %
3474% %
3475%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3476%
3477% PlasmaImage() initializes an image with plasma fractal values. The image
3478% must be initialized with a base color and the random number generator
3479% seeded before this method is called.
3480%
3481% The format of the PlasmaImage method is:
3482%
3483% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristybb503372010-05-27 20:51:26 +00003484% size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003485%
3486% A description of each parameter follows:
3487%
3488% o image: the image.
3489%
3490% o segment: Define the region to apply plasma fractals values.
3491%
3492% o attenuate: Define the plasmattenuation factor.
3493%
3494% o depth: Limit the plasma recursion depth.
3495%
3496*/
3497
3498static inline Quantum PlasmaPixel(RandomInfo *random_info,
3499 const MagickRealType pixel,const MagickRealType noise)
3500{
3501 Quantum
3502 plasma;
3503
cristyce70c172010-01-07 17:15:30 +00003504 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003505 noise/2.0);
3506 return(plasma);
3507}
3508
3509MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
cristybb503372010-05-27 20:51:26 +00003510 RandomInfo *random_info,const SegmentInfo *segment,size_t attenuate,
3511 size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003512{
3513 ExceptionInfo
3514 *exception;
3515
cristybb503372010-05-27 20:51:26 +00003516 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003517 x,
3518 x_mid,
3519 y,
3520 y_mid;
3521
3522 MagickRealType
3523 plasma;
3524
3525 PixelPacket
3526 u,
3527 v;
3528
3529 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3530 return(MagickTrue);
3531 if (depth != 0)
3532 {
3533 SegmentInfo
3534 local_info;
3535
3536 /*
3537 Divide the area into quadrants and recurse.
3538 */
3539 depth--;
3540 attenuate++;
cristybb503372010-05-27 20:51:26 +00003541 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3542 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003543 local_info=(*segment);
3544 local_info.x2=(double) x_mid;
3545 local_info.y2=(double) y_mid;
3546 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3547 local_info=(*segment);
3548 local_info.y1=(double) y_mid;
3549 local_info.x2=(double) x_mid;
3550 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3551 local_info=(*segment);
3552 local_info.x1=(double) x_mid;
3553 local_info.y2=(double) y_mid;
3554 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3555 local_info=(*segment);
3556 local_info.x1=(double) x_mid;
3557 local_info.y1=(double) y_mid;
3558 return(PlasmaImageProxy(image,random_info,&local_info,attenuate,depth));
3559 }
3560 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
3561 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00003562 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3563 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003564 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3565 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3566 return(MagickFalse);
3567 /*
3568 Average pixels and apply plasma.
3569 */
3570 exception=(&image->exception);
3571 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3572 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3573 {
3574 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003575 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003576
3577 /*
3578 Left pixel.
3579 */
cristybb503372010-05-27 20:51:26 +00003580 x=(ssize_t) ceil(segment->x1-0.5);
3581 (void) GetOneVirtualPixel(image,x,(ssize_t) ceil(segment->y1-0.5),&u,
cristy06609ee2010-03-17 20:21:27 +00003582 exception);
cristybb503372010-05-27 20:51:26 +00003583 (void) GetOneVirtualPixel(image,x,(ssize_t) ceil(segment->y2-0.5),&v,
cristy06609ee2010-03-17 20:21:27 +00003584 exception);
cristy3ed852e2009-09-05 21:47:34 +00003585 q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
3586 if (q == (PixelPacket *) NULL)
3587 return(MagickTrue);
3588 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3589 plasma);
3590 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3591 plasma);
3592 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3593 plasma);
3594 (void) SyncAuthenticPixels(image,exception);
3595 if (segment->x1 != segment->x2)
3596 {
3597 /*
3598 Right pixel.
3599 */
cristybb503372010-05-27 20:51:26 +00003600 x=(ssize_t) ceil(segment->x2-0.5);
3601 (void) GetOneVirtualPixel(image,x,(ssize_t) ceil(segment->y1-0.5),&u,
cristy3ed852e2009-09-05 21:47:34 +00003602 exception);
cristybb503372010-05-27 20:51:26 +00003603 (void) GetOneVirtualPixel(image,x,(ssize_t) ceil(segment->y2-0.5),&v,
cristy3ed852e2009-09-05 21:47:34 +00003604 exception);
3605 q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
3606 if (q == (PixelPacket *) NULL)
3607 return(MagickTrue);
3608 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3609 plasma);
3610 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3611 2.0,plasma);
3612 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3613 plasma);
3614 (void) SyncAuthenticPixels(image,exception);
3615 }
3616 }
3617 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3618 {
3619 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3620 {
3621 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003622 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003623
3624 /*
3625 Bottom pixel.
3626 */
cristybb503372010-05-27 20:51:26 +00003627 y=(ssize_t) ceil(segment->y2-0.5);
3628 (void) GetOneVirtualPixel(image,(ssize_t) ceil(segment->x1-0.5),y,&u,
cristy3ed852e2009-09-05 21:47:34 +00003629 exception);
cristybb503372010-05-27 20:51:26 +00003630 (void) GetOneVirtualPixel(image,(ssize_t) ceil(segment->x2-0.5),y,&v,
cristy3ed852e2009-09-05 21:47:34 +00003631 exception);
3632 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3633 if (q == (PixelPacket *) NULL)
3634 return(MagickTrue);
3635 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3636 plasma);
3637 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3638 2.0,plasma);
3639 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3640 plasma);
3641 (void) SyncAuthenticPixels(image,exception);
3642 }
3643 if (segment->y1 != segment->y2)
3644 {
3645 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003646 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003647
3648 /*
3649 Top pixel.
3650 */
cristybb503372010-05-27 20:51:26 +00003651 y=(ssize_t) ceil(segment->y1-0.5);
3652 (void) GetOneVirtualPixel(image,(ssize_t) ceil(segment->x1-0.5),y,&u,
cristy3ed852e2009-09-05 21:47:34 +00003653 exception);
cristybb503372010-05-27 20:51:26 +00003654 (void) GetOneVirtualPixel(image,(ssize_t) ceil(segment->x2-0.5),y,&v,
cristy3ed852e2009-09-05 21:47:34 +00003655 exception);
3656 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3657 if (q == (PixelPacket *) NULL)
3658 return(MagickTrue);
3659 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3660 plasma);
3661 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3662 2.0,plasma);
3663 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3664 plasma);
3665 (void) SyncAuthenticPixels(image,exception);
3666 }
3667 }
3668 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3669 {
3670 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003671 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003672
3673 /*
3674 Middle pixel.
3675 */
cristybb503372010-05-27 20:51:26 +00003676 x=(ssize_t) ceil(segment->x1-0.5);
3677 y=(ssize_t) ceil(segment->y1-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003678 (void) GetOneVirtualPixel(image,x,y,&u,exception);
cristybb503372010-05-27 20:51:26 +00003679 x=(ssize_t) ceil(segment->x2-0.5);
3680 y=(ssize_t) ceil(segment->y2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003681 (void) GetOneVirtualPixel(image,x,y,&v,exception);
3682 q=QueueAuthenticPixels(image,x_mid,y_mid,1,1,exception);
3683 if (q == (PixelPacket *) NULL)
3684 return(MagickTrue);
3685 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3686 plasma);
3687 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3688 plasma);
3689 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3690 plasma);
3691 (void) SyncAuthenticPixels(image,exception);
3692 }
3693 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3694 return(MagickTrue);
3695 return(MagickFalse);
3696}
3697
3698MagickExport MagickBooleanType PlasmaImage(Image *image,
cristybb503372010-05-27 20:51:26 +00003699 const SegmentInfo *segment,size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003700{
3701 MagickBooleanType
3702 status;
3703
3704 RandomInfo
3705 *random_info;
3706
3707 if (image->debug != MagickFalse)
3708 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3709 assert(image != (Image *) NULL);
3710 assert(image->signature == MagickSignature);
3711 if (image->debug != MagickFalse)
3712 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3713 random_info=AcquireRandomInfo();
3714 status=PlasmaImageProxy(image,random_info,segment,attenuate,depth);
3715 random_info=DestroyRandomInfo(random_info);
3716 return(status);
3717}
3718
3719/*
3720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3721% %
3722% %
3723% %
3724% P o l a r o i d I m a g e %
3725% %
3726% %
3727% %
3728%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3729%
3730% PolaroidImage() simulates a Polaroid picture.
3731%
3732% The format of the AnnotateImage method is:
3733%
3734% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3735% const double angle,ExceptionInfo exception)
3736%
3737% A description of each parameter follows:
3738%
3739% o image: the image.
3740%
3741% o draw_info: the draw info.
3742%
cristycee97112010-05-28 00:44:52 +00003743% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003744%
3745% o exception: return any errors or warnings in this structure.
3746%
3747*/
3748MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3749 const double angle,ExceptionInfo *exception)
3750{
3751 const char
3752 *value;
3753
cristybb503372010-05-27 20:51:26 +00003754 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003755 quantum;
3756
3757 Image
3758 *bend_image,
3759 *caption_image,
3760 *flop_image,
3761 *picture_image,
3762 *polaroid_image,
3763 *rotate_image,
3764 *trim_image;
3765
cristybb503372010-05-27 20:51:26 +00003766 size_t
cristy3ed852e2009-09-05 21:47:34 +00003767 height;
3768
3769 /*
3770 Simulate a Polaroid picture.
3771 */
3772 assert(image != (Image *) NULL);
3773 assert(image->signature == MagickSignature);
3774 if (image->debug != MagickFalse)
3775 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3776 assert(exception != (ExceptionInfo *) NULL);
3777 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003778 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003779 image->rows)/25.0,10.0);
3780 height=image->rows+2*quantum;
3781 caption_image=(Image *) NULL;
3782 value=GetImageProperty(image,"Caption");
3783 if (value != (const char *) NULL)
3784 {
3785 char
3786 *caption,
3787 geometry[MaxTextExtent];
3788
3789 DrawInfo
3790 *annotate_info;
3791
cristybb503372010-05-27 20:51:26 +00003792 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003793 count;
3794
3795 MagickBooleanType
3796 status;
3797
3798 TypeMetric
3799 metrics;
3800
3801 /*
3802 Generate caption image.
3803 */
3804 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3805 if (caption_image == (Image *) NULL)
3806 return((Image *) NULL);
3807 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
3808 caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
3809 value);
3810 (void) CloneString(&annotate_info->text,caption);
3811 count=FormatMagickCaption(caption_image,annotate_info,&metrics,&caption);
cristybb503372010-05-27 20:51:26 +00003812 status=SetImageExtent(caption_image,image->columns,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00003813 ((count+1)*(metrics.ascent-metrics.descent)+0.5));
3814 if (status == MagickFalse)
3815 caption_image=DestroyImage(caption_image);
3816 else
3817 {
3818 caption_image->background_color=image->border_color;
3819 (void) SetImageBackgroundColor(caption_image);
3820 (void) CloneString(&annotate_info->text,caption);
cristye7f51092010-01-17 00:39:37 +00003821 (void) FormatMagickString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00003822 metrics.ascent);
3823 if (annotate_info->gravity == UndefinedGravity)
3824 (void) CloneString(&annotate_info->geometry,AcquireString(
3825 geometry));
3826 (void) AnnotateImage(caption_image,annotate_info);
3827 height+=caption_image->rows;
3828 }
3829 annotate_info=DestroyDrawInfo(annotate_info);
3830 caption=DestroyString(caption);
3831 }
3832 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3833 exception);
3834 if (picture_image == (Image *) NULL)
3835 {
3836 if (caption_image != (Image *) NULL)
3837 caption_image=DestroyImage(caption_image);
3838 return((Image *) NULL);
3839 }
3840 picture_image->background_color=image->border_color;
3841 (void) SetImageBackgroundColor(picture_image);
3842 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
3843 if (caption_image != (Image *) NULL)
3844 {
3845 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
cristybb503372010-05-27 20:51:26 +00003846 quantum,(ssize_t) (image->rows+3*quantum/2));
cristy3ed852e2009-09-05 21:47:34 +00003847 caption_image=DestroyImage(caption_image);
3848 }
3849 (void) QueryColorDatabase("none",&picture_image->background_color,exception);
3850 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
3851 rotate_image=RotateImage(picture_image,90.0,exception);
3852 picture_image=DestroyImage(picture_image);
3853 if (rotate_image == (Image *) NULL)
3854 return((Image *) NULL);
3855 picture_image=rotate_image;
3856 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
3857 picture_image->columns,exception);
3858 picture_image=DestroyImage(picture_image);
3859 if (bend_image == (Image *) NULL)
3860 return((Image *) NULL);
3861 InheritException(&bend_image->exception,exception);
3862 picture_image=bend_image;
3863 rotate_image=RotateImage(picture_image,-90.0,exception);
3864 picture_image=DestroyImage(picture_image);
3865 if (rotate_image == (Image *) NULL)
3866 return((Image *) NULL);
3867 picture_image=rotate_image;
3868 picture_image->background_color=image->background_color;
3869 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
3870 exception);
3871 if (polaroid_image == (Image *) NULL)
3872 {
3873 picture_image=DestroyImage(picture_image);
3874 return(picture_image);
3875 }
3876 flop_image=FlopImage(polaroid_image,exception);
3877 polaroid_image=DestroyImage(polaroid_image);
3878 if (flop_image == (Image *) NULL)
3879 {
3880 picture_image=DestroyImage(picture_image);
3881 return(picture_image);
3882 }
3883 polaroid_image=flop_image;
3884 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
cristybb503372010-05-27 20:51:26 +00003885 (ssize_t) (-0.01*picture_image->columns/2.0),0L);
cristy3ed852e2009-09-05 21:47:34 +00003886 picture_image=DestroyImage(picture_image);
3887 (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
3888 rotate_image=RotateImage(polaroid_image,angle,exception);
3889 polaroid_image=DestroyImage(polaroid_image);
3890 if (rotate_image == (Image *) NULL)
3891 return((Image *) NULL);
3892 polaroid_image=rotate_image;
3893 trim_image=TrimImage(polaroid_image,exception);
3894 polaroid_image=DestroyImage(polaroid_image);
3895 if (trim_image == (Image *) NULL)
3896 return((Image *) NULL);
3897 polaroid_image=trim_image;
3898 return(polaroid_image);
3899}
3900
3901/*
3902%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3903% %
3904% %
3905% %
cristy3ed852e2009-09-05 21:47:34 +00003906% S e p i a T o n e I m a g e %
3907% %
3908% %
3909% %
3910%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3911%
3912% MagickSepiaToneImage() applies a special effect to the image, similar to the
3913% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
3914% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
3915% threshold of 80% is a good starting point for a reasonable tone.
3916%
3917% The format of the SepiaToneImage method is:
3918%
3919% Image *SepiaToneImage(const Image *image,const double threshold,
3920% ExceptionInfo *exception)
3921%
3922% A description of each parameter follows:
3923%
3924% o image: the image.
3925%
3926% o threshold: the tone threshold.
3927%
3928% o exception: return any errors or warnings in this structure.
3929%
3930*/
3931MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
3932 ExceptionInfo *exception)
3933{
3934#define SepiaToneImageTag "SepiaTone/Image"
3935
cristyc4c8d132010-01-07 01:58:38 +00003936 CacheView
3937 *image_view,
3938 *sepia_view;
3939
cristy3ed852e2009-09-05 21:47:34 +00003940 Image
3941 *sepia_image;
3942
cristy3ed852e2009-09-05 21:47:34 +00003943 MagickBooleanType
3944 status;
3945
cristybb503372010-05-27 20:51:26 +00003946 MagickOffsetType
3947 progress;
3948
3949 ssize_t
3950 y;
3951
cristy3ed852e2009-09-05 21:47:34 +00003952 /*
3953 Initialize sepia-toned image attributes.
3954 */
3955 assert(image != (const Image *) NULL);
3956 assert(image->signature == MagickSignature);
3957 if (image->debug != MagickFalse)
3958 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3959 assert(exception != (ExceptionInfo *) NULL);
3960 assert(exception->signature == MagickSignature);
3961 sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3962 if (sepia_image == (Image *) NULL)
3963 return((Image *) NULL);
3964 if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
3965 {
3966 InheritException(exception,&sepia_image->exception);
3967 sepia_image=DestroyImage(sepia_image);
3968 return((Image *) NULL);
3969 }
3970 /*
3971 Tone each row of the image.
3972 */
3973 status=MagickTrue;
3974 progress=0;
3975 image_view=AcquireCacheView(image);
3976 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00003977#if defined(MAGICKCORE_OPENMP_SUPPORT)
3978 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003979#endif
cristybb503372010-05-27 20:51:26 +00003980 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003981 {
3982 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003983 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003984
cristybb503372010-05-27 20:51:26 +00003985 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003986 x;
3987
3988 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003989 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003990
3991 if (status == MagickFalse)
3992 continue;
3993 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3994 q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
3995 exception);
3996 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3997 {
3998 status=MagickFalse;
3999 continue;
4000 }
cristybb503372010-05-27 20:51:26 +00004001 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004002 {
4003 MagickRealType
4004 intensity,
4005 tone;
4006
4007 intensity=(MagickRealType) PixelIntensityToQuantum(p);
4008 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4009 (MagickRealType) QuantumRange-threshold;
cristyce70c172010-01-07 17:15:30 +00004010 q->red=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004011 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4012 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristyce70c172010-01-07 17:15:30 +00004013 q->green=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004014 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristyce70c172010-01-07 17:15:30 +00004015 q->blue=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004016 tone=threshold/7.0;
4017 if ((MagickRealType) q->green < tone)
cristyce70c172010-01-07 17:15:30 +00004018 q->green=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004019 if ((MagickRealType) q->blue < tone)
cristyce70c172010-01-07 17:15:30 +00004020 q->blue=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004021 p++;
4022 q++;
4023 }
4024 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4025 status=MagickFalse;
4026 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4027 {
4028 MagickBooleanType
4029 proceed;
4030
cristyb5d5f722009-11-04 03:03:49 +00004031#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004032 #pragma omp critical (MagickCore_SepiaToneImage)
4033#endif
4034 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4035 image->rows);
4036 if (proceed == MagickFalse)
4037 status=MagickFalse;
4038 }
4039 }
4040 sepia_view=DestroyCacheView(sepia_view);
4041 image_view=DestroyCacheView(image_view);
4042 (void) NormalizeImage(sepia_image);
4043 (void) ContrastImage(sepia_image,MagickTrue);
4044 if (status == MagickFalse)
4045 sepia_image=DestroyImage(sepia_image);
4046 return(sepia_image);
4047}
4048
4049/*
4050%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4051% %
4052% %
4053% %
4054% S h a d o w I m a g e %
4055% %
4056% %
4057% %
4058%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4059%
4060% ShadowImage() simulates a shadow from the specified image and returns it.
4061%
4062% The format of the ShadowImage method is:
4063%
4064% Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004065% const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004066% ExceptionInfo *exception)
4067%
4068% A description of each parameter follows:
4069%
4070% o image: the image.
4071%
4072% o opacity: percentage transparency.
4073%
4074% o sigma: the standard deviation of the Gaussian, in pixels.
4075%
4076% o x_offset: the shadow x-offset.
4077%
4078% o y_offset: the shadow y-offset.
4079%
4080% o exception: return any errors or warnings in this structure.
4081%
4082*/
4083MagickExport Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004084 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004085 ExceptionInfo *exception)
4086{
4087#define ShadowImageTag "Shadow/Image"
4088
cristyc4c8d132010-01-07 01:58:38 +00004089 CacheView
4090 *image_view;
4091
cristy3ed852e2009-09-05 21:47:34 +00004092 Image
4093 *border_image,
4094 *clone_image,
4095 *shadow_image;
4096
cristy3ed852e2009-09-05 21:47:34 +00004097 MagickBooleanType
4098 status;
4099
cristybb503372010-05-27 20:51:26 +00004100 MagickOffsetType
4101 progress;
4102
cristy3ed852e2009-09-05 21:47:34 +00004103 RectangleInfo
4104 border_info;
4105
cristybb503372010-05-27 20:51:26 +00004106 ssize_t
4107 y;
4108
cristy3ed852e2009-09-05 21:47:34 +00004109 assert(image != (Image *) NULL);
4110 assert(image->signature == MagickSignature);
4111 if (image->debug != MagickFalse)
4112 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4113 assert(exception != (ExceptionInfo *) NULL);
4114 assert(exception->signature == MagickSignature);
4115 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4116 if (clone_image == (Image *) NULL)
4117 return((Image *) NULL);
4118 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4119 clone_image->compose=OverCompositeOp;
cristybb503372010-05-27 20:51:26 +00004120 border_info.width=(size_t) floor(2.0*sigma+0.5);
4121 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004122 border_info.x=0;
4123 border_info.y=0;
4124 (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4125 border_image=BorderImage(clone_image,&border_info,exception);
4126 clone_image=DestroyImage(clone_image);
4127 if (border_image == (Image *) NULL)
4128 return((Image *) NULL);
4129 if (border_image->matte == MagickFalse)
4130 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4131 /*
4132 Shadow image.
4133 */
4134 status=MagickTrue;
4135 progress=0;
4136 image_view=AcquireCacheView(border_image);
cristyb5d5f722009-11-04 03:03:49 +00004137#if defined(MAGICKCORE_OPENMP_SUPPORT)
4138 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004139#endif
cristybb503372010-05-27 20:51:26 +00004140 for (y=0; y < (ssize_t) border_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004141 {
cristybb503372010-05-27 20:51:26 +00004142 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004143 x;
4144
4145 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004146 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004147
4148 if (status == MagickFalse)
4149 continue;
4150 q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4151 exception);
4152 if (q == (PixelPacket *) NULL)
4153 {
4154 status=MagickFalse;
4155 continue;
4156 }
cristybb503372010-05-27 20:51:26 +00004157 for (x=0; x < (ssize_t) border_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004158 {
4159 q->red=border_image->background_color.red;
4160 q->green=border_image->background_color.green;
4161 q->blue=border_image->background_color.blue;
4162 if (border_image->matte == MagickFalse)
4163 q->opacity=border_image->background_color.opacity;
4164 else
cristy46f08202010-01-10 04:04:21 +00004165 q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
4166 GetAlphaPixelComponent(q)*opacity/100.0));
cristy3ed852e2009-09-05 21:47:34 +00004167 q++;
4168 }
4169 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4170 status=MagickFalse;
4171 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4172 {
4173 MagickBooleanType
4174 proceed;
4175
cristyb5d5f722009-11-04 03:03:49 +00004176#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004177 #pragma omp critical (MagickCore_ShadowImage)
4178#endif
4179 proceed=SetImageProgress(image,ShadowImageTag,progress++,
4180 border_image->rows);
4181 if (proceed == MagickFalse)
4182 status=MagickFalse;
4183 }
4184 }
4185 image_view=DestroyCacheView(image_view);
4186 shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
4187 border_image=DestroyImage(border_image);
4188 if (shadow_image == (Image *) NULL)
4189 return((Image *) NULL);
4190 if (shadow_image->page.width == 0)
4191 shadow_image->page.width=shadow_image->columns;
4192 if (shadow_image->page.height == 0)
4193 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004194 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4195 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4196 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4197 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004198 return(shadow_image);
4199}
4200
4201/*
4202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4203% %
4204% %
4205% %
4206% S k e t c h I m a g e %
4207% %
4208% %
4209% %
4210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4211%
4212% SketchImage() simulates a pencil sketch. We convolve the image with a
4213% Gaussian operator of the given radius and standard deviation (sigma). For
4214% reasonable results, radius should be larger than sigma. Use a radius of 0
4215% and SketchImage() selects a suitable radius for you. Angle gives the angle
4216% of the sketch.
4217%
4218% The format of the SketchImage method is:
4219%
4220% Image *SketchImage(const Image *image,const double radius,
4221% const double sigma,const double angle,ExceptionInfo *exception)
4222%
4223% A description of each parameter follows:
4224%
4225% o image: the image.
4226%
4227% o radius: the radius of the Gaussian, in pixels, not counting
4228% the center pixel.
4229%
4230% o sigma: the standard deviation of the Gaussian, in pixels.
4231%
cristycee97112010-05-28 00:44:52 +00004232% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00004233%
4234% o exception: return any errors or warnings in this structure.
4235%
4236*/
4237MagickExport Image *SketchImage(const Image *image,const double radius,
4238 const double sigma,const double angle,ExceptionInfo *exception)
4239{
cristyfa112112010-01-04 17:48:07 +00004240 CacheView
4241 *random_view;
4242
cristy3ed852e2009-09-05 21:47:34 +00004243 Image
4244 *blend_image,
4245 *blur_image,
4246 *dodge_image,
4247 *random_image,
4248 *sketch_image;
4249
cristybb503372010-05-27 20:51:26 +00004250 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004251 y;
4252
4253 MagickBooleanType
4254 status;
4255
4256 MagickPixelPacket
4257 zero;
4258
4259 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004260 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004261
4262 /*
4263 Sketch image.
4264 */
4265 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4266 MagickTrue,exception);
4267 if (random_image == (Image *) NULL)
4268 return((Image *) NULL);
4269 status=MagickTrue;
4270 GetMagickPixelPacket(random_image,&zero);
cristy1b784432009-12-19 02:20:40 +00004271 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004272 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004273#if defined(MAGICKCORE_OPENMP_SUPPORT)
4274 #pragma omp parallel for schedule(dynamic,4) shared(status)
4275#endif
cristybb503372010-05-27 20:51:26 +00004276 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004277 {
cristy6ebe97c2010-07-03 01:17:28 +00004278 int
4279 id;
4280
cristy3ed852e2009-09-05 21:47:34 +00004281 MagickPixelPacket
4282 pixel;
4283
4284 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004285 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00004286
cristybb503372010-05-27 20:51:26 +00004287 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004288 x;
4289
4290 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004291 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004292
cristy1b784432009-12-19 02:20:40 +00004293 if (status == MagickFalse)
4294 continue;
cristy3ed852e2009-09-05 21:47:34 +00004295 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4296 exception);
4297 if (q == (PixelPacket *) NULL)
4298 {
4299 status=MagickFalse;
4300 continue;
4301 }
4302 indexes=GetCacheViewAuthenticIndexQueue(random_view);
4303 pixel=zero;
cristy1b784432009-12-19 02:20:40 +00004304 id=GetOpenMPThreadId();
cristybb503372010-05-27 20:51:26 +00004305 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004306 {
4307 pixel.red=(MagickRealType) (QuantumRange*
cristy1b784432009-12-19 02:20:40 +00004308 GetPseudoRandomValue(random_info[id]));
cristy3ed852e2009-09-05 21:47:34 +00004309 pixel.green=pixel.red;
4310 pixel.blue=pixel.red;
4311 if (image->colorspace == CMYKColorspace)
4312 pixel.index=pixel.red;
4313 SetPixelPacket(random_image,&pixel,q,indexes+x);
4314 q++;
4315 }
4316 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4317 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004318 }
4319 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004320 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004321 if (status == MagickFalse)
4322 {
4323 random_image=DestroyImage(random_image);
4324 return(random_image);
4325 }
4326 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4327 random_image=DestroyImage(random_image);
4328 if (blur_image == (Image *) NULL)
4329 return((Image *) NULL);
4330 dodge_image=EdgeImage(blur_image,radius,exception);
4331 blur_image=DestroyImage(blur_image);
4332 if (dodge_image == (Image *) NULL)
4333 return((Image *) NULL);
4334 (void) NormalizeImage(dodge_image);
4335 (void) NegateImage(dodge_image,MagickFalse);
4336 (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4337 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4338 if (sketch_image == (Image *) NULL)
4339 {
4340 dodge_image=DestroyImage(dodge_image);
4341 return((Image *) NULL);
4342 }
4343 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4344 dodge_image=DestroyImage(dodge_image);
4345 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4346 if (blend_image == (Image *) NULL)
4347 {
4348 sketch_image=DestroyImage(sketch_image);
4349 return((Image *) NULL);
4350 }
4351 (void) SetImageArtifact(blend_image,"compose:args","20x80");
4352 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4353 blend_image=DestroyImage(blend_image);
4354 return(sketch_image);
4355}
4356
4357/*
4358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4359% %
4360% %
4361% %
4362% S o l a r i z e I m a g e %
4363% %
4364% %
4365% %
4366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4367%
4368% SolarizeImage() applies a special effect to the image, similar to the effect
4369% achieved in a photo darkroom by selectively exposing areas of photo
4370% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4371% measure of the extent of the solarization.
4372%
4373% The format of the SolarizeImage method is:
4374%
4375% MagickBooleanType SolarizeImage(Image *image,const double threshold)
4376%
4377% A description of each parameter follows:
4378%
4379% o image: the image.
4380%
4381% o threshold: Define the extent of the solarization.
4382%
4383*/
4384MagickExport MagickBooleanType SolarizeImage(Image *image,
4385 const double threshold)
4386{
4387#define SolarizeImageTag "Solarize/Image"
4388
cristyc4c8d132010-01-07 01:58:38 +00004389 CacheView
4390 *image_view;
4391
cristy3ed852e2009-09-05 21:47:34 +00004392 ExceptionInfo
4393 *exception;
4394
cristy3ed852e2009-09-05 21:47:34 +00004395 MagickBooleanType
4396 status;
4397
cristybb503372010-05-27 20:51:26 +00004398 MagickOffsetType
4399 progress;
4400
4401 ssize_t
4402 y;
4403
cristy3ed852e2009-09-05 21:47:34 +00004404 assert(image != (Image *) NULL);
4405 assert(image->signature == MagickSignature);
4406 if (image->debug != MagickFalse)
4407 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4408 if (image->storage_class == PseudoClass)
4409 {
cristybb503372010-05-27 20:51:26 +00004410 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004411 i;
4412
4413 /*
4414 Solarize colormap.
4415 */
cristybb503372010-05-27 20:51:26 +00004416 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004417 {
4418 if ((MagickRealType) image->colormap[i].red > threshold)
4419 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4420 if ((MagickRealType) image->colormap[i].green > threshold)
4421 image->colormap[i].green=(Quantum) QuantumRange-
4422 image->colormap[i].green;
4423 if ((MagickRealType) image->colormap[i].blue > threshold)
4424 image->colormap[i].blue=(Quantum) QuantumRange-
4425 image->colormap[i].blue;
4426 }
4427 }
4428 /*
4429 Solarize image.
4430 */
4431 status=MagickTrue;
4432 progress=0;
4433 exception=(&image->exception);
4434 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004435#if defined(MAGICKCORE_OPENMP_SUPPORT)
4436 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004437#endif
cristybb503372010-05-27 20:51:26 +00004438 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004439 {
cristybb503372010-05-27 20:51:26 +00004440 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004441 x;
4442
4443 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004444 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004445
4446 if (status == MagickFalse)
4447 continue;
4448 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4449 exception);
4450 if (q == (PixelPacket *) NULL)
4451 {
4452 status=MagickFalse;
4453 continue;
4454 }
cristybb503372010-05-27 20:51:26 +00004455 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004456 {
4457 if ((MagickRealType) q->red > threshold)
4458 q->red=(Quantum) QuantumRange-q->red;
4459 if ((MagickRealType) q->green > threshold)
4460 q->green=(Quantum) QuantumRange-q->green;
4461 if ((MagickRealType) q->blue > threshold)
4462 q->blue=(Quantum) QuantumRange-q->blue;
4463 q++;
4464 }
4465 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4466 status=MagickFalse;
4467 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4468 {
4469 MagickBooleanType
4470 proceed;
4471
cristyb5d5f722009-11-04 03:03:49 +00004472#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004473 #pragma omp critical (MagickCore_SolarizeImage)
4474#endif
4475 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4476 if (proceed == MagickFalse)
4477 status=MagickFalse;
4478 }
4479 }
4480 image_view=DestroyCacheView(image_view);
4481 return(status);
4482}
4483
4484/*
4485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4486% %
4487% %
4488% %
4489% S t e g a n o I m a g e %
4490% %
4491% %
4492% %
4493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4494%
4495% SteganoImage() hides a digital watermark within the image. Recover
4496% the hidden watermark later to prove that the authenticity of an image.
4497% Offset defines the start position within the image to hide the watermark.
4498%
4499% The format of the SteganoImage method is:
4500%
4501% Image *SteganoImage(const Image *image,Image *watermark,
4502% ExceptionInfo *exception)
4503%
4504% A description of each parameter follows:
4505%
4506% o image: the image.
4507%
4508% o watermark: the watermark image.
4509%
4510% o exception: return any errors or warnings in this structure.
4511%
4512*/
4513MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4514 ExceptionInfo *exception)
4515{
cristybb503372010-05-27 20:51:26 +00004516#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) \
cristy3ed852e2009-09-05 21:47:34 +00004517 (i)) & 0x01) != 0)
cristyeaedf062010-05-29 22:36:02 +00004518#define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) != 0 ? (size_t) (alpha) \
4519 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004520#define SteganoImageTag "Stegano/Image"
4521
4522 Image
4523 *stegano_image;
4524
4525 int
4526 c;
4527
cristybb503372010-05-27 20:51:26 +00004528 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004529 i,
4530 j,
4531 k,
4532 y;
4533
4534 MagickBooleanType
4535 status;
4536
4537 PixelPacket
4538 pixel;
4539
cristybb503372010-05-27 20:51:26 +00004540 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004541 x;
4542
4543 register PixelPacket
4544 *q;
4545
cristybb503372010-05-27 20:51:26 +00004546 size_t
cristyeaedf062010-05-29 22:36:02 +00004547 depth,
4548 one;
cristy3ed852e2009-09-05 21:47:34 +00004549
4550 /*
4551 Initialize steganographic image attributes.
4552 */
4553 assert(image != (const Image *) NULL);
4554 assert(image->signature == MagickSignature);
4555 if (image->debug != MagickFalse)
4556 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4557 assert(watermark != (const Image *) NULL);
4558 assert(watermark->signature == MagickSignature);
4559 assert(exception != (ExceptionInfo *) NULL);
4560 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004561 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004562 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4563 if (stegano_image == (Image *) NULL)
4564 return((Image *) NULL);
4565 if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
4566 {
4567 InheritException(exception,&stegano_image->exception);
4568 stegano_image=DestroyImage(stegano_image);
4569 return((Image *) NULL);
4570 }
4571 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4572 /*
4573 Hide watermark in low-order bits of image.
4574 */
4575 c=0;
4576 i=0;
4577 j=0;
4578 depth=stegano_image->depth;
4579 k=image->offset;
cristybb503372010-05-27 20:51:26 +00004580 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004581 {
cristybb503372010-05-27 20:51:26 +00004582 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004583 {
cristybb503372010-05-27 20:51:26 +00004584 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004585 {
4586 (void) GetOneVirtualPixel(watermark,x,y,&pixel,exception);
cristybb503372010-05-27 20:51:26 +00004587 if ((k/(ssize_t) stegano_image->columns) >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004588 break;
cristybb503372010-05-27 20:51:26 +00004589 q=GetAuthenticPixels(stegano_image,k % (ssize_t) stegano_image->columns,
4590 k/(ssize_t) stegano_image->columns,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004591 if (q == (PixelPacket *) NULL)
4592 break;
4593 switch (c)
4594 {
4595 case 0:
4596 {
4597 SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4598 break;
4599 }
4600 case 1:
4601 {
4602 SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4603 break;
4604 }
4605 case 2:
4606 {
4607 SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4608 break;
4609 }
4610 }
4611 if (SyncAuthenticPixels(stegano_image,exception) == MagickFalse)
4612 break;
4613 c++;
4614 if (c == 3)
4615 c=0;
4616 k++;
cristybb503372010-05-27 20:51:26 +00004617 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004618 k=0;
4619 if (k == image->offset)
4620 j++;
4621 }
4622 }
cristy8b27a6d2010-02-14 03:31:15 +00004623 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004624 {
cristy8b27a6d2010-02-14 03:31:15 +00004625 MagickBooleanType
4626 proceed;
4627
4628 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4629 (depth-i),depth);
4630 if (proceed == MagickFalse)
4631 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004632 }
4633 }
4634 if (stegano_image->storage_class == PseudoClass)
4635 (void) SyncImage(stegano_image);
4636 return(stegano_image);
4637}
4638
4639/*
4640%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4641% %
4642% %
4643% %
4644% S t e r e o A n a g l y p h I m a g e %
4645% %
4646% %
4647% %
4648%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4649%
4650% StereoAnaglyphImage() combines two images and produces a single image that
4651% is the composite of a left and right image of a stereo pair. Special
4652% red-green stereo glasses are required to view this effect.
4653%
4654% The format of the StereoAnaglyphImage method is:
4655%
4656% Image *StereoImage(const Image *left_image,const Image *right_image,
4657% ExceptionInfo *exception)
4658% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004659% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004660% ExceptionInfo *exception)
4661%
4662% A description of each parameter follows:
4663%
4664% o left_image: the left image.
4665%
4666% o right_image: the right image.
4667%
4668% o exception: return any errors or warnings in this structure.
4669%
4670% o x_offset: amount, in pixels, by which the left image is offset to the
4671% right of the right image.
4672%
4673% o y_offset: amount, in pixels, by which the left image is offset to the
4674% bottom of the right image.
4675%
4676%
4677*/
4678MagickExport Image *StereoImage(const Image *left_image,
4679 const Image *right_image,ExceptionInfo *exception)
4680{
4681 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4682}
4683
4684MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004685 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004686 ExceptionInfo *exception)
4687{
4688#define StereoImageTag "Stereo/Image"
4689
4690 const Image
4691 *image;
4692
4693 Image
4694 *stereo_image;
4695
cristybb503372010-05-27 20:51:26 +00004696 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004697 y;
4698
4699 MagickBooleanType
4700 status;
4701
4702 assert(left_image != (const Image *) NULL);
4703 assert(left_image->signature == MagickSignature);
4704 if (left_image->debug != MagickFalse)
4705 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4706 left_image->filename);
4707 assert(right_image != (const Image *) NULL);
4708 assert(right_image->signature == MagickSignature);
4709 assert(exception != (ExceptionInfo *) NULL);
4710 assert(exception->signature == MagickSignature);
4711 assert(right_image != (const Image *) NULL);
4712 image=left_image;
4713 if ((left_image->columns != right_image->columns) ||
4714 (left_image->rows != right_image->rows))
4715 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4716 /*
4717 Initialize stereo image attributes.
4718 */
4719 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4720 MagickTrue,exception);
4721 if (stereo_image == (Image *) NULL)
4722 return((Image *) NULL);
4723 if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
4724 {
4725 InheritException(exception,&stereo_image->exception);
4726 stereo_image=DestroyImage(stereo_image);
4727 return((Image *) NULL);
4728 }
4729 /*
4730 Copy left image to red channel and right image to blue channel.
4731 */
cristybb503372010-05-27 20:51:26 +00004732 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004733 {
4734 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004735 *restrict p,
4736 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004737
cristybb503372010-05-27 20:51:26 +00004738 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004739 x;
4740
4741 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004742 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004743
4744 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4745 exception);
4746 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4747 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
4748 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
4749 (r == (PixelPacket *) NULL))
4750 break;
cristybb503372010-05-27 20:51:26 +00004751 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004752 {
cristyce70c172010-01-07 17:15:30 +00004753 r->red=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00004754 r->green=q->green;
4755 r->blue=q->blue;
4756 r->opacity=(Quantum) ((p->opacity+q->opacity)/2);
4757 p++;
4758 q++;
4759 r++;
4760 }
4761 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4762 break;
cristy8b27a6d2010-02-14 03:31:15 +00004763 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004764 {
cristy8b27a6d2010-02-14 03:31:15 +00004765 MagickBooleanType
4766 proceed;
4767
cristybb503372010-05-27 20:51:26 +00004768 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4769 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004770 if (proceed == MagickFalse)
4771 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004772 }
4773 }
4774 return(stereo_image);
4775}
4776
4777/*
4778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4779% %
4780% %
4781% %
4782% S w i r l I m a g e %
4783% %
4784% %
4785% %
4786%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4787%
4788% SwirlImage() swirls the pixels about the center of the image, where
4789% degrees indicates the sweep of the arc through which each pixel is moved.
4790% You get a more dramatic effect as the degrees move from 1 to 360.
4791%
4792% The format of the SwirlImage method is:
4793%
4794% Image *SwirlImage(const Image *image,double degrees,
4795% ExceptionInfo *exception)
4796%
4797% A description of each parameter follows:
4798%
4799% o image: the image.
4800%
4801% o degrees: Define the tightness of the swirling effect.
4802%
4803% o exception: return any errors or warnings in this structure.
4804%
4805*/
4806MagickExport Image *SwirlImage(const Image *image,double degrees,
4807 ExceptionInfo *exception)
4808{
4809#define SwirlImageTag "Swirl/Image"
4810
cristyfa112112010-01-04 17:48:07 +00004811 CacheView
4812 *image_view,
4813 *swirl_view;
4814
cristy3ed852e2009-09-05 21:47:34 +00004815 Image
4816 *swirl_image;
4817
cristy3ed852e2009-09-05 21:47:34 +00004818 MagickBooleanType
4819 status;
4820
cristybb503372010-05-27 20:51:26 +00004821 MagickOffsetType
4822 progress;
4823
cristy3ed852e2009-09-05 21:47:34 +00004824 MagickPixelPacket
4825 zero;
4826
4827 MagickRealType
4828 radius;
4829
4830 PointInfo
4831 center,
4832 scale;
4833
4834 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00004835 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00004836
cristybb503372010-05-27 20:51:26 +00004837 ssize_t
4838 y;
4839
cristy3ed852e2009-09-05 21:47:34 +00004840 /*
4841 Initialize swirl image attributes.
4842 */
4843 assert(image != (const Image *) NULL);
4844 assert(image->signature == MagickSignature);
4845 if (image->debug != MagickFalse)
4846 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4847 assert(exception != (ExceptionInfo *) NULL);
4848 assert(exception->signature == MagickSignature);
4849 swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4850 if (swirl_image == (Image *) NULL)
4851 return((Image *) NULL);
4852 if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
4853 {
4854 InheritException(exception,&swirl_image->exception);
4855 swirl_image=DestroyImage(swirl_image);
4856 return((Image *) NULL);
4857 }
4858 if (swirl_image->background_color.opacity != OpaqueOpacity)
4859 swirl_image->matte=MagickTrue;
4860 /*
4861 Compute scaling factor.
4862 */
4863 center.x=(double) image->columns/2.0;
4864 center.y=(double) image->rows/2.0;
4865 radius=MagickMax(center.x,center.y);
4866 scale.x=1.0;
4867 scale.y=1.0;
4868 if (image->columns > image->rows)
4869 scale.y=(double) image->columns/(double) image->rows;
4870 else
4871 if (image->columns < image->rows)
4872 scale.x=(double) image->rows/(double) image->columns;
4873 degrees=(double) DegreesToRadians(degrees);
4874 /*
4875 Swirl image.
4876 */
4877 status=MagickTrue;
4878 progress=0;
4879 GetMagickPixelPacket(swirl_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00004880 resample_filter=AcquireResampleFilterThreadSet(image,
4881 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004882 image_view=AcquireCacheView(image);
4883 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00004884#if defined(MAGICKCORE_OPENMP_SUPPORT)
4885 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004886#endif
cristybb503372010-05-27 20:51:26 +00004887 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004888 {
cristy6ebe97c2010-07-03 01:17:28 +00004889 int
4890 id;
4891
cristy3ed852e2009-09-05 21:47:34 +00004892 MagickPixelPacket
4893 pixel;
4894
4895 MagickRealType
4896 distance;
4897
4898 PointInfo
4899 delta;
4900
4901 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004902 *restrict swirl_indexes;
cristy3ed852e2009-09-05 21:47:34 +00004903
cristybb503372010-05-27 20:51:26 +00004904 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004905 x;
4906
4907 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004908 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004909
4910 if (status == MagickFalse)
4911 continue;
4912 q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
4913 exception);
4914 if (q == (PixelPacket *) NULL)
4915 {
4916 status=MagickFalse;
4917 continue;
4918 }
4919 swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
4920 delta.y=scale.y*(double) (y-center.y);
4921 pixel=zero;
4922 id=GetOpenMPThreadId();
cristybb503372010-05-27 20:51:26 +00004923 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004924 {
4925 /*
4926 Determine if the pixel is within an ellipse.
4927 */
4928 delta.x=scale.x*(double) (x-center.x);
4929 distance=delta.x*delta.x+delta.y*delta.y;
4930 if (distance < (radius*radius))
4931 {
4932 MagickRealType
4933 cosine,
4934 factor,
4935 sine;
4936
4937 /*
4938 Swirl the pixel.
4939 */
4940 factor=1.0-sqrt((double) distance)/radius;
4941 sine=sin((double) (degrees*factor*factor));
4942 cosine=cos((double) (degrees*factor*factor));
4943 (void) ResamplePixelColor(resample_filter[id],(double) ((cosine*
4944 delta.x-sine*delta.y)/scale.x+center.x),(double) ((sine*delta.x+
4945 cosine*delta.y)/scale.y+center.y),&pixel);
4946 SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
4947 }
4948 q++;
4949 }
4950 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
4951 status=MagickFalse;
4952 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4953 {
4954 MagickBooleanType
4955 proceed;
4956
cristyb5d5f722009-11-04 03:03:49 +00004957#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004958 #pragma omp critical (MagickCore_SwirlImage)
4959#endif
4960 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
4961 if (proceed == MagickFalse)
4962 status=MagickFalse;
4963 }
4964 }
4965 swirl_view=DestroyCacheView(swirl_view);
4966 image_view=DestroyCacheView(image_view);
4967 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
4968 if (status == MagickFalse)
4969 swirl_image=DestroyImage(swirl_image);
4970 return(swirl_image);
4971}
4972
4973/*
4974%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4975% %
4976% %
4977% %
4978% T i n t I m a g e %
4979% %
4980% %
4981% %
4982%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4983%
4984% TintImage() applies a color vector to each pixel in the image. The length
4985% of the vector is 0 for black and white and at its maximum for the midtones.
4986% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
4987%
4988% The format of the TintImage method is:
4989%
4990% Image *TintImage(const Image *image,const char *opacity,
4991% const PixelPacket tint,ExceptionInfo *exception)
4992%
4993% A description of each parameter follows:
4994%
4995% o image: the image.
4996%
4997% o opacity: A color value used for tinting.
4998%
4999% o tint: A color value used for tinting.
5000%
5001% o exception: return any errors or warnings in this structure.
5002%
5003*/
5004MagickExport Image *TintImage(const Image *image,const char *opacity,
5005 const PixelPacket tint,ExceptionInfo *exception)
5006{
5007#define TintImageTag "Tint/Image"
5008
cristyc4c8d132010-01-07 01:58:38 +00005009 CacheView
5010 *image_view,
5011 *tint_view;
5012
cristy3ed852e2009-09-05 21:47:34 +00005013 GeometryInfo
5014 geometry_info;
5015
5016 Image
5017 *tint_image;
5018
cristy3ed852e2009-09-05 21:47:34 +00005019 MagickBooleanType
5020 status;
5021
cristybb503372010-05-27 20:51:26 +00005022 MagickOffsetType
5023 progress;
cristy3ed852e2009-09-05 21:47:34 +00005024
5025 MagickPixelPacket
5026 color_vector,
5027 pixel;
5028
cristybb503372010-05-27 20:51:26 +00005029 MagickStatusType
5030 flags;
5031
5032 ssize_t
5033 y;
5034
cristy3ed852e2009-09-05 21:47:34 +00005035 /*
5036 Allocate tint image.
5037 */
5038 assert(image != (const Image *) NULL);
5039 assert(image->signature == MagickSignature);
5040 if (image->debug != MagickFalse)
5041 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5042 assert(exception != (ExceptionInfo *) NULL);
5043 assert(exception->signature == MagickSignature);
5044 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5045 if (tint_image == (Image *) NULL)
5046 return((Image *) NULL);
5047 if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5048 {
5049 InheritException(exception,&tint_image->exception);
5050 tint_image=DestroyImage(tint_image);
5051 return((Image *) NULL);
5052 }
5053 if (opacity == (const char *) NULL)
5054 return(tint_image);
5055 /*
5056 Determine RGB values of the color.
5057 */
5058 flags=ParseGeometry(opacity,&geometry_info);
5059 pixel.red=geometry_info.rho;
5060 if ((flags & SigmaValue) != 0)
5061 pixel.green=geometry_info.sigma;
5062 else
5063 pixel.green=pixel.red;
5064 if ((flags & XiValue) != 0)
5065 pixel.blue=geometry_info.xi;
5066 else
5067 pixel.blue=pixel.red;
5068 if ((flags & PsiValue) != 0)
5069 pixel.opacity=geometry_info.psi;
5070 else
5071 pixel.opacity=(MagickRealType) OpaqueOpacity;
5072 color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5073 PixelIntensity(&tint));
5074 color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5075 PixelIntensity(&tint));
5076 color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5077 PixelIntensity(&tint));
5078 /*
5079 Tint image.
5080 */
5081 status=MagickTrue;
5082 progress=0;
5083 image_view=AcquireCacheView(image);
5084 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005085#if defined(MAGICKCORE_OPENMP_SUPPORT)
5086 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005087#endif
cristybb503372010-05-27 20:51:26 +00005088 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005089 {
5090 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005091 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005092
cristybb503372010-05-27 20:51:26 +00005093 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005094 x;
5095
5096 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005097 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005098
5099 if (status == MagickFalse)
5100 continue;
5101 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5102 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5103 exception);
5104 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5105 {
5106 status=MagickFalse;
5107 continue;
5108 }
cristybb503372010-05-27 20:51:26 +00005109 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005110 {
5111 MagickPixelPacket
5112 pixel;
5113
5114 MagickRealType
5115 weight;
5116
5117 weight=QuantumScale*p->red-0.5;
5118 pixel.red=(MagickRealType) p->red+color_vector.red*(1.0-(4.0*
5119 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005120 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00005121 weight=QuantumScale*p->green-0.5;
5122 pixel.green=(MagickRealType) p->green+color_vector.green*(1.0-(4.0*
5123 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005124 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00005125 weight=QuantumScale*p->blue-0.5;
5126 pixel.blue=(MagickRealType) p->blue+color_vector.blue*(1.0-(4.0*
5127 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005128 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5129 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00005130 p++;
5131 q++;
5132 }
5133 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5134 status=MagickFalse;
5135 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5136 {
5137 MagickBooleanType
5138 proceed;
5139
cristyb5d5f722009-11-04 03:03:49 +00005140#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005141 #pragma omp critical (MagickCore_TintImage)
5142#endif
5143 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5144 if (proceed == MagickFalse)
5145 status=MagickFalse;
5146 }
5147 }
5148 tint_view=DestroyCacheView(tint_view);
5149 image_view=DestroyCacheView(image_view);
5150 if (status == MagickFalse)
5151 tint_image=DestroyImage(tint_image);
5152 return(tint_image);
5153}
5154
5155/*
5156%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5157% %
5158% %
5159% %
5160% V i g n e t t e I m a g e %
5161% %
5162% %
5163% %
5164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5165%
5166% VignetteImage() softens the edges of the image in vignette style.
5167%
5168% The format of the VignetteImage method is:
5169%
5170% Image *VignetteImage(const Image *image,const double radius,
cristybb503372010-05-27 20:51:26 +00005171% const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005172%
5173% A description of each parameter follows:
5174%
5175% o image: the image.
5176%
5177% o radius: the radius of the pixel neighborhood.
5178%
5179% o sigma: the standard deviation of the Gaussian, in pixels.
5180%
5181% o x, y: Define the x and y ellipse offset.
5182%
5183% o exception: return any errors or warnings in this structure.
5184%
5185*/
5186MagickExport Image *VignetteImage(const Image *image,const double radius,
cristybb503372010-05-27 20:51:26 +00005187 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005188{
5189 char
5190 ellipse[MaxTextExtent];
5191
5192 DrawInfo
5193 *draw_info;
5194
5195 Image
5196 *canvas_image,
5197 *blur_image,
5198 *oval_image,
5199 *vignette_image;
5200
5201 assert(image != (Image *) NULL);
5202 assert(image->signature == MagickSignature);
5203 if (image->debug != MagickFalse)
5204 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5205 assert(exception != (ExceptionInfo *) NULL);
5206 assert(exception->signature == MagickSignature);
5207 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5208 if (canvas_image == (Image *) NULL)
5209 return((Image *) NULL);
5210 if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
5211 {
5212 InheritException(exception,&canvas_image->exception);
5213 canvas_image=DestroyImage(canvas_image);
5214 return((Image *) NULL);
5215 }
5216 canvas_image->matte=MagickTrue;
5217 oval_image=CloneImage(canvas_image,canvas_image->columns,
5218 canvas_image->rows,MagickTrue,exception);
5219 if (oval_image == (Image *) NULL)
5220 {
5221 canvas_image=DestroyImage(canvas_image);
5222 return((Image *) NULL);
5223 }
5224 (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5225 (void) SetImageBackgroundColor(oval_image);
5226 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5227 (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5228 (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
5229 (void) FormatMagickString(ellipse,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00005230 "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
cristy8cd5b312010-01-07 01:10:24 +00005231 image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005232 draw_info->primitive=AcquireString(ellipse);
5233 (void) DrawImage(oval_image,draw_info);
5234 draw_info=DestroyDrawInfo(draw_info);
5235 blur_image=BlurImage(oval_image,radius,sigma,exception);
5236 oval_image=DestroyImage(oval_image);
5237 if (blur_image == (Image *) NULL)
5238 {
5239 canvas_image=DestroyImage(canvas_image);
5240 return((Image *) NULL);
5241 }
5242 blur_image->matte=MagickFalse;
5243 (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5244 blur_image=DestroyImage(blur_image);
5245 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5246 canvas_image=DestroyImage(canvas_image);
5247 return(vignette_image);
5248}
5249
5250/*
5251%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5252% %
5253% %
5254% %
5255% W a v e I m a g e %
5256% %
5257% %
5258% %
5259%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5260%
5261% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005262% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005263% by the given parameters.
5264%
5265% The format of the WaveImage method is:
5266%
5267% Image *WaveImage(const Image *image,const double amplitude,
5268% const double wave_length,ExceptionInfo *exception)
5269%
5270% A description of each parameter follows:
5271%
5272% o image: the image.
5273%
5274% o amplitude, wave_length: Define the amplitude and wave length of the
5275% sine wave.
5276%
5277% o exception: return any errors or warnings in this structure.
5278%
5279*/
5280MagickExport Image *WaveImage(const Image *image,const double amplitude,
5281 const double wave_length,ExceptionInfo *exception)
5282{
5283#define WaveImageTag "Wave/Image"
5284
cristyfa112112010-01-04 17:48:07 +00005285 CacheView
5286 *wave_view;
5287
cristy3ed852e2009-09-05 21:47:34 +00005288 Image
5289 *wave_image;
5290
cristy3ed852e2009-09-05 21:47:34 +00005291 MagickBooleanType
5292 status;
5293
cristybb503372010-05-27 20:51:26 +00005294 MagickOffsetType
5295 progress;
5296
cristy3ed852e2009-09-05 21:47:34 +00005297 MagickPixelPacket
5298 zero;
5299
5300 MagickRealType
5301 *sine_map;
5302
cristybb503372010-05-27 20:51:26 +00005303 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005304 i;
5305
5306 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00005307 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00005308
cristybb503372010-05-27 20:51:26 +00005309 ssize_t
5310 y;
5311
cristy3ed852e2009-09-05 21:47:34 +00005312 /*
5313 Initialize wave image attributes.
5314 */
5315 assert(image != (Image *) NULL);
5316 assert(image->signature == MagickSignature);
5317 if (image->debug != MagickFalse)
5318 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5319 assert(exception != (ExceptionInfo *) NULL);
5320 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005321 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005322 fabs(amplitude)),MagickTrue,exception);
5323 if (wave_image == (Image *) NULL)
5324 return((Image *) NULL);
5325 if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
5326 {
5327 InheritException(exception,&wave_image->exception);
5328 wave_image=DestroyImage(wave_image);
5329 return((Image *) NULL);
5330 }
5331 if (wave_image->background_color.opacity != OpaqueOpacity)
5332 wave_image->matte=MagickTrue;
5333 /*
5334 Allocate sine map.
5335 */
5336 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5337 sizeof(*sine_map));
5338 if (sine_map == (MagickRealType *) NULL)
5339 {
5340 wave_image=DestroyImage(wave_image);
5341 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5342 }
cristybb503372010-05-27 20:51:26 +00005343 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy3ed852e2009-09-05 21:47:34 +00005344 sine_map[i]=fabs(amplitude)+amplitude*sin((2*MagickPI*i)/wave_length);
5345 /*
5346 Wave image.
5347 */
5348 status=MagickTrue;
5349 progress=0;
5350 GetMagickPixelPacket(wave_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00005351 resample_filter=AcquireResampleFilterThreadSet(image,
5352 BackgroundVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005353 wave_view=AcquireCacheView(wave_image);
cristyb5d5f722009-11-04 03:03:49 +00005354#if defined(MAGICKCORE_OPENMP_SUPPORT)
5355 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005356#endif
cristybb503372010-05-27 20:51:26 +00005357 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005358 {
cristy6ebe97c2010-07-03 01:17:28 +00005359 int
5360 id;
5361
cristy3ed852e2009-09-05 21:47:34 +00005362 MagickPixelPacket
5363 pixel;
5364
5365 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00005366 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00005367
cristybb503372010-05-27 20:51:26 +00005368 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005369 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=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5377 exception);
5378 if (q == (PixelPacket *) NULL)
5379 {
5380 status=MagickFalse;
5381 continue;
5382 }
5383 indexes=GetCacheViewAuthenticIndexQueue(wave_view);
5384 pixel=zero;
5385 id=GetOpenMPThreadId();
cristybb503372010-05-27 20:51:26 +00005386 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005387 {
5388 (void) ResamplePixelColor(resample_filter[id],(double) x,(double) (y-
5389 sine_map[x]),&pixel);
5390 SetPixelPacket(wave_image,&pixel,q,indexes+x);
5391 q++;
5392 }
5393 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5394 status=MagickFalse;
5395 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5396 {
5397 MagickBooleanType
5398 proceed;
5399
cristyb5d5f722009-11-04 03:03:49 +00005400#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005401 #pragma omp critical (MagickCore_WaveImage)
5402#endif
5403 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5404 if (proceed == MagickFalse)
5405 status=MagickFalse;
5406 }
5407 }
5408 wave_view=DestroyCacheView(wave_view);
5409 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5410 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5411 if (status == MagickFalse)
5412 wave_image=DestroyImage(wave_image);
5413 return(wave_image);
5414}