blob: 0cd3ccffa4602c87e1202f5ccc27372ab8173e5e [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/annotate.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/cache.h"
48#include "MagickCore/cache-view.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/composite.h"
52#include "MagickCore/decorate.h"
53#include "MagickCore/draw.h"
54#include "MagickCore/effect.h"
55#include "MagickCore/enhance.h"
56#include "MagickCore/exception.h"
57#include "MagickCore/exception-private.h"
58#include "MagickCore/fx.h"
59#include "MagickCore/fx-private.h"
60#include "MagickCore/gem.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/layer.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/image.h"
66#include "MagickCore/image-private.h"
67#include "MagickCore/magick.h"
68#include "MagickCore/memory_.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/property.h"
75#include "MagickCore/quantum.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/random_.h"
78#include "MagickCore/random-private.h"
79#include "MagickCore/resample.h"
80#include "MagickCore/resample-private.h"
81#include "MagickCore/resize.h"
82#include "MagickCore/shear.h"
83#include "MagickCore/splay-tree.h"
84#include "MagickCore/statistic.h"
85#include "MagickCore/string_.h"
86#include "MagickCore/string-private.h"
87#include "MagickCore/thread-private.h"
88#include "MagickCore/transform.h"
89#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000090
91/*
92 Define declarations.
93*/
94#define LeftShiftOperator 0xf5
95#define RightShiftOperator 0xf6
96#define LessThanEqualOperator 0xf7
97#define GreaterThanEqualOperator 0xf8
98#define EqualOperator 0xf9
99#define NotEqualOperator 0xfa
100#define LogicalAndOperator 0xfb
101#define LogicalOrOperator 0xfc
cristy116af162010-08-13 01:25:47 +0000102#define ExponentialNotation 0xfd
cristy3ed852e2009-09-05 21:47:34 +0000103
104struct _FxInfo
105{
106 const Image
107 *images;
108
cristy3ed852e2009-09-05 21:47:34 +0000109 char
110 *expression;
111
112 FILE
113 *file;
114
115 SplayTreeInfo
116 *colors,
117 *symbols;
118
cristyd76c51e2011-03-26 00:21:26 +0000119 CacheView
120 **view;
cristy3ed852e2009-09-05 21:47:34 +0000121
122 RandomInfo
123 *random_info;
124
125 ExceptionInfo
126 *exception;
127};
128
129/*
130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
131% %
132% %
133% %
134+ A c q u i r e F x I n f o %
135% %
136% %
137% %
138%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
139%
140% AcquireFxInfo() allocates the FxInfo structure.
141%
142% The format of the AcquireFxInfo method is:
143%
144% FxInfo *AcquireFxInfo(Image *image,const char *expression)
cristy0a9b3722010-10-23 18:45:49 +0000145%
cristy3ed852e2009-09-05 21:47:34 +0000146% 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
cristycb180922011-03-11 14:41:24 +0000158 const Image
159 *next;
160
cristy3ed852e2009-09-05 21:47:34 +0000161 FxInfo
162 *fx_info;
163
cristybb503372010-05-27 20:51:26 +0000164 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000165 i;
166
cristy73bd4a52010-10-05 11:24:23 +0000167 fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +0000168 if (fx_info == (FxInfo *) NULL)
169 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
170 (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
171 fx_info->exception=AcquireExceptionInfo();
anthony7d86e172011-03-23 12:37:06 +0000172 fx_info->images=image;
cristy3ed852e2009-09-05 21:47:34 +0000173 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
174 RelinquishMagickMemory);
175 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
176 RelinquishMagickMemory);
cristyd76c51e2011-03-26 00:21:26 +0000177 fx_info->view=(CacheView **) AcquireQuantumMemory(GetImageListLength(
178 fx_info->images),sizeof(*fx_info->view));
179 if (fx_info->view == (CacheView **) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000180 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristya2262262011-03-11 02:50:37 +0000181 i=0;
cristy0ea377f2011-03-24 00:54:19 +0000182 next=GetFirstImageInList(fx_info->images);
183 for ( ; next != (Image *) NULL; next=next->next)
cristy3ed852e2009-09-05 21:47:34 +0000184 {
cristyd76c51e2011-03-26 00:21:26 +0000185 fx_info->view[i]=AcquireCacheView(next);
cristya2262262011-03-11 02:50:37 +0000186 i++;
cristy3ed852e2009-09-05 21:47:34 +0000187 }
188 fx_info->random_info=AcquireRandomInfo();
189 fx_info->expression=ConstantString(expression);
190 fx_info->file=stderr;
191 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
cristy37af0912011-05-23 16:09:42 +0000192 /*
193 Force right-to-left associativity for unary negation.
194 */
195 (void) SubstituteString(&fx_info->expression,"-","-1.0*");
cristy3ed852e2009-09-05 21:47:34 +0000196 if ((strstr(fx_info->expression,"e+") != (char *) NULL) ||
197 (strstr(fx_info->expression,"e-") != (char *) NULL))
198 {
199 /*
cristy116af162010-08-13 01:25:47 +0000200 Convert scientific notation.
cristy3ed852e2009-09-05 21:47:34 +0000201 */
cristy116af162010-08-13 01:25:47 +0000202 (void) SubstituteString(&fx_info->expression,"0e+","0**10^");
203 (void) SubstituteString(&fx_info->expression,"1e+","1**10^");
204 (void) SubstituteString(&fx_info->expression,"2e+","2**10^");
205 (void) SubstituteString(&fx_info->expression,"3e+","3**10^");
206 (void) SubstituteString(&fx_info->expression,"4e+","4**10^");
207 (void) SubstituteString(&fx_info->expression,"5e+","5**10^");
208 (void) SubstituteString(&fx_info->expression,"6e+","6**10^");
209 (void) SubstituteString(&fx_info->expression,"7e+","7**10^");
210 (void) SubstituteString(&fx_info->expression,"8e+","8**10^");
211 (void) SubstituteString(&fx_info->expression,"9e+","9**10^");
cristy2ffa8cd2011-05-24 12:45:55 +0000212 (void) SubstituteString(&fx_info->expression,"0e-1.0*","0**10^-");
cristy37af0912011-05-23 16:09:42 +0000213 (void) SubstituteString(&fx_info->expression,"1e-1.0*","1**10^-");
214 (void) SubstituteString(&fx_info->expression,"2e-1.0*","2**10^-");
215 (void) SubstituteString(&fx_info->expression,"3e-1.0*","3**10^-");
216 (void) SubstituteString(&fx_info->expression,"4e-1.0*","4**10^-");
217 (void) SubstituteString(&fx_info->expression,"5e-1.0*","5**10^-");
218 (void) SubstituteString(&fx_info->expression,"6e-1.0*","6**10^-");
219 (void) SubstituteString(&fx_info->expression,"7e-1.0*","7**10^-");
220 (void) SubstituteString(&fx_info->expression,"8e-1.0*","8**10^-");
221 (void) SubstituteString(&fx_info->expression,"9e-1.0*","9**10^-");
cristy3ed852e2009-09-05 21:47:34 +0000222 }
cristy37af0912011-05-23 16:09:42 +0000223 if ((strstr(fx_info->expression,"E+") != (char *) NULL) ||
224 (strstr(fx_info->expression,"E-") != (char *) NULL))
225 {
226 /*
227 Convert scientific notation.
228 */
229 (void) SubstituteString(&fx_info->expression,"0E+","0**10^");
230 (void) SubstituteString(&fx_info->expression,"1E+","1**10^");
231 (void) SubstituteString(&fx_info->expression,"2E+","2**10^");
232 (void) SubstituteString(&fx_info->expression,"3E+","3**10^");
233 (void) SubstituteString(&fx_info->expression,"4E+","4**10^");
234 (void) SubstituteString(&fx_info->expression,"5E+","5**10^");
235 (void) SubstituteString(&fx_info->expression,"6E+","6**10^");
236 (void) SubstituteString(&fx_info->expression,"7E+","7**10^");
237 (void) SubstituteString(&fx_info->expression,"8E+","8**10^");
238 (void) SubstituteString(&fx_info->expression,"9E+","9**10^");
cristy2ffa8cd2011-05-24 12:45:55 +0000239 (void) SubstituteString(&fx_info->expression,"0E-1.0*","0**10^-");
cristy37af0912011-05-23 16:09:42 +0000240 (void) SubstituteString(&fx_info->expression,"1E-1.0*","1**10^-");
241 (void) SubstituteString(&fx_info->expression,"2E-1.0*","2**10^-");
242 (void) SubstituteString(&fx_info->expression,"3E-1.0*","3**10^-");
243 (void) SubstituteString(&fx_info->expression,"4E-1.0*","4**10^-");
244 (void) SubstituteString(&fx_info->expression,"5E-1.0*","5**10^-");
245 (void) SubstituteString(&fx_info->expression,"6E-1.0*","6**10^-");
246 (void) SubstituteString(&fx_info->expression,"7E-1.0*","7**10^-");
247 (void) SubstituteString(&fx_info->expression,"8E-1.0*","8**10^-");
248 (void) SubstituteString(&fx_info->expression,"9E-1.0*","9**10^-");
249 }
cristy8b8a3ae2010-10-23 18:49:46 +0000250 /*
cristy3ed852e2009-09-05 21:47:34 +0000251 Convert complex to simple operators.
252 */
253 fx_op[1]='\0';
254 *fx_op=(char) LeftShiftOperator;
255 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
256 *fx_op=(char) RightShiftOperator;
257 (void) SubstituteString(&fx_info->expression,">>",fx_op);
258 *fx_op=(char) LessThanEqualOperator;
259 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
260 *fx_op=(char) GreaterThanEqualOperator;
261 (void) SubstituteString(&fx_info->expression,">=",fx_op);
262 *fx_op=(char) EqualOperator;
263 (void) SubstituteString(&fx_info->expression,"==",fx_op);
264 *fx_op=(char) NotEqualOperator;
265 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
266 *fx_op=(char) LogicalAndOperator;
267 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
268 *fx_op=(char) LogicalOrOperator;
269 (void) SubstituteString(&fx_info->expression,"||",fx_op);
cristy116af162010-08-13 01:25:47 +0000270 *fx_op=(char) ExponentialNotation;
271 (void) SubstituteString(&fx_info->expression,"**",fx_op);
cristy3ed852e2009-09-05 21:47:34 +0000272 return(fx_info);
273}
274
275/*
276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
277% %
278% %
279% %
280% A d d N o i s e I m a g e %
281% %
282% %
283% %
284%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
285%
286% AddNoiseImage() adds random noise to the image.
287%
288% The format of the AddNoiseImage method is:
289%
290% Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
291% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000292%
293% A description of each parameter follows:
294%
295% o image: the image.
296%
297% o channel: the channel type.
298%
299% o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
300% Impulse, Laplacian, or Poisson.
301%
302% o exception: return any errors or warnings in this structure.
303%
304*/
cristy490408a2011-07-07 14:42:05 +0000305MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type, ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000306{
307#define AddNoiseImageTag "AddNoise/Image"
308
cristyfa112112010-01-04 17:48:07 +0000309 CacheView
310 *image_view,
311 *noise_view;
312
cristy3ed852e2009-09-05 21:47:34 +0000313 const char
314 *option;
315
316 Image
317 *noise_image;
318
cristy3ed852e2009-09-05 21:47:34 +0000319 MagickBooleanType
320 status;
321
cristybb503372010-05-27 20:51:26 +0000322 MagickOffsetType
323 progress;
324
cristy3ed852e2009-09-05 21:47:34 +0000325 MagickRealType
326 attenuate;
327
328 RandomInfo
cristyfa112112010-01-04 17:48:07 +0000329 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +0000330
cristybb503372010-05-27 20:51:26 +0000331 ssize_t
332 y;
333
cristy3ed852e2009-09-05 21:47:34 +0000334 /*
335 Initialize noise image attributes.
336 */
337 assert(image != (const Image *) NULL);
338 assert(image->signature == MagickSignature);
339 if (image->debug != MagickFalse)
340 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
341 assert(exception != (ExceptionInfo *) NULL);
342 assert(exception->signature == MagickSignature);
cristyb3a73b52011-07-26 01:34:43 +0000343 noise_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000344 if (noise_image == (Image *) NULL)
345 return((Image *) NULL);
346 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
347 {
348 InheritException(exception,&noise_image->exception);
349 noise_image=DestroyImage(noise_image);
350 return((Image *) NULL);
351 }
352 /*
353 Add noise in each row.
354 */
355 attenuate=1.0;
356 option=GetImageArtifact(image,"attenuate");
357 if (option != (char *) NULL)
cristyc1acd842011-05-19 23:05:47 +0000358 attenuate=InterpretLocaleValue(option,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000359 status=MagickTrue;
360 progress=0;
361 random_info=AcquireRandomInfoThreadSet();
362 image_view=AcquireCacheView(image);
363 noise_view=AcquireCacheView(noise_image);
cristy319a1e72010-02-21 15:13:11 +0000364#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000365 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000366#endif
cristybb503372010-05-27 20:51:26 +0000367 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000368 {
cristy5c9e6f22010-09-17 17:31:01 +0000369 const int
370 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000371
cristy3ed852e2009-09-05 21:47:34 +0000372 MagickBooleanType
373 sync;
374
cristy4c08aed2011-07-01 19:47:50 +0000375 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000376 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000377
cristybb503372010-05-27 20:51:26 +0000378 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000379 x;
380
cristy4c08aed2011-07-01 19:47:50 +0000381 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000382 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000383
384 if (status == MagickFalse)
385 continue;
386 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
387 q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
388 exception);
cristy4c08aed2011-07-01 19:47:50 +0000389 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000390 {
391 status=MagickFalse;
392 continue;
393 }
cristybb503372010-05-27 20:51:26 +0000394 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000395 {
cristyb3a73b52011-07-26 01:34:43 +0000396 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
397 SetPixelRed(noise_image,ClampToQuantum(GenerateDifferentialNoise(
398 random_info[id],GetPixelRed(image,p),noise_type,attenuate)),q);
399 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
400 SetPixelGreen(noise_image,ClampToQuantum(GenerateDifferentialNoise(
401 random_info[id],GetPixelGreen(image,p),noise_type,attenuate)),q);
402 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
403 SetPixelBlue(noise_image,ClampToQuantum(GenerateDifferentialNoise(
404 random_info[id],GetPixelBlue(image,p),noise_type,attenuate)),q);
405 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
406 (image->colorspace == CMYKColorspace))
407 SetPixelBlack(noise_image,ClampToQuantum(GenerateDifferentialNoise(
408 random_info[id],GetPixelBlack(image,p),noise_type,attenuate)),q);
409 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
410 SetPixelAlpha(noise_image,ClampToQuantum(GenerateDifferentialNoise(
411 random_info[id],GetPixelAlpha(image,p),noise_type,attenuate)),q);
cristyed231572011-07-14 02:18:59 +0000412 p+=GetPixelChannels(image);
413 q+=GetPixelChannels(noise_image);
cristy3ed852e2009-09-05 21:47:34 +0000414 }
415 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
416 if (sync == MagickFalse)
417 status=MagickFalse;
418 if (image->progress_monitor != (MagickProgressMonitor) NULL)
419 {
420 MagickBooleanType
421 proceed;
422
cristyb5d5f722009-11-04 03:03:49 +0000423#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy319a1e72010-02-21 15:13:11 +0000424 #pragma omp critical (MagickCore_AddNoiseImage)
cristy3ed852e2009-09-05 21:47:34 +0000425#endif
426 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
427 image->rows);
428 if (proceed == MagickFalse)
429 status=MagickFalse;
430 }
431 }
432 noise_view=DestroyCacheView(noise_view);
433 image_view=DestroyCacheView(image_view);
434 random_info=DestroyRandomInfoThreadSet(random_info);
435 if (status == MagickFalse)
436 noise_image=DestroyImage(noise_image);
437 return(noise_image);
438}
439
440/*
441%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
442% %
443% %
444% %
445% B l u e S h i f t I m a g e %
446% %
447% %
448% %
449%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
450%
451% BlueShiftImage() mutes the colors of the image to simulate a scene at
452% nighttime in the moonlight.
453%
454% The format of the BlueShiftImage method is:
455%
456% Image *BlueShiftImage(const Image *image,const double factor,
457% ExceptionInfo *exception)
458%
459% A description of each parameter follows:
460%
461% o image: the image.
462%
463% o factor: the shift factor.
464%
465% o exception: return any errors or warnings in this structure.
466%
467*/
468MagickExport Image *BlueShiftImage(const Image *image,const double factor,
469 ExceptionInfo *exception)
470{
471#define BlueShiftImageTag "BlueShift/Image"
472
cristyc4c8d132010-01-07 01:58:38 +0000473 CacheView
474 *image_view,
475 *shift_view;
476
cristy3ed852e2009-09-05 21:47:34 +0000477 Image
478 *shift_image;
479
cristy3ed852e2009-09-05 21:47:34 +0000480 MagickBooleanType
481 status;
482
cristybb503372010-05-27 20:51:26 +0000483 MagickOffsetType
484 progress;
485
486 ssize_t
487 y;
488
cristy3ed852e2009-09-05 21:47:34 +0000489 /*
490 Allocate blue shift image.
491 */
492 assert(image != (const Image *) NULL);
493 assert(image->signature == MagickSignature);
494 if (image->debug != MagickFalse)
495 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
496 assert(exception != (ExceptionInfo *) NULL);
497 assert(exception->signature == MagickSignature);
498 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,
499 exception);
500 if (shift_image == (Image *) NULL)
501 return((Image *) NULL);
502 if (SetImageStorageClass(shift_image,DirectClass) == MagickFalse)
503 {
504 InheritException(exception,&shift_image->exception);
505 shift_image=DestroyImage(shift_image);
506 return((Image *) NULL);
507 }
508 /*
509 Blue-shift DirectClass image.
510 */
511 status=MagickTrue;
512 progress=0;
513 image_view=AcquireCacheView(image);
514 shift_view=AcquireCacheView(shift_image);
cristy319a1e72010-02-21 15:13:11 +0000515#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000516 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000517#endif
cristybb503372010-05-27 20:51:26 +0000518 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000519 {
520 MagickBooleanType
521 sync;
522
cristy4c08aed2011-07-01 19:47:50 +0000523 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000524 pixel;
525
526 Quantum
527 quantum;
528
cristy4c08aed2011-07-01 19:47:50 +0000529 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000530 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000531
cristybb503372010-05-27 20:51:26 +0000532 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000533 x;
534
cristy4c08aed2011-07-01 19:47:50 +0000535 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000536 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000537
538 if (status == MagickFalse)
539 continue;
540 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
541 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
542 exception);
cristy4c08aed2011-07-01 19:47:50 +0000543 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000544 {
545 status=MagickFalse;
546 continue;
547 }
cristybb503372010-05-27 20:51:26 +0000548 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000549 {
cristy4c08aed2011-07-01 19:47:50 +0000550 quantum=GetPixelRed(image,p);
551 if (GetPixelGreen(image,p) < quantum)
552 quantum=GetPixelGreen(image,p);
553 if (GetPixelBlue(image,p) < quantum)
554 quantum=GetPixelBlue(image,p);
555 pixel.red=0.5*(GetPixelRed(image,p)+factor*quantum);
556 pixel.green=0.5*(GetPixelGreen(image,p)+factor*quantum);
557 pixel.blue=0.5*(GetPixelBlue(image,p)+factor*quantum);
558 quantum=GetPixelRed(image,p);
559 if (GetPixelGreen(image,p) > quantum)
560 quantum=GetPixelGreen(image,p);
561 if (GetPixelBlue(image,p) > quantum)
562 quantum=GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000563 pixel.red=0.5*(pixel.red+factor*quantum);
564 pixel.green=0.5*(pixel.green+factor*quantum);
565 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristy4c08aed2011-07-01 19:47:50 +0000566 SetPixelRed(shift_image,ClampToQuantum(pixel.red),q);
567 SetPixelGreen(shift_image,ClampToQuantum(pixel.green),q);
568 SetPixelBlue(shift_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000569 p+=GetPixelChannels(image);
570 q+=GetPixelChannels(shift_image);
cristy3ed852e2009-09-05 21:47:34 +0000571 }
572 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
573 if (sync == MagickFalse)
574 status=MagickFalse;
575 if (image->progress_monitor != (MagickProgressMonitor) NULL)
576 {
577 MagickBooleanType
578 proceed;
579
cristy319a1e72010-02-21 15:13:11 +0000580#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000581 #pragma omp critical (MagickCore_BlueShiftImage)
582#endif
583 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
584 image->rows);
585 if (proceed == MagickFalse)
586 status=MagickFalse;
587 }
588 }
589 image_view=DestroyCacheView(image_view);
590 shift_view=DestroyCacheView(shift_view);
591 if (status == MagickFalse)
592 shift_image=DestroyImage(shift_image);
593 return(shift_image);
594}
595
596/*
597%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
598% %
599% %
600% %
601% C h a r c o a l I m a g e %
602% %
603% %
604% %
605%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
606%
607% CharcoalImage() creates a new image that is a copy of an existing one with
608% the edge highlighted. It allocates the memory necessary for the new Image
609% structure and returns a pointer to the new image.
610%
611% The format of the CharcoalImage method is:
612%
613% Image *CharcoalImage(const Image *image,const double radius,
614% const double sigma,ExceptionInfo *exception)
615%
616% A description of each parameter follows:
617%
618% o image: the image.
619%
620% o radius: the radius of the pixel neighborhood.
621%
622% o sigma: the standard deviation of the Gaussian, in pixels.
623%
624% o exception: return any errors or warnings in this structure.
625%
626*/
627MagickExport Image *CharcoalImage(const Image *image,const double radius,
628 const double sigma,ExceptionInfo *exception)
629{
630 Image
631 *charcoal_image,
632 *clone_image,
633 *edge_image;
634
635 assert(image != (Image *) NULL);
636 assert(image->signature == MagickSignature);
637 if (image->debug != MagickFalse)
638 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
639 assert(exception != (ExceptionInfo *) NULL);
640 assert(exception->signature == MagickSignature);
641 clone_image=CloneImage(image,0,0,MagickTrue,exception);
642 if (clone_image == (Image *) NULL)
643 return((Image *) NULL);
644 (void) SetImageType(clone_image,GrayscaleType);
645 edge_image=EdgeImage(clone_image,radius,exception);
646 clone_image=DestroyImage(clone_image);
647 if (edge_image == (Image *) NULL)
648 return((Image *) NULL);
649 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
650 edge_image=DestroyImage(edge_image);
651 if (charcoal_image == (Image *) NULL)
652 return((Image *) NULL);
653 (void) NormalizeImage(charcoal_image);
cristyb3e7c6c2011-07-24 01:43:55 +0000654 (void) NegateImage(charcoal_image,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +0000655 (void) SetImageType(charcoal_image,GrayscaleType);
656 return(charcoal_image);
657}
658
659/*
660%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661% %
662% %
663% %
664% C o l o r i z e I m a g e %
665% %
666% %
667% %
668%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669%
670% ColorizeImage() blends the fill color with each pixel in the image.
671% A percentage blend is specified with opacity. Control the application
672% of different color components by specifying a different percentage for
673% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
674%
675% The format of the ColorizeImage method is:
676%
677% Image *ColorizeImage(const Image *image,const char *opacity,
678% const PixelPacket colorize,ExceptionInfo *exception)
679%
680% A description of each parameter follows:
681%
682% o image: the image.
683%
684% o opacity: A character string indicating the level of opacity as a
685% percentage.
686%
687% o colorize: A color value.
688%
689% o exception: return any errors or warnings in this structure.
690%
691*/
692MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
693 const PixelPacket colorize,ExceptionInfo *exception)
694{
695#define ColorizeImageTag "Colorize/Image"
696
cristyc4c8d132010-01-07 01:58:38 +0000697 CacheView
698 *colorize_view,
699 *image_view;
700
cristy3ed852e2009-09-05 21:47:34 +0000701 GeometryInfo
702 geometry_info;
703
704 Image
705 *colorize_image;
706
cristy3ed852e2009-09-05 21:47:34 +0000707 MagickBooleanType
708 status;
709
cristybb503372010-05-27 20:51:26 +0000710 MagickOffsetType
711 progress;
712
cristy4c08aed2011-07-01 19:47:50 +0000713 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000714 pixel;
715
716 MagickStatusType
717 flags;
718
cristybb503372010-05-27 20:51:26 +0000719 ssize_t
720 y;
721
cristy3ed852e2009-09-05 21:47:34 +0000722 /*
723 Allocate colorized image.
724 */
725 assert(image != (const Image *) NULL);
726 assert(image->signature == MagickSignature);
727 if (image->debug != MagickFalse)
728 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
729 assert(exception != (ExceptionInfo *) NULL);
730 assert(exception->signature == MagickSignature);
731 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
732 exception);
733 if (colorize_image == (Image *) NULL)
734 return((Image *) NULL);
735 if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
736 {
737 InheritException(exception,&colorize_image->exception);
738 colorize_image=DestroyImage(colorize_image);
739 return((Image *) NULL);
740 }
741 if (opacity == (const char *) NULL)
742 return(colorize_image);
743 /*
744 Determine RGB values of the pen color.
745 */
746 flags=ParseGeometry(opacity,&geometry_info);
747 pixel.red=geometry_info.rho;
748 pixel.green=geometry_info.rho;
749 pixel.blue=geometry_info.rho;
cristy4c08aed2011-07-01 19:47:50 +0000750 pixel.alpha=(MagickRealType) OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +0000751 if ((flags & SigmaValue) != 0)
752 pixel.green=geometry_info.sigma;
753 if ((flags & XiValue) != 0)
754 pixel.blue=geometry_info.xi;
755 if ((flags & PsiValue) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000756 pixel.alpha=geometry_info.psi;
cristy3ed852e2009-09-05 21:47:34 +0000757 /*
758 Colorize DirectClass image.
759 */
760 status=MagickTrue;
761 progress=0;
762 image_view=AcquireCacheView(image);
763 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000764#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000765 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000766#endif
cristybb503372010-05-27 20:51:26 +0000767 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000768 {
769 MagickBooleanType
770 sync;
771
cristy4c08aed2011-07-01 19:47:50 +0000772 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000773 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000774
cristybb503372010-05-27 20:51:26 +0000775 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000776 x;
777
cristy4c08aed2011-07-01 19:47:50 +0000778 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000779 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000780
781 if (status == MagickFalse)
782 continue;
783 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
784 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
785 exception);
cristy4c08aed2011-07-01 19:47:50 +0000786 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000787 {
788 status=MagickFalse;
789 continue;
790 }
cristybb503372010-05-27 20:51:26 +0000791 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000792 {
cristy4c08aed2011-07-01 19:47:50 +0000793 SetPixelRed(colorize_image,ClampToQuantum((GetPixelRed(image,p)*
794 (100.0-pixel.red)+colorize.red*pixel.red)/100.0),q);
795 SetPixelGreen(colorize_image,ClampToQuantum((GetPixelGreen(image,p)*
796 (100.0-pixel.green)+colorize.green*pixel.green)/100.0),q);
797 SetPixelBlue(colorize_image,ClampToQuantum((GetPixelBlue(image,p)*
798 (100.0-pixel.blue)+colorize.blue*pixel.blue)/100.0),q);
799 SetPixelAlpha(colorize_image,ClampToQuantum((GetPixelAlpha(image,p)*
800 (100.0-pixel.alpha)+colorize.alpha*pixel.alpha)/100.0),q);
cristyed231572011-07-14 02:18:59 +0000801 p+=GetPixelChannels(image);
802 q+=GetPixelChannels(colorize_image);
cristy3ed852e2009-09-05 21:47:34 +0000803 }
804 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
805 if (sync == MagickFalse)
806 status=MagickFalse;
807 if (image->progress_monitor != (MagickProgressMonitor) NULL)
808 {
809 MagickBooleanType
810 proceed;
811
cristy319a1e72010-02-21 15:13:11 +0000812#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000813 #pragma omp critical (MagickCore_ColorizeImage)
814#endif
815 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
816 if (proceed == MagickFalse)
817 status=MagickFalse;
818 }
819 }
820 image_view=DestroyCacheView(image_view);
821 colorize_view=DestroyCacheView(colorize_view);
822 if (status == MagickFalse)
823 colorize_image=DestroyImage(colorize_image);
824 return(colorize_image);
825}
826
827/*
828%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
829% %
830% %
831% %
cristye6365592010-04-02 17:31:23 +0000832% C o l o r M a t r i x I m a g e %
833% %
834% %
835% %
836%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
837%
838% ColorMatrixImage() applies color transformation to an image. This method
839% permits saturation changes, hue rotation, luminance to alpha, and various
840% other effects. Although variable-sized transformation matrices can be used,
841% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
842% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
843% except offsets are in column 6 rather than 5 (in support of CMYKA images)
844% and offsets are normalized (divide Flash offset by 255).
845%
846% The format of the ColorMatrixImage method is:
847%
848% Image *ColorMatrixImage(const Image *image,
849% const KernelInfo *color_matrix,ExceptionInfo *exception)
850%
851% A description of each parameter follows:
852%
853% o image: the image.
854%
855% o color_matrix: the color matrix.
856%
857% o exception: return any errors or warnings in this structure.
858%
859*/
860MagickExport Image *ColorMatrixImage(const Image *image,
861 const KernelInfo *color_matrix,ExceptionInfo *exception)
862{
863#define ColorMatrixImageTag "ColorMatrix/Image"
864
865 CacheView
866 *color_view,
867 *image_view;
868
869 double
870 ColorMatrix[6][6] =
871 {
872 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
873 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
874 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
875 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
876 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
877 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
878 };
879
880 Image
881 *color_image;
882
cristye6365592010-04-02 17:31:23 +0000883 MagickBooleanType
884 status;
885
cristybb503372010-05-27 20:51:26 +0000886 MagickOffsetType
887 progress;
888
889 register ssize_t
cristye6365592010-04-02 17:31:23 +0000890 i;
891
cristybb503372010-05-27 20:51:26 +0000892 ssize_t
893 u,
894 v,
895 y;
896
cristye6365592010-04-02 17:31:23 +0000897 /*
898 Create color matrix.
899 */
900 assert(image != (Image *) NULL);
901 assert(image->signature == MagickSignature);
902 if (image->debug != MagickFalse)
903 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
904 assert(exception != (ExceptionInfo *) NULL);
905 assert(exception->signature == MagickSignature);
906 i=0;
cristybb503372010-05-27 20:51:26 +0000907 for (v=0; v < (ssize_t) color_matrix->height; v++)
908 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000909 {
910 if ((v < 6) && (u < 6))
911 ColorMatrix[v][u]=color_matrix->values[i];
912 i++;
913 }
914 /*
915 Initialize color image.
916 */
cristy12550e62010-06-07 12:46:40 +0000917 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000918 if (color_image == (Image *) NULL)
919 return((Image *) NULL);
920 if (SetImageStorageClass(color_image,DirectClass) == MagickFalse)
921 {
922 InheritException(exception,&color_image->exception);
923 color_image=DestroyImage(color_image);
924 return((Image *) NULL);
925 }
926 if (image->debug != MagickFalse)
927 {
928 char
929 format[MaxTextExtent],
930 *message;
931
932 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
933 " ColorMatrix image with color matrix:");
934 message=AcquireString("");
935 for (v=0; v < 6; v++)
936 {
937 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000938 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000939 (void) ConcatenateString(&message,format);
940 for (u=0; u < 6; u++)
941 {
cristyb51dff52011-05-19 16:55:47 +0000942 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
cristye6365592010-04-02 17:31:23 +0000943 ColorMatrix[v][u]);
944 (void) ConcatenateString(&message,format);
945 }
946 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
947 }
948 message=DestroyString(message);
949 }
950 /*
951 ColorMatrix image.
952 */
953 status=MagickTrue;
954 progress=0;
955 image_view=AcquireCacheView(image);
956 color_view=AcquireCacheView(color_image);
957#if defined(MAGICKCORE_OPENMP_SUPPORT)
958 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
959#endif
cristybb503372010-05-27 20:51:26 +0000960 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000961 {
962 MagickRealType
963 pixel;
964
cristy4c08aed2011-07-01 19:47:50 +0000965 register const Quantum
cristye6365592010-04-02 17:31:23 +0000966 *restrict p;
967
cristy4c08aed2011-07-01 19:47:50 +0000968 register Quantum
969 *restrict q;
970
cristybb503372010-05-27 20:51:26 +0000971 register ssize_t
cristye6365592010-04-02 17:31:23 +0000972 x;
973
cristye6365592010-04-02 17:31:23 +0000974 if (status == MagickFalse)
975 continue;
976 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
977 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
978 exception);
cristy4c08aed2011-07-01 19:47:50 +0000979 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristye6365592010-04-02 17:31:23 +0000980 {
981 status=MagickFalse;
982 continue;
983 }
cristybb503372010-05-27 20:51:26 +0000984 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +0000985 {
cristybb503372010-05-27 20:51:26 +0000986 register ssize_t
cristye6365592010-04-02 17:31:23 +0000987 v;
988
cristybb503372010-05-27 20:51:26 +0000989 size_t
cristye6365592010-04-02 17:31:23 +0000990 height;
991
992 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +0000993 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +0000994 {
cristy4c08aed2011-07-01 19:47:50 +0000995 pixel=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
996 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +0000997 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000998 pixel+=ColorMatrix[v][3]*GetPixelBlack(image,p);
999 if (image->matte != MagickFalse)
1000 pixel+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
cristye6365592010-04-02 17:31:23 +00001001 pixel+=QuantumRange*ColorMatrix[v][5];
1002 switch (v)
1003 {
cristy4c08aed2011-07-01 19:47:50 +00001004 case 0: SetPixelRed(color_image,ClampToQuantum(pixel),q); break;
1005 case 1: SetPixelGreen(color_image,ClampToQuantum(pixel),q); break;
1006 case 2: SetPixelBlue(color_image,ClampToQuantum(pixel),q); break;
cristye6365592010-04-02 17:31:23 +00001007 case 3:
1008 {
cristy4c08aed2011-07-01 19:47:50 +00001009 if (image->colorspace == CMYKColorspace)
1010 SetPixelBlack(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001011 break;
1012 }
1013 case 4:
1014 {
cristy4c08aed2011-07-01 19:47:50 +00001015 if (image->matte != MagickFalse)
1016 SetPixelAlpha(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001017 break;
1018 }
1019 }
1020 }
cristyed231572011-07-14 02:18:59 +00001021 p+=GetPixelChannels(image);
1022 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +00001023 }
1024 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1025 status=MagickFalse;
1026 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1027 {
1028 MagickBooleanType
1029 proceed;
1030
1031#if defined(MAGICKCORE_OPENMP_SUPPORT)
1032 #pragma omp critical (MagickCore_ColorMatrixImage)
1033#endif
1034 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1035 image->rows);
1036 if (proceed == MagickFalse)
1037 status=MagickFalse;
1038 }
1039 }
1040 color_view=DestroyCacheView(color_view);
1041 image_view=DestroyCacheView(image_view);
1042 if (status == MagickFalse)
1043 color_image=DestroyImage(color_image);
1044 return(color_image);
1045}
1046
1047/*
1048%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1049% %
1050% %
1051% %
cristy3ed852e2009-09-05 21:47:34 +00001052+ D e s t r o y F x I n f o %
1053% %
1054% %
1055% %
1056%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1057%
1058% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1059%
1060% The format of the DestroyFxInfo method is:
1061%
1062% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1063%
1064% A description of each parameter follows:
1065%
1066% o fx_info: the fx info.
1067%
1068*/
1069MagickExport FxInfo *DestroyFxInfo(FxInfo *fx_info)
1070{
cristybb503372010-05-27 20:51:26 +00001071 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001072 i;
1073
1074 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1075 fx_info->expression=DestroyString(fx_info->expression);
1076 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1077 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001078 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001079 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1080 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001081 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1082 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1083 return(fx_info);
1084}
1085
1086/*
1087%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1088% %
1089% %
1090% %
cristy3ed852e2009-09-05 21:47:34 +00001091+ 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 %
1092% %
1093% %
1094% %
1095%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1096%
1097% FxEvaluateChannelExpression() evaluates an expression and returns the
1098% results.
1099%
1100% The format of the FxEvaluateExpression method is:
1101%
1102% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001103% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001104% MagickRealType *alpha,Exceptioninfo *exception)
1105% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1106% MagickRealType *alpha,Exceptioninfo *exception)
1107%
1108% A description of each parameter follows:
1109%
1110% o fx_info: the fx info.
1111%
1112% o channel: the channel.
1113%
1114% o x,y: the pixel position.
1115%
1116% o alpha: the result.
1117%
1118% o exception: return any errors or warnings in this structure.
1119%
1120*/
1121
cristy351842f2010-03-07 15:27:38 +00001122static inline double MagickMax(const double x,const double y)
1123{
1124 if (x > y)
1125 return(x);
1126 return(y);
1127}
1128
1129static inline double MagickMin(const double x,const double y)
1130{
1131 if (x < y)
1132 return(x);
1133 return(y);
1134}
1135
cristy3ed852e2009-09-05 21:47:34 +00001136static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001137 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001138{
1139 char
1140 key[MaxTextExtent],
1141 statistic[MaxTextExtent];
1142
1143 const char
1144 *value;
1145
1146 register const char
1147 *p;
1148
1149 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1150 if (*p == '.')
1151 switch (*++p) /* e.g. depth.r */
1152 {
1153 case 'r': channel=RedChannel; break;
1154 case 'g': channel=GreenChannel; break;
1155 case 'b': channel=BlueChannel; break;
1156 case 'c': channel=CyanChannel; break;
1157 case 'm': channel=MagentaChannel; break;
1158 case 'y': channel=YellowChannel; break;
1159 case 'k': channel=BlackChannel; break;
1160 default: break;
1161 }
cristyb51dff52011-05-19 16:55:47 +00001162 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001163 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001164 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1165 if (value != (const char *) NULL)
cristyc1acd842011-05-19 23:05:47 +00001166 return(QuantumScale*InterpretLocaleValue(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001167 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1168 if (LocaleNCompare(symbol,"depth",5) == 0)
1169 {
cristybb503372010-05-27 20:51:26 +00001170 size_t
cristy3ed852e2009-09-05 21:47:34 +00001171 depth;
1172
cristyfefab1b2011-07-05 00:33:22 +00001173 depth=GetImageDepth(image,exception);
1174 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001175 }
1176 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1177 {
1178 double
1179 kurtosis,
1180 skewness;
1181
cristyd42d9952011-07-08 14:21:50 +00001182 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001183 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001184 }
1185 if (LocaleNCompare(symbol,"maxima",6) == 0)
1186 {
1187 double
1188 maxima,
1189 minima;
1190
cristyd42d9952011-07-08 14:21:50 +00001191 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001192 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001193 }
1194 if (LocaleNCompare(symbol,"mean",4) == 0)
1195 {
1196 double
1197 mean,
1198 standard_deviation;
1199
cristyd42d9952011-07-08 14:21:50 +00001200 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001201 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001202 }
1203 if (LocaleNCompare(symbol,"minima",6) == 0)
1204 {
1205 double
1206 maxima,
1207 minima;
1208
cristyd42d9952011-07-08 14:21:50 +00001209 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001210 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001211 }
1212 if (LocaleNCompare(symbol,"skewness",8) == 0)
1213 {
1214 double
1215 kurtosis,
1216 skewness;
1217
cristyd42d9952011-07-08 14:21:50 +00001218 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001219 (void) FormatLocaleString(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
cristyd42d9952011-07-08 14:21:50 +00001227 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001228 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001229 standard_deviation);
1230 }
1231 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1232 ConstantString(statistic));
cristyc1acd842011-05-19 23:05:47 +00001233 return(QuantumScale*InterpretLocaleValue(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001234}
1235
1236static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001237 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001238 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001239
cristy0568ffc2011-07-25 16:54:14 +00001240static inline MagickRealType FxMax(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001241 const ssize_t x,const ssize_t y,const char *expression,
1242 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001243{
1244 MagickRealType
1245 alpha,
1246 beta;
1247
1248 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1249 return((MagickRealType) MagickMax((double) alpha,(double) beta));
1250}
1251
cristy0568ffc2011-07-25 16:54:14 +00001252static inline MagickRealType FxMin(FxInfo *fx_info,PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001253 const ssize_t x,const ssize_t y,const char *expression,
1254 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001255{
1256 MagickRealType
1257 alpha,
1258 beta;
1259
1260 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1261 return((MagickRealType) MagickMin((double) alpha,(double) beta));
1262}
1263
1264static inline const char *FxSubexpression(const char *expression,
1265 ExceptionInfo *exception)
1266{
1267 const char
1268 *subexpression;
1269
cristybb503372010-05-27 20:51:26 +00001270 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001271 level;
1272
1273 level=0;
1274 subexpression=expression;
1275 while ((*subexpression != '\0') &&
1276 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1277 {
1278 if (strchr("(",(int) *subexpression) != (char *) NULL)
1279 level++;
1280 else
1281 if (strchr(")",(int) *subexpression) != (char *) NULL)
1282 level--;
1283 subexpression++;
1284 }
1285 if (*subexpression == '\0')
1286 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1287 "UnbalancedParenthesis","`%s'",expression);
1288 return(subexpression);
1289}
1290
cristy0568ffc2011-07-25 16:54:14 +00001291static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001292 const ssize_t x,const ssize_t y,const char *expression,
1293 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001294{
1295 char
1296 *q,
1297 subexpression[MaxTextExtent],
1298 symbol[MaxTextExtent];
1299
1300 const char
1301 *p,
1302 *value;
1303
1304 Image
1305 *image;
1306
cristy4c08aed2011-07-01 19:47:50 +00001307 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001308 pixel;
1309
1310 MagickRealType
1311 alpha,
1312 beta;
1313
1314 PointInfo
1315 point;
1316
cristybb503372010-05-27 20:51:26 +00001317 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001318 i;
1319
1320 size_t
1321 length;
1322
cristybb503372010-05-27 20:51:26 +00001323 size_t
cristy3ed852e2009-09-05 21:47:34 +00001324 level;
1325
1326 p=expression;
1327 i=GetImageIndexInList(fx_info->images);
1328 level=0;
1329 point.x=(double) x;
1330 point.y=(double) y;
1331 if (isalpha((int) *(p+1)) == 0)
1332 {
1333 if (strchr("suv",(int) *p) != (char *) NULL)
1334 {
1335 switch (*p)
1336 {
1337 case 's':
1338 default:
1339 {
1340 i=GetImageIndexInList(fx_info->images);
1341 break;
1342 }
1343 case 'u': i=0; break;
1344 case 'v': i=1; break;
1345 }
1346 p++;
1347 if (*p == '[')
1348 {
1349 level++;
1350 q=subexpression;
1351 for (p++; *p != '\0'; )
1352 {
1353 if (*p == '[')
1354 level++;
1355 else
1356 if (*p == ']')
1357 {
1358 level--;
1359 if (level == 0)
1360 break;
1361 }
1362 *q++=(*p++);
1363 }
1364 *q='\0';
1365 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1366 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001367 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001368 p++;
1369 }
1370 if (*p == '.')
1371 p++;
1372 }
1373 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1374 {
1375 p++;
1376 if (*p == '{')
1377 {
1378 level++;
1379 q=subexpression;
1380 for (p++; *p != '\0'; )
1381 {
1382 if (*p == '{')
1383 level++;
1384 else
1385 if (*p == '}')
1386 {
1387 level--;
1388 if (level == 0)
1389 break;
1390 }
1391 *q++=(*p++);
1392 }
1393 *q='\0';
1394 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1395 &beta,exception);
1396 point.x=alpha;
1397 point.y=beta;
1398 p++;
1399 }
1400 else
1401 if (*p == '[')
1402 {
1403 level++;
1404 q=subexpression;
1405 for (p++; *p != '\0'; )
1406 {
1407 if (*p == '[')
1408 level++;
1409 else
1410 if (*p == ']')
1411 {
1412 level--;
1413 if (level == 0)
1414 break;
1415 }
1416 *q++=(*p++);
1417 }
1418 *q='\0';
1419 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1420 &beta,exception);
1421 point.x+=alpha;
1422 point.y+=beta;
1423 p++;
1424 }
1425 if (*p == '.')
1426 p++;
1427 }
1428 }
1429 length=GetImageListLength(fx_info->images);
1430 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001431 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001432 i%=length;
1433 image=GetImageFromList(fx_info->images,i);
1434 if (image == (Image *) NULL)
1435 {
1436 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1437 "NoSuchImage","`%s'",expression);
1438 return(0.0);
1439 }
cristy4c08aed2011-07-01 19:47:50 +00001440 GetPixelInfo(image,&pixel);
1441 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001442 point.x,point.y,&pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00001443 if ((strlen(p) > 2) &&
1444 (LocaleCompare(p,"intensity") != 0) &&
1445 (LocaleCompare(p,"luminance") != 0) &&
1446 (LocaleCompare(p,"hue") != 0) &&
1447 (LocaleCompare(p,"saturation") != 0) &&
1448 (LocaleCompare(p,"lightness") != 0))
1449 {
1450 char
1451 name[MaxTextExtent];
1452
1453 (void) CopyMagickString(name,p,MaxTextExtent);
1454 for (q=name+(strlen(name)-1); q > name; q--)
1455 {
1456 if (*q == ')')
1457 break;
1458 if (*q == '.')
1459 {
1460 *q='\0';
1461 break;
1462 }
1463 }
1464 if ((strlen(name) > 2) &&
1465 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1466 {
cristy4c08aed2011-07-01 19:47:50 +00001467 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001468 *color;
1469
cristy4c08aed2011-07-01 19:47:50 +00001470 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1471 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001472 {
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),
cristy4c08aed2011-07-01 19:47:50 +00001480 ClonePixelInfo(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001481 p+=strlen(name);
1482 }
1483 }
1484 }
1485 (void) CopyMagickString(symbol,p,MaxTextExtent);
1486 StripString(symbol);
1487 if (*symbol == '\0')
1488 {
1489 switch (channel)
1490 {
cristy0568ffc2011-07-25 16:54:14 +00001491 case RedPixelChannel: return(QuantumScale*pixel.red);
1492 case GreenPixelChannel: return(QuantumScale*pixel.green);
1493 case BluePixelChannel: return(QuantumScale*pixel.blue);
1494 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001495 {
1496 if (image->colorspace != CMYKColorspace)
1497 {
1498 (void) ThrowMagickException(exception,GetMagickModule(),
1499 OptionError,"ColorSeparatedImageRequired","`%s'",
1500 image->filename);
1501 return(0.0);
1502 }
cristy4c08aed2011-07-01 19:47:50 +00001503 return(QuantumScale*pixel.black);
1504 }
cristy0568ffc2011-07-25 16:54:14 +00001505 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001506 {
1507 MagickRealType
1508 alpha;
1509
1510 if (pixel.matte == MagickFalse)
1511 return(1.0);
1512 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1513 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001514 }
cristyb3a73b52011-07-26 01:34:43 +00001515 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001516 {
cristy4c08aed2011-07-01 19:47:50 +00001517 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001518 }
cristy3ed852e2009-09-05 21:47:34 +00001519 default:
1520 break;
1521 }
1522 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1523 "UnableToParseExpression","`%s'",p);
1524 return(0.0);
1525 }
1526 switch (*symbol)
1527 {
1528 case 'A':
1529 case 'a':
1530 {
1531 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001532 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001533 break;
1534 }
1535 case 'B':
1536 case 'b':
1537 {
1538 if (LocaleCompare(symbol,"b") == 0)
1539 return(QuantumScale*pixel.blue);
1540 break;
1541 }
1542 case 'C':
1543 case 'c':
1544 {
1545 if (LocaleNCompare(symbol,"channel",7) == 0)
1546 {
1547 GeometryInfo
1548 channel_info;
1549
1550 MagickStatusType
1551 flags;
1552
1553 flags=ParseGeometry(symbol+7,&channel_info);
1554 if (image->colorspace == CMYKColorspace)
1555 switch (channel)
1556 {
cristy0568ffc2011-07-25 16:54:14 +00001557 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001558 {
1559 if ((flags & RhoValue) == 0)
1560 return(0.0);
1561 return(channel_info.rho);
1562 }
cristy0568ffc2011-07-25 16:54:14 +00001563 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001564 {
1565 if ((flags & SigmaValue) == 0)
1566 return(0.0);
1567 return(channel_info.sigma);
1568 }
cristy0568ffc2011-07-25 16:54:14 +00001569 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001570 {
1571 if ((flags & XiValue) == 0)
1572 return(0.0);
1573 return(channel_info.xi);
1574 }
cristy0568ffc2011-07-25 16:54:14 +00001575 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001576 {
1577 if ((flags & PsiValue) == 0)
1578 return(0.0);
1579 return(channel_info.psi);
1580 }
cristy0568ffc2011-07-25 16:54:14 +00001581 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001582 {
1583 if ((flags & ChiValue) == 0)
1584 return(0.0);
1585 return(channel_info.chi);
1586 }
1587 default:
1588 return(0.0);
1589 }
1590 switch (channel)
1591 {
cristy0568ffc2011-07-25 16:54:14 +00001592 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001593 {
1594 if ((flags & RhoValue) == 0)
1595 return(0.0);
1596 return(channel_info.rho);
1597 }
cristy0568ffc2011-07-25 16:54:14 +00001598 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001599 {
1600 if ((flags & SigmaValue) == 0)
1601 return(0.0);
1602 return(channel_info.sigma);
1603 }
cristy0568ffc2011-07-25 16:54:14 +00001604 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001605 {
1606 if ((flags & XiValue) == 0)
1607 return(0.0);
1608 return(channel_info.xi);
1609 }
cristy0568ffc2011-07-25 16:54:14 +00001610 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001611 {
1612 if ((flags & ChiValue) == 0)
1613 return(0.0);
1614 return(channel_info.chi);
1615 }
cristy0568ffc2011-07-25 16:54:14 +00001616 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001617 {
1618 if ((flags & PsiValue) == 0)
1619 return(0.0);
1620 return(channel_info.psi);
1621 }
cristy3ed852e2009-09-05 21:47:34 +00001622 default:
1623 return(0.0);
1624 }
1625 return(0.0);
1626 }
1627 if (LocaleCompare(symbol,"c") == 0)
1628 return(QuantumScale*pixel.red);
1629 break;
1630 }
1631 case 'D':
1632 case 'd':
1633 {
1634 if (LocaleNCompare(symbol,"depth",5) == 0)
1635 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1636 break;
1637 }
1638 case 'G':
1639 case 'g':
1640 {
1641 if (LocaleCompare(symbol,"g") == 0)
1642 return(QuantumScale*pixel.green);
1643 break;
1644 }
1645 case 'K':
1646 case 'k':
1647 {
1648 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1649 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1650 if (LocaleCompare(symbol,"k") == 0)
1651 {
1652 if (image->colorspace != CMYKColorspace)
1653 {
1654 (void) ThrowMagickException(exception,GetMagickModule(),
1655 OptionError,"ColorSeparatedImageRequired","`%s'",
1656 image->filename);
1657 return(0.0);
1658 }
cristy4c08aed2011-07-01 19:47:50 +00001659 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001660 }
1661 break;
1662 }
1663 case 'H':
1664 case 'h':
1665 {
1666 if (LocaleCompare(symbol,"h") == 0)
1667 return((MagickRealType) image->rows);
1668 if (LocaleCompare(symbol,"hue") == 0)
1669 {
1670 double
1671 hue,
1672 lightness,
1673 saturation;
1674
cristyce70c172010-01-07 17:15:30 +00001675 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1676 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001677 return(hue);
1678 }
1679 break;
1680 }
1681 case 'I':
1682 case 'i':
1683 {
1684 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1685 (LocaleCompare(symbol,"image.minima") == 0) ||
1686 (LocaleCompare(symbol,"image.maxima") == 0) ||
1687 (LocaleCompare(symbol,"image.mean") == 0) ||
1688 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1689 (LocaleCompare(symbol,"image.skewness") == 0) ||
1690 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1691 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1692 if (LocaleCompare(symbol,"image.resolution.x") == 0)
1693 return(image->x_resolution);
1694 if (LocaleCompare(symbol,"image.resolution.y") == 0)
1695 return(image->y_resolution);
1696 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001697 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001698 if (LocaleCompare(symbol,"i") == 0)
1699 return((MagickRealType) x);
1700 break;
1701 }
1702 case 'J':
1703 case 'j':
1704 {
1705 if (LocaleCompare(symbol,"j") == 0)
1706 return((MagickRealType) y);
1707 break;
1708 }
1709 case 'L':
1710 case 'l':
1711 {
1712 if (LocaleCompare(symbol,"lightness") == 0)
1713 {
1714 double
1715 hue,
1716 lightness,
1717 saturation;
1718
cristyce70c172010-01-07 17:15:30 +00001719 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1720 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001721 return(lightness);
1722 }
1723 if (LocaleCompare(symbol,"luminance") == 0)
1724 {
1725 double
1726 luminence;
1727
1728 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1729 return(QuantumScale*luminence);
1730 }
1731 break;
1732 }
1733 case 'M':
1734 case 'm':
1735 {
1736 if (LocaleNCompare(symbol,"maxima",6) == 0)
1737 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1738 if (LocaleNCompare(symbol,"mean",4) == 0)
1739 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1740 if (LocaleNCompare(symbol,"minima",6) == 0)
1741 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1742 if (LocaleCompare(symbol,"m") == 0)
1743 return(QuantumScale*pixel.blue);
1744 break;
1745 }
1746 case 'N':
1747 case 'n':
1748 {
1749 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001750 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001751 break;
1752 }
1753 case 'O':
1754 case 'o':
1755 {
1756 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001757 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001758 break;
1759 }
1760 case 'P':
1761 case 'p':
1762 {
1763 if (LocaleCompare(symbol,"page.height") == 0)
1764 return((MagickRealType) image->page.height);
1765 if (LocaleCompare(symbol,"page.width") == 0)
1766 return((MagickRealType) image->page.width);
1767 if (LocaleCompare(symbol,"page.x") == 0)
1768 return((MagickRealType) image->page.x);
1769 if (LocaleCompare(symbol,"page.y") == 0)
1770 return((MagickRealType) image->page.y);
1771 break;
1772 }
1773 case 'R':
1774 case 'r':
1775 {
1776 if (LocaleCompare(symbol,"resolution.x") == 0)
1777 return(image->x_resolution);
1778 if (LocaleCompare(symbol,"resolution.y") == 0)
1779 return(image->y_resolution);
1780 if (LocaleCompare(symbol,"r") == 0)
1781 return(QuantumScale*pixel.red);
1782 break;
1783 }
1784 case 'S':
1785 case 's':
1786 {
1787 if (LocaleCompare(symbol,"saturation") == 0)
1788 {
1789 double
1790 hue,
1791 lightness,
1792 saturation;
1793
cristyce70c172010-01-07 17:15:30 +00001794 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1795 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001796 return(saturation);
1797 }
1798 if (LocaleNCompare(symbol,"skewness",8) == 0)
1799 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1800 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1801 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1802 break;
1803 }
1804 case 'T':
1805 case 't':
1806 {
1807 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001808 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001809 break;
1810 }
1811 case 'W':
1812 case 'w':
1813 {
1814 if (LocaleCompare(symbol,"w") == 0)
1815 return((MagickRealType) image->columns);
1816 break;
1817 }
1818 case 'Y':
1819 case 'y':
1820 {
1821 if (LocaleCompare(symbol,"y") == 0)
1822 return(QuantumScale*pixel.green);
1823 break;
1824 }
1825 case 'Z':
1826 case 'z':
1827 {
1828 if (LocaleCompare(symbol,"z") == 0)
1829 {
1830 MagickRealType
1831 depth;
1832
cristyfefab1b2011-07-05 00:33:22 +00001833 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001834 return(depth);
1835 }
1836 break;
1837 }
1838 default:
1839 break;
1840 }
1841 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1842 if (value != (const char *) NULL)
cristyc1acd842011-05-19 23:05:47 +00001843 return((MagickRealType) InterpretLocaleValue(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001844 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1845 "UnableToParseExpression","`%s'",symbol);
1846 return(0.0);
1847}
1848
1849static const char *FxOperatorPrecedence(const char *expression,
1850 ExceptionInfo *exception)
1851{
1852 typedef enum
1853 {
1854 UndefinedPrecedence,
1855 NullPrecedence,
1856 BitwiseComplementPrecedence,
1857 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001858 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001859 MultiplyPrecedence,
1860 AdditionPrecedence,
1861 ShiftPrecedence,
1862 RelationalPrecedence,
1863 EquivalencyPrecedence,
1864 BitwiseAndPrecedence,
1865 BitwiseOrPrecedence,
1866 LogicalAndPrecedence,
1867 LogicalOrPrecedence,
1868 TernaryPrecedence,
1869 AssignmentPrecedence,
1870 CommaPrecedence,
1871 SeparatorPrecedence
1872 } FxPrecedence;
1873
1874 FxPrecedence
1875 precedence,
1876 target;
1877
1878 register const char
1879 *subexpression;
1880
1881 register int
1882 c;
1883
cristybb503372010-05-27 20:51:26 +00001884 size_t
cristy3ed852e2009-09-05 21:47:34 +00001885 level;
1886
1887 c=0;
1888 level=0;
1889 subexpression=(const char *) NULL;
1890 target=NullPrecedence;
1891 while (*expression != '\0')
1892 {
1893 precedence=UndefinedPrecedence;
1894 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1895 {
1896 expression++;
1897 continue;
1898 }
cristy488fa882010-03-01 22:34:24 +00001899 switch (*expression)
1900 {
1901 case 'A':
1902 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001903 {
cristyb33454f2011-08-03 02:10:45 +00001904#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001905 if (LocaleNCompare(expression,"acosh",5) == 0)
1906 {
1907 expression+=5;
1908 break;
1909 }
cristyb33454f2011-08-03 02:10:45 +00001910#endif
1911#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001912 if (LocaleNCompare(expression,"asinh",5) == 0)
1913 {
1914 expression+=5;
1915 break;
1916 }
cristyb33454f2011-08-03 02:10:45 +00001917#endif
1918#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001919 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001920 {
1921 expression+=5;
1922 break;
1923 }
cristyb33454f2011-08-03 02:10:45 +00001924#endif
cristy488fa882010-03-01 22:34:24 +00001925 break;
cristy3ed852e2009-09-05 21:47:34 +00001926 }
cristy488fa882010-03-01 22:34:24 +00001927 case 'J':
1928 case 'j':
1929 {
1930 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1931 (LocaleNCompare(expression,"j1",2) == 0))
1932 {
1933 expression+=2;
1934 break;
1935 }
1936 break;
1937 }
cristy2def9322010-06-18 23:59:37 +00001938 case '#':
1939 {
1940 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1941 expression++;
1942 break;
1943 }
cristy488fa882010-03-01 22:34:24 +00001944 default:
1945 break;
1946 }
cristy3ed852e2009-09-05 21:47:34 +00001947 if ((c == (int) '{') || (c == (int) '['))
1948 level++;
1949 else
1950 if ((c == (int) '}') || (c == (int) ']'))
1951 level--;
1952 if (level == 0)
1953 switch ((unsigned char) *expression)
1954 {
1955 case '~':
1956 case '!':
1957 {
1958 precedence=BitwiseComplementPrecedence;
1959 break;
1960 }
1961 case '^':
cristy6621e252010-08-13 00:42:57 +00001962 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001963 {
1964 precedence=ExponentPrecedence;
1965 break;
1966 }
1967 default:
1968 {
1969 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1970 (strchr(")",c) != (char *) NULL))) &&
1971 (((islower((int) ((char) *expression)) != 0) ||
1972 (strchr("(",(int) *expression) != (char *) NULL)) ||
1973 ((isdigit((int) ((char) c)) == 0) &&
1974 (isdigit((int) ((char) *expression)) != 0))) &&
1975 (strchr("xy",(int) *expression) == (char *) NULL))
1976 precedence=MultiplyPrecedence;
1977 break;
1978 }
1979 case '*':
1980 case '/':
1981 case '%':
1982 {
1983 precedence=MultiplyPrecedence;
1984 break;
1985 }
1986 case '+':
1987 case '-':
1988 {
1989 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1990 (isalpha(c) != 0))
1991 precedence=AdditionPrecedence;
1992 break;
1993 }
1994 case LeftShiftOperator:
1995 case RightShiftOperator:
1996 {
1997 precedence=ShiftPrecedence;
1998 break;
1999 }
2000 case '<':
2001 case LessThanEqualOperator:
2002 case GreaterThanEqualOperator:
2003 case '>':
2004 {
2005 precedence=RelationalPrecedence;
2006 break;
2007 }
2008 case EqualOperator:
2009 case NotEqualOperator:
2010 {
2011 precedence=EquivalencyPrecedence;
2012 break;
2013 }
2014 case '&':
2015 {
2016 precedence=BitwiseAndPrecedence;
2017 break;
2018 }
2019 case '|':
2020 {
2021 precedence=BitwiseOrPrecedence;
2022 break;
2023 }
2024 case LogicalAndOperator:
2025 {
2026 precedence=LogicalAndPrecedence;
2027 break;
2028 }
2029 case LogicalOrOperator:
2030 {
2031 precedence=LogicalOrPrecedence;
2032 break;
2033 }
cristy116af162010-08-13 01:25:47 +00002034 case ExponentialNotation:
2035 {
2036 precedence=ExponentialNotationPrecedence;
2037 break;
2038 }
cristy3ed852e2009-09-05 21:47:34 +00002039 case ':':
2040 case '?':
2041 {
2042 precedence=TernaryPrecedence;
2043 break;
2044 }
2045 case '=':
2046 {
2047 precedence=AssignmentPrecedence;
2048 break;
2049 }
2050 case ',':
2051 {
2052 precedence=CommaPrecedence;
2053 break;
2054 }
2055 case ';':
2056 {
2057 precedence=SeparatorPrecedence;
2058 break;
2059 }
2060 }
2061 if ((precedence == BitwiseComplementPrecedence) ||
2062 (precedence == TernaryPrecedence) ||
2063 (precedence == AssignmentPrecedence))
2064 {
2065 if (precedence > target)
2066 {
2067 /*
2068 Right-to-left associativity.
2069 */
2070 target=precedence;
2071 subexpression=expression;
2072 }
2073 }
2074 else
2075 if (precedence >= target)
2076 {
2077 /*
2078 Left-to-right associativity.
2079 */
2080 target=precedence;
2081 subexpression=expression;
2082 }
2083 if (strchr("(",(int) *expression) != (char *) NULL)
2084 expression=FxSubexpression(expression,exception);
2085 c=(int) (*expression++);
2086 }
2087 return(subexpression);
2088}
2089
2090static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002091 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002092 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002093{
2094 char
2095 *q,
2096 subexpression[MaxTextExtent];
2097
2098 MagickRealType
2099 alpha,
2100 gamma;
2101
2102 register const char
2103 *p;
2104
2105 *beta=0.0;
2106 if (exception->severity != UndefinedException)
2107 return(0.0);
2108 while (isspace((int) *expression) != 0)
2109 expression++;
2110 if (*expression == '\0')
2111 {
2112 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2113 "MissingExpression","`%s'",expression);
2114 return(0.0);
2115 }
cristy66322f02010-05-17 11:40:48 +00002116 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002117 p=FxOperatorPrecedence(expression,exception);
2118 if (p != (const char *) NULL)
2119 {
2120 (void) CopyMagickString(subexpression,expression,(size_t)
2121 (p-expression+1));
2122 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2123 exception);
2124 switch ((unsigned char) *p)
2125 {
2126 case '~':
2127 {
2128 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002129 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002130 return(*beta);
2131 }
2132 case '!':
2133 {
2134 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2135 return(*beta == 0.0 ? 1.0 : 0.0);
2136 }
2137 case '^':
2138 {
2139 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2140 channel,x,y,++p,beta,exception));
2141 return(*beta);
2142 }
2143 case '*':
cristy116af162010-08-13 01:25:47 +00002144 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002145 {
2146 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2147 return(alpha*(*beta));
2148 }
2149 case '/':
2150 {
2151 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2152 if (*beta == 0.0)
2153 {
2154 if (exception->severity == UndefinedException)
2155 (void) ThrowMagickException(exception,GetMagickModule(),
2156 OptionError,"DivideByZero","`%s'",expression);
2157 return(0.0);
2158 }
2159 return(alpha/(*beta));
2160 }
2161 case '%':
2162 {
2163 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2164 *beta=fabs(floor(((double) *beta)+0.5));
2165 if (*beta == 0.0)
2166 {
2167 (void) ThrowMagickException(exception,GetMagickModule(),
2168 OptionError,"DivideByZero","`%s'",expression);
2169 return(0.0);
2170 }
2171 return(fmod((double) alpha,(double) *beta));
2172 }
2173 case '+':
2174 {
2175 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2176 return(alpha+(*beta));
2177 }
2178 case '-':
2179 {
2180 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2181 return(alpha-(*beta));
2182 }
2183 case LeftShiftOperator:
2184 {
2185 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002186 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002187 (gamma+0.5));
2188 return(*beta);
2189 }
2190 case RightShiftOperator:
2191 {
2192 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002193 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002194 (gamma+0.5));
2195 return(*beta);
2196 }
2197 case '<':
2198 {
2199 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2200 return(alpha < *beta ? 1.0 : 0.0);
2201 }
2202 case LessThanEqualOperator:
2203 {
2204 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2205 return(alpha <= *beta ? 1.0 : 0.0);
2206 }
2207 case '>':
2208 {
2209 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2210 return(alpha > *beta ? 1.0 : 0.0);
2211 }
2212 case GreaterThanEqualOperator:
2213 {
2214 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2215 return(alpha >= *beta ? 1.0 : 0.0);
2216 }
2217 case EqualOperator:
2218 {
2219 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2220 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2221 }
2222 case NotEqualOperator:
2223 {
2224 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2225 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2226 }
2227 case '&':
2228 {
2229 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002230 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002231 (gamma+0.5));
2232 return(*beta);
2233 }
2234 case '|':
2235 {
2236 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002237 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002238 (gamma+0.5));
2239 return(*beta);
2240 }
2241 case LogicalAndOperator:
2242 {
2243 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2244 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2245 return(*beta);
2246 }
2247 case LogicalOrOperator:
2248 {
2249 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2250 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2251 return(*beta);
2252 }
2253 case '?':
2254 {
2255 MagickRealType
2256 gamma;
2257
2258 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2259 q=subexpression;
2260 p=StringToken(":",&q);
2261 if (q == (char *) NULL)
2262 {
2263 (void) ThrowMagickException(exception,GetMagickModule(),
2264 OptionError,"UnableToParseExpression","`%s'",subexpression);
2265 return(0.0);
2266 }
2267 if (fabs((double) alpha) > MagickEpsilon)
2268 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2269 else
2270 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2271 return(gamma);
2272 }
2273 case '=':
2274 {
2275 char
2276 numeric[MaxTextExtent];
2277
2278 q=subexpression;
2279 while (isalpha((int) ((unsigned char) *q)) != 0)
2280 q++;
2281 if (*q != '\0')
2282 {
2283 (void) ThrowMagickException(exception,GetMagickModule(),
2284 OptionError,"UnableToParseExpression","`%s'",subexpression);
2285 return(0.0);
2286 }
2287 ClearMagickException(exception);
2288 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002289 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002290 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002291 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2292 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2293 subexpression),ConstantString(numeric));
2294 return(*beta);
2295 }
2296 case ',':
2297 {
2298 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2299 return(alpha);
2300 }
2301 case ';':
2302 {
2303 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2304 return(*beta);
2305 }
2306 default:
2307 {
2308 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2309 exception);
2310 return(gamma);
2311 }
2312 }
2313 }
2314 if (strchr("(",(int) *expression) != (char *) NULL)
2315 {
2316 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2317 subexpression[strlen(subexpression)-1]='\0';
2318 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2319 exception);
2320 return(gamma);
2321 }
cristy8b8a3ae2010-10-23 18:49:46 +00002322 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002323 {
2324 case '+':
2325 {
2326 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2327 exception);
2328 return(1.0*gamma);
2329 }
2330 case '-':
2331 {
2332 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2333 exception);
2334 return(-1.0*gamma);
2335 }
2336 case '~':
2337 {
2338 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2339 exception);
cristybb503372010-05-27 20:51:26 +00002340 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002341 }
2342 case 'A':
2343 case 'a':
2344 {
2345 if (LocaleNCompare(expression,"abs",3) == 0)
2346 {
2347 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2348 exception);
2349 return((MagickRealType) fabs((double) alpha));
2350 }
cristyb33454f2011-08-03 02:10:45 +00002351#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002352 if (LocaleNCompare(expression,"acosh",5) == 0)
2353 {
2354 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2355 exception);
2356 return((MagickRealType) acosh((double) alpha));
2357 }
cristyb33454f2011-08-03 02:10:45 +00002358#endif
cristy3ed852e2009-09-05 21:47:34 +00002359 if (LocaleNCompare(expression,"acos",4) == 0)
2360 {
2361 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2362 exception);
2363 return((MagickRealType) acos((double) alpha));
2364 }
cristy43c22f42010-03-30 12:34:07 +00002365#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002366 if (LocaleNCompare(expression,"airy",4) == 0)
2367 {
2368 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2369 exception);
2370 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002371 return(1.0);
2372 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002373 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002374 }
cristy43c22f42010-03-30 12:34:07 +00002375#endif
cristyb33454f2011-08-03 02:10:45 +00002376#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002377 if (LocaleNCompare(expression,"asinh",5) == 0)
2378 {
2379 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2380 exception);
2381 return((MagickRealType) asinh((double) alpha));
2382 }
cristyb33454f2011-08-03 02:10:45 +00002383#endif
cristy3ed852e2009-09-05 21:47:34 +00002384 if (LocaleNCompare(expression,"asin",4) == 0)
2385 {
2386 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2387 exception);
2388 return((MagickRealType) asin((double) alpha));
2389 }
2390 if (LocaleNCompare(expression,"alt",3) == 0)
2391 {
2392 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2393 exception);
cristybb503372010-05-27 20:51:26 +00002394 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002395 }
2396 if (LocaleNCompare(expression,"atan2",5) == 0)
2397 {
2398 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2399 exception);
2400 return((MagickRealType) atan2((double) alpha,(double) *beta));
2401 }
cristyb33454f2011-08-03 02:10:45 +00002402#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002403 if (LocaleNCompare(expression,"atanh",5) == 0)
2404 {
2405 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2406 exception);
2407 return((MagickRealType) atanh((double) alpha));
2408 }
cristyb33454f2011-08-03 02:10:45 +00002409#endif
cristy3ed852e2009-09-05 21:47:34 +00002410 if (LocaleNCompare(expression,"atan",4) == 0)
2411 {
2412 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2413 exception);
2414 return((MagickRealType) atan((double) alpha));
2415 }
2416 if (LocaleCompare(expression,"a") == 0)
2417 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2418 break;
2419 }
2420 case 'B':
2421 case 'b':
2422 {
2423 if (LocaleCompare(expression,"b") == 0)
2424 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2425 break;
2426 }
2427 case 'C':
2428 case 'c':
2429 {
2430 if (LocaleNCompare(expression,"ceil",4) == 0)
2431 {
2432 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2433 exception);
2434 return((MagickRealType) ceil((double) alpha));
2435 }
2436 if (LocaleNCompare(expression,"cosh",4) == 0)
2437 {
2438 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2439 exception);
2440 return((MagickRealType) cosh((double) alpha));
2441 }
2442 if (LocaleNCompare(expression,"cos",3) == 0)
2443 {
2444 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2445 exception);
2446 return((MagickRealType) cos((double) alpha));
2447 }
2448 if (LocaleCompare(expression,"c") == 0)
2449 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2450 break;
2451 }
2452 case 'D':
2453 case 'd':
2454 {
2455 if (LocaleNCompare(expression,"debug",5) == 0)
2456 {
2457 const char
2458 *type;
2459
2460 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2461 exception);
2462 if (fx_info->images->colorspace == CMYKColorspace)
2463 switch (channel)
2464 {
cristy0568ffc2011-07-25 16:54:14 +00002465 case CyanPixelChannel: type="cyan"; break;
2466 case MagentaPixelChannel: type="magenta"; break;
2467 case YellowPixelChannel: type="yellow"; break;
2468 case AlphaPixelChannel: type="opacity"; break;
2469 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002470 default: type="unknown"; break;
2471 }
2472 else
2473 switch (channel)
2474 {
cristy0568ffc2011-07-25 16:54:14 +00002475 case RedPixelChannel: type="red"; break;
2476 case GreenPixelChannel: type="green"; break;
2477 case BluePixelChannel: type="blue"; break;
2478 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002479 default: type="unknown"; break;
2480 }
2481 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2482 if (strlen(subexpression) > 1)
2483 subexpression[strlen(subexpression)-1]='\0';
2484 if (fx_info->file != (FILE *) NULL)
cristy1e604812011-05-19 18:07:50 +00002485 (void) FormatLocaleFile(fx_info->file,
2486 "%s[%.20g,%.20g].%s: %s=%.*g\n",fx_info->images->filename,
2487 (double) x,(double) y,type,subexpression,GetMagickPrecision(),
2488 (double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002489 return(0.0);
2490 }
2491 break;
2492 }
2493 case 'E':
2494 case 'e':
2495 {
2496 if (LocaleCompare(expression,"epsilon") == 0)
2497 return((MagickRealType) MagickEpsilon);
2498 if (LocaleNCompare(expression,"exp",3) == 0)
2499 {
2500 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2501 exception);
2502 return((MagickRealType) exp((double) alpha));
2503 }
2504 if (LocaleCompare(expression,"e") == 0)
2505 return((MagickRealType) 2.7182818284590452354);
2506 break;
2507 }
2508 case 'F':
2509 case 'f':
2510 {
2511 if (LocaleNCompare(expression,"floor",5) == 0)
2512 {
2513 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2514 exception);
2515 return((MagickRealType) floor((double) alpha));
2516 }
2517 break;
2518 }
2519 case 'G':
2520 case 'g':
2521 {
2522 if (LocaleCompare(expression,"g") == 0)
2523 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2524 break;
2525 }
2526 case 'H':
2527 case 'h':
2528 {
2529 if (LocaleCompare(expression,"h") == 0)
2530 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2531 if (LocaleCompare(expression,"hue") == 0)
2532 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2533 if (LocaleNCompare(expression,"hypot",5) == 0)
2534 {
2535 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2536 exception);
2537 return((MagickRealType) hypot((double) alpha,(double) *beta));
2538 }
2539 break;
2540 }
2541 case 'K':
2542 case 'k':
2543 {
2544 if (LocaleCompare(expression,"k") == 0)
2545 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2546 break;
2547 }
2548 case 'I':
2549 case 'i':
2550 {
2551 if (LocaleCompare(expression,"intensity") == 0)
2552 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2553 if (LocaleNCompare(expression,"int",3) == 0)
2554 {
2555 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2556 exception);
cristy16788e42010-08-13 13:44:26 +00002557 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002558 }
2559 if (LocaleCompare(expression,"i") == 0)
2560 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2561 break;
2562 }
2563 case 'J':
2564 case 'j':
2565 {
2566 if (LocaleCompare(expression,"j") == 0)
2567 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002568#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002569 if (LocaleNCompare(expression,"j0",2) == 0)
2570 {
2571 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2572 exception);
2573 return((MagickRealType) j0((double) alpha));
2574 }
cristy161b9262010-03-20 19:34:32 +00002575#endif
2576#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002577 if (LocaleNCompare(expression,"j1",2) == 0)
2578 {
2579 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2580 exception);
2581 return((MagickRealType) j1((double) alpha));
2582 }
cristy161b9262010-03-20 19:34:32 +00002583#endif
cristyaa018fa2010-04-08 23:03:54 +00002584#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002585 if (LocaleNCompare(expression,"jinc",4) == 0)
2586 {
2587 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2588 exception);
cristy0946a822010-03-12 17:14:58 +00002589 if (alpha == 0.0)
2590 return(1.0);
cristy69928f92010-03-12 13:27:09 +00002591 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/
cristyfce2f7b2010-03-12 00:29:49 +00002592 (MagickPI*alpha));
2593 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002594 }
cristyaa018fa2010-04-08 23:03:54 +00002595#endif
cristy3ed852e2009-09-05 21:47:34 +00002596 break;
2597 }
2598 case 'L':
2599 case 'l':
2600 {
2601 if (LocaleNCompare(expression,"ln",2) == 0)
2602 {
2603 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2604 exception);
2605 return((MagickRealType) log((double) alpha));
2606 }
cristyc8ed5322010-08-31 12:07:59 +00002607 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002608 {
cristyc8ed5322010-08-31 12:07:59 +00002609 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002610 exception);
2611 return((MagickRealType) log10((double) alpha))/log10(2.0);
2612 }
2613 if (LocaleNCompare(expression,"log",3) == 0)
2614 {
2615 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2616 exception);
2617 return((MagickRealType) log10((double) alpha));
2618 }
2619 if (LocaleCompare(expression,"lightness") == 0)
2620 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2621 break;
2622 }
2623 case 'M':
2624 case 'm':
2625 {
2626 if (LocaleCompare(expression,"MaxRGB") == 0)
2627 return((MagickRealType) QuantumRange);
2628 if (LocaleNCompare(expression,"maxima",6) == 0)
2629 break;
2630 if (LocaleNCompare(expression,"max",3) == 0)
2631 return(FxMax(fx_info,channel,x,y,expression+3,exception));
2632 if (LocaleNCompare(expression,"minima",6) == 0)
2633 break;
2634 if (LocaleNCompare(expression,"min",3) == 0)
2635 return(FxMin(fx_info,channel,x,y,expression+3,exception));
2636 if (LocaleNCompare(expression,"mod",3) == 0)
2637 {
2638 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2639 exception);
2640 return((MagickRealType) fmod((double) alpha,(double) *beta));
2641 }
2642 if (LocaleCompare(expression,"m") == 0)
2643 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2644 break;
2645 }
2646 case 'N':
2647 case 'n':
2648 {
2649 if (LocaleCompare(expression,"n") == 0)
2650 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2651 break;
2652 }
2653 case 'O':
2654 case 'o':
2655 {
2656 if (LocaleCompare(expression,"Opaque") == 0)
2657 return(1.0);
2658 if (LocaleCompare(expression,"o") == 0)
2659 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2660 break;
2661 }
2662 case 'P':
2663 case 'p':
2664 {
2665 if (LocaleCompare(expression,"pi") == 0)
2666 return((MagickRealType) MagickPI);
2667 if (LocaleNCompare(expression,"pow",3) == 0)
2668 {
2669 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2670 exception);
2671 return((MagickRealType) pow((double) alpha,(double) *beta));
2672 }
2673 if (LocaleCompare(expression,"p") == 0)
2674 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2675 break;
2676 }
2677 case 'Q':
2678 case 'q':
2679 {
2680 if (LocaleCompare(expression,"QuantumRange") == 0)
2681 return((MagickRealType) QuantumRange);
2682 if (LocaleCompare(expression,"QuantumScale") == 0)
2683 return((MagickRealType) QuantumScale);
2684 break;
2685 }
2686 case 'R':
2687 case 'r':
2688 {
2689 if (LocaleNCompare(expression,"rand",4) == 0)
2690 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2691 if (LocaleNCompare(expression,"round",5) == 0)
2692 {
2693 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2694 exception);
cristy16788e42010-08-13 13:44:26 +00002695 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002696 }
2697 if (LocaleCompare(expression,"r") == 0)
2698 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2699 break;
2700 }
2701 case 'S':
2702 case 's':
2703 {
2704 if (LocaleCompare(expression,"saturation") == 0)
2705 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2706 if (LocaleNCompare(expression,"sign",4) == 0)
2707 {
2708 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2709 exception);
2710 return(alpha < 0.0 ? -1.0 : 1.0);
2711 }
cristya6a09e72010-03-02 14:51:02 +00002712 if (LocaleNCompare(expression,"sinc",4) == 0)
2713 {
2714 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2715 exception);
2716 if (alpha == 0)
2717 return(1.0);
2718 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2719 (MagickPI*alpha));
2720 return(gamma);
2721 }
cristy3ed852e2009-09-05 21:47:34 +00002722 if (LocaleNCompare(expression,"sinh",4) == 0)
2723 {
2724 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2725 exception);
2726 return((MagickRealType) sinh((double) alpha));
2727 }
2728 if (LocaleNCompare(expression,"sin",3) == 0)
2729 {
2730 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2731 exception);
2732 return((MagickRealType) sin((double) alpha));
2733 }
2734 if (LocaleNCompare(expression,"sqrt",4) == 0)
2735 {
2736 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2737 exception);
2738 return((MagickRealType) sqrt((double) alpha));
2739 }
2740 if (LocaleCompare(expression,"s") == 0)
2741 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2742 break;
2743 }
2744 case 'T':
2745 case 't':
2746 {
2747 if (LocaleNCompare(expression,"tanh",4) == 0)
2748 {
2749 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2750 exception);
2751 return((MagickRealType) tanh((double) alpha));
2752 }
2753 if (LocaleNCompare(expression,"tan",3) == 0)
2754 {
2755 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2756 exception);
2757 return((MagickRealType) tan((double) alpha));
2758 }
2759 if (LocaleCompare(expression,"Transparent") == 0)
2760 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002761 if (LocaleNCompare(expression,"trunc",5) == 0)
2762 {
2763 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2764 exception);
2765 if (alpha >= 0.0)
2766 return((MagickRealType) floor((double) alpha));
2767 return((MagickRealType) ceil((double) alpha));
2768 }
cristy3ed852e2009-09-05 21:47:34 +00002769 if (LocaleCompare(expression,"t") == 0)
2770 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2771 break;
2772 }
2773 case 'U':
2774 case 'u':
2775 {
2776 if (LocaleCompare(expression,"u") == 0)
2777 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2778 break;
2779 }
2780 case 'V':
2781 case 'v':
2782 {
2783 if (LocaleCompare(expression,"v") == 0)
2784 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2785 break;
2786 }
2787 case 'W':
2788 case 'w':
2789 {
2790 if (LocaleCompare(expression,"w") == 0)
2791 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2792 break;
2793 }
2794 case 'Y':
2795 case 'y':
2796 {
2797 if (LocaleCompare(expression,"y") == 0)
2798 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2799 break;
2800 }
2801 case 'Z':
2802 case 'z':
2803 {
2804 if (LocaleCompare(expression,"z") == 0)
2805 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2806 break;
2807 }
2808 default:
2809 break;
2810 }
2811 q=(char *) expression;
cristyc1acd842011-05-19 23:05:47 +00002812 alpha=InterpretLocaleValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002813 if (q == expression)
2814 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2815 return(alpha);
2816}
2817
2818MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2819 MagickRealType *alpha,ExceptionInfo *exception)
2820{
2821 MagickBooleanType
2822 status;
2823
cristy30539862010-11-10 14:14:14 +00002824 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002825 return(status);
2826}
2827
2828MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2829 MagickRealType *alpha,ExceptionInfo *exception)
2830{
2831 FILE
2832 *file;
2833
2834 MagickBooleanType
2835 status;
2836
2837 file=fx_info->file;
2838 fx_info->file=(FILE *) NULL;
2839 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2840 fx_info->file=file;
2841 return(status);
2842}
2843
2844MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002845 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002846 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002847{
2848 MagickRealType
2849 beta;
2850
2851 beta=0.0;
2852 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2853 exception);
2854 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2855}
2856
2857/*
2858%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2859% %
2860% %
2861% %
2862% F x I m a g e %
2863% %
2864% %
2865% %
2866%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2867%
2868% FxImage() applies a mathematical expression to the specified image.
2869%
2870% The format of the FxImage method is:
2871%
2872% Image *FxImage(const Image *image,const char *expression,
2873% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002874%
2875% A description of each parameter follows:
2876%
2877% o image: the image.
2878%
cristy3ed852e2009-09-05 21:47:34 +00002879% o expression: A mathematical expression.
2880%
2881% o exception: return any errors or warnings in this structure.
2882%
2883*/
2884
2885static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2886{
cristybb503372010-05-27 20:51:26 +00002887 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002888 i;
2889
2890 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002891 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002892 if (fx_info[i] != (FxInfo *) NULL)
2893 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002894 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002895 return(fx_info);
2896}
2897
2898static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2899 ExceptionInfo *exception)
2900{
2901 char
2902 *fx_expression;
2903
2904 FxInfo
2905 **fx_info;
2906
2907 MagickRealType
2908 alpha;
2909
cristybb503372010-05-27 20:51:26 +00002910 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002911 i;
2912
cristybb503372010-05-27 20:51:26 +00002913 size_t
cristy3ed852e2009-09-05 21:47:34 +00002914 number_threads;
2915
2916 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002917 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002918 if (fx_info == (FxInfo **) NULL)
2919 return((FxInfo **) NULL);
2920 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2921 if (*expression != '@')
2922 fx_expression=ConstantString(expression);
2923 else
2924 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00002925 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002926 {
2927 fx_info[i]=AcquireFxInfo(image,fx_expression);
2928 if (fx_info[i] == (FxInfo *) NULL)
2929 return(DestroyFxThreadSet(fx_info));
2930 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2931 }
2932 fx_expression=DestroyString(fx_expression);
2933 return(fx_info);
2934}
2935
2936MagickExport Image *FxImage(const Image *image,const char *expression,
2937 ExceptionInfo *exception)
2938{
cristy3ed852e2009-09-05 21:47:34 +00002939#define FxImageTag "Fx/Image"
2940
cristyfa112112010-01-04 17:48:07 +00002941 CacheView
cristy79cedc72011-07-25 00:41:15 +00002942 *fx_view,
2943 *image_view;
cristyfa112112010-01-04 17:48:07 +00002944
cristy3ed852e2009-09-05 21:47:34 +00002945 FxInfo
cristyfa112112010-01-04 17:48:07 +00002946 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00002947
2948 Image
2949 *fx_image;
2950
cristy3ed852e2009-09-05 21:47:34 +00002951 MagickBooleanType
2952 status;
2953
cristybb503372010-05-27 20:51:26 +00002954 MagickOffsetType
2955 progress;
2956
cristy3ed852e2009-09-05 21:47:34 +00002957 MagickRealType
2958 alpha;
2959
cristybb503372010-05-27 20:51:26 +00002960 ssize_t
2961 y;
2962
cristy3ed852e2009-09-05 21:47:34 +00002963 assert(image != (Image *) NULL);
2964 assert(image->signature == MagickSignature);
2965 if (image->debug != MagickFalse)
2966 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00002967 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00002968 if (fx_image == (Image *) NULL)
2969 return((Image *) NULL);
2970 if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
2971 {
2972 InheritException(exception,&fx_image->exception);
2973 fx_image=DestroyImage(fx_image);
2974 return((Image *) NULL);
2975 }
2976 fx_info=AcquireFxThreadSet(image,expression,exception);
2977 if (fx_info == (FxInfo **) NULL)
2978 {
2979 fx_image=DestroyImage(fx_image);
2980 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2981 }
2982 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
2983 if (status == MagickFalse)
2984 {
2985 fx_image=DestroyImage(fx_image);
2986 fx_info=DestroyFxThreadSet(fx_info);
2987 return((Image *) NULL);
2988 }
2989 /*
2990 Fx image.
2991 */
2992 status=MagickTrue;
2993 progress=0;
cristy79cedc72011-07-25 00:41:15 +00002994 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00002995 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00002996#if defined(MAGICKCORE_OPENMP_SUPPORT)
2997 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002998#endif
cristybb503372010-05-27 20:51:26 +00002999 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003000 {
cristy5c9e6f22010-09-17 17:31:01 +00003001 const int
3002 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003003
cristy79cedc72011-07-25 00:41:15 +00003004 register const Quantum
3005 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003006
cristy4c08aed2011-07-01 19:47:50 +00003007 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003008 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003009
cristy79cedc72011-07-25 00:41:15 +00003010 register ssize_t
3011 x;
3012
cristy3ed852e2009-09-05 21:47:34 +00003013 if (status == MagickFalse)
3014 continue;
cristy79cedc72011-07-25 00:41:15 +00003015 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00003016 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003017 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003018 {
3019 status=MagickFalse;
3020 continue;
3021 }
cristybb503372010-05-27 20:51:26 +00003022 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003023 {
cristy79cedc72011-07-25 00:41:15 +00003024 register ssize_t
3025 i;
3026
3027 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3028 {
3029 MagickRealType
3030 alpha;
3031
3032 PixelChannel
3033 channel;
3034
3035 PixelTrait
3036 fx_traits,
3037 traits;
3038
3039 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3040 if (traits == UndefinedPixelTrait)
3041 continue;
3042 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3043 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
3044 if (fx_traits == UndefinedPixelTrait)
3045 continue;
3046 if ((fx_traits & CopyPixelTrait) != 0)
3047 {
3048 q[channel]=p[i];
3049 continue;
3050 }
3051 alpha=0.0;
cristy0568ffc2011-07-25 16:54:14 +00003052 (void) FxEvaluateChannelExpression(fx_info[id],(PixelChannel) i,x,y,
3053 &alpha,exception);
cristyb3a73b52011-07-26 01:34:43 +00003054 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003055 }
3056 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003057 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003058 }
3059 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3060 status=MagickFalse;
3061 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3062 {
3063 MagickBooleanType
3064 proceed;
3065
cristyb5d5f722009-11-04 03:03:49 +00003066#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy490408a2011-07-07 14:42:05 +00003067 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003068#endif
3069 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3070 if (proceed == MagickFalse)
3071 status=MagickFalse;
3072 }
3073 }
cristy3ed852e2009-09-05 21:47:34 +00003074 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003075 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003076 fx_info=DestroyFxThreadSet(fx_info);
3077 if (status == MagickFalse)
3078 fx_image=DestroyImage(fx_image);
3079 return(fx_image);
3080}
3081
3082/*
3083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3084% %
3085% %
3086% %
3087% I m p l o d e I m a g e %
3088% %
3089% %
3090% %
3091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3092%
3093% ImplodeImage() creates a new image that is a copy of an existing
3094% one with the image pixels "implode" by the specified percentage. It
3095% allocates the memory necessary for the new Image structure and returns a
3096% pointer to the new image.
3097%
3098% The format of the ImplodeImage method is:
3099%
3100% Image *ImplodeImage(const Image *image,const double amount,
3101% ExceptionInfo *exception)
3102%
3103% A description of each parameter follows:
3104%
3105% o implode_image: Method ImplodeImage returns a pointer to the image
3106% after it is implode. A null image is returned if there is a memory
3107% shortage.
3108%
3109% o image: the image.
3110%
3111% o amount: Define the extent of the implosion.
3112%
3113% o exception: return any errors or warnings in this structure.
3114%
3115*/
3116MagickExport Image *ImplodeImage(const Image *image,const double amount,
3117 ExceptionInfo *exception)
3118{
3119#define ImplodeImageTag "Implode/Image"
3120
cristyfa112112010-01-04 17:48:07 +00003121 CacheView
3122 *image_view,
3123 *implode_view;
3124
cristy3ed852e2009-09-05 21:47:34 +00003125 Image
3126 *implode_image;
3127
cristy3ed852e2009-09-05 21:47:34 +00003128 MagickBooleanType
3129 status;
3130
cristybb503372010-05-27 20:51:26 +00003131 MagickOffsetType
3132 progress;
3133
cristy4c08aed2011-07-01 19:47:50 +00003134 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003135 zero;
3136
3137 MagickRealType
3138 radius;
3139
3140 PointInfo
3141 center,
3142 scale;
3143
cristybb503372010-05-27 20:51:26 +00003144 ssize_t
3145 y;
3146
cristy3ed852e2009-09-05 21:47:34 +00003147 /*
3148 Initialize implode image attributes.
3149 */
3150 assert(image != (Image *) NULL);
3151 assert(image->signature == MagickSignature);
3152 if (image->debug != MagickFalse)
3153 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3154 assert(exception != (ExceptionInfo *) NULL);
3155 assert(exception->signature == MagickSignature);
3156 implode_image=CloneImage(image,0,0,MagickTrue,exception);
3157 if (implode_image == (Image *) NULL)
3158 return((Image *) NULL);
3159 if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
3160 {
3161 InheritException(exception,&implode_image->exception);
3162 implode_image=DestroyImage(implode_image);
3163 return((Image *) NULL);
3164 }
cristy4c08aed2011-07-01 19:47:50 +00003165 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003166 implode_image->matte=MagickTrue;
3167 /*
3168 Compute scaling factor.
3169 */
3170 scale.x=1.0;
3171 scale.y=1.0;
3172 center.x=0.5*image->columns;
3173 center.y=0.5*image->rows;
3174 radius=center.x;
3175 if (image->columns > image->rows)
3176 scale.y=(double) image->columns/(double) image->rows;
3177 else
3178 if (image->columns < image->rows)
3179 {
3180 scale.x=(double) image->rows/(double) image->columns;
3181 radius=center.y;
3182 }
3183 /*
3184 Implode image.
3185 */
3186 status=MagickTrue;
3187 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003188 GetPixelInfo(implode_image,&zero);
cristy3ed852e2009-09-05 21:47:34 +00003189 image_view=AcquireCacheView(image);
3190 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003191#if defined(MAGICKCORE_OPENMP_SUPPORT)
3192 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003193#endif
cristybb503372010-05-27 20:51:26 +00003194 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003195 {
cristy4c08aed2011-07-01 19:47:50 +00003196 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003197 pixel;
3198
3199 MagickRealType
3200 distance;
3201
3202 PointInfo
3203 delta;
3204
cristybb503372010-05-27 20:51:26 +00003205 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003206 x;
3207
cristy4c08aed2011-07-01 19:47:50 +00003208 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003209 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003210
3211 if (status == MagickFalse)
3212 continue;
3213 q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3214 exception);
cristy4c08aed2011-07-01 19:47:50 +00003215 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003216 {
3217 status=MagickFalse;
3218 continue;
3219 }
cristy3ed852e2009-09-05 21:47:34 +00003220 delta.y=scale.y*(double) (y-center.y);
3221 pixel=zero;
cristybb503372010-05-27 20:51:26 +00003222 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003223 {
3224 /*
3225 Determine if the pixel is within an ellipse.
3226 */
3227 delta.x=scale.x*(double) (x-center.x);
3228 distance=delta.x*delta.x+delta.y*delta.y;
3229 if (distance < (radius*radius))
3230 {
3231 double
3232 factor;
3233
3234 /*
3235 Implode the pixel.
3236 */
3237 factor=1.0;
3238 if (distance > 0.0)
3239 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3240 radius/2)),-amount);
cristy4c08aed2011-07-01 19:47:50 +00003241 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00003242 UndefinedInterpolatePixel,(double) (factor*delta.x/scale.x+
3243 center.x),(double) (factor*delta.y/scale.y+center.y),&pixel,
3244 exception);
cristy4c08aed2011-07-01 19:47:50 +00003245 SetPixelPixelInfo(implode_image,&pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00003246 }
cristyed231572011-07-14 02:18:59 +00003247 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003248 }
3249 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3250 status=MagickFalse;
3251 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3252 {
3253 MagickBooleanType
3254 proceed;
3255
cristyb5d5f722009-11-04 03:03:49 +00003256#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003257 #pragma omp critical (MagickCore_ImplodeImage)
3258#endif
3259 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3260 if (proceed == MagickFalse)
3261 status=MagickFalse;
3262 }
3263 }
3264 implode_view=DestroyCacheView(implode_view);
3265 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003266 if (status == MagickFalse)
3267 implode_image=DestroyImage(implode_image);
3268 return(implode_image);
3269}
3270
3271/*
3272%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3273% %
3274% %
3275% %
3276% M o r p h I m a g e s %
3277% %
3278% %
3279% %
3280%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3281%
3282% The MorphImages() method requires a minimum of two images. The first
3283% image is transformed into the second by a number of intervening images
3284% as specified by frames.
3285%
3286% The format of the MorphImage method is:
3287%
cristybb503372010-05-27 20:51:26 +00003288% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003289% ExceptionInfo *exception)
3290%
3291% A description of each parameter follows:
3292%
3293% o image: the image.
3294%
3295% o number_frames: Define the number of in-between image to generate.
3296% The more in-between frames, the smoother the morph.
3297%
3298% o exception: return any errors or warnings in this structure.
3299%
3300*/
3301MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003302 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003303{
3304#define MorphImageTag "Morph/Image"
3305
3306 Image
3307 *morph_image,
3308 *morph_images;
3309
cristy9d314ff2011-03-09 01:30:28 +00003310 MagickBooleanType
3311 status;
cristy3ed852e2009-09-05 21:47:34 +00003312
3313 MagickOffsetType
3314 scene;
3315
3316 MagickRealType
3317 alpha,
3318 beta;
3319
3320 register const Image
3321 *next;
3322
cristybb503372010-05-27 20:51:26 +00003323 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003324 i;
3325
cristy9d314ff2011-03-09 01:30:28 +00003326 ssize_t
3327 y;
cristy3ed852e2009-09-05 21:47:34 +00003328
3329 /*
3330 Clone first frame in sequence.
3331 */
3332 assert(image != (Image *) NULL);
3333 assert(image->signature == MagickSignature);
3334 if (image->debug != MagickFalse)
3335 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3336 assert(exception != (ExceptionInfo *) NULL);
3337 assert(exception->signature == MagickSignature);
3338 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3339 if (morph_images == (Image *) NULL)
3340 return((Image *) NULL);
3341 if (GetNextImageInList(image) == (Image *) NULL)
3342 {
3343 /*
3344 Morph single image.
3345 */
cristybb503372010-05-27 20:51:26 +00003346 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003347 {
3348 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3349 if (morph_image == (Image *) NULL)
3350 {
3351 morph_images=DestroyImageList(morph_images);
3352 return((Image *) NULL);
3353 }
3354 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003355 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003356 {
cristy8b27a6d2010-02-14 03:31:15 +00003357 MagickBooleanType
3358 proceed;
3359
cristybb503372010-05-27 20:51:26 +00003360 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3361 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003362 if (proceed == MagickFalse)
3363 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003364 }
3365 }
3366 return(GetFirstImageInList(morph_images));
3367 }
3368 /*
3369 Morph image sequence.
3370 */
3371 status=MagickTrue;
3372 scene=0;
3373 next=image;
3374 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3375 {
cristybb503372010-05-27 20:51:26 +00003376 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003377 {
3378 CacheView
3379 *image_view,
3380 *morph_view;
3381
3382 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3383 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003384 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristybb503372010-05-27 20:51:26 +00003385 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
cristy15b98cd2010-09-12 19:42:50 +00003386 next->rows+beta*GetNextImageInList(next)->rows+0.5),
3387 next->filter,next->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003388 if (morph_image == (Image *) NULL)
3389 {
3390 morph_images=DestroyImageList(morph_images);
3391 return((Image *) NULL);
3392 }
3393 if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
3394 {
3395 InheritException(exception,&morph_image->exception);
3396 morph_image=DestroyImage(morph_image);
3397 return((Image *) NULL);
3398 }
3399 AppendImageToList(&morph_images,morph_image);
3400 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003401 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3402 morph_images->rows,GetNextImageInList(next)->filter,
3403 GetNextImageInList(next)->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003404 if (morph_image == (Image *) NULL)
3405 {
3406 morph_images=DestroyImageList(morph_images);
3407 return((Image *) NULL);
3408 }
3409 image_view=AcquireCacheView(morph_image);
3410 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003411#if defined(MAGICKCORE_OPENMP_SUPPORT)
3412 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003413#endif
cristybb503372010-05-27 20:51:26 +00003414 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003415 {
3416 MagickBooleanType
3417 sync;
3418
cristy4c08aed2011-07-01 19:47:50 +00003419 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003420 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003421
cristybb503372010-05-27 20:51:26 +00003422 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003423 x;
3424
cristy4c08aed2011-07-01 19:47:50 +00003425 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003426 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003427
3428 if (status == MagickFalse)
3429 continue;
3430 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3431 exception);
3432 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3433 exception);
cristy4c08aed2011-07-01 19:47:50 +00003434 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003435 {
3436 status=MagickFalse;
3437 continue;
3438 }
cristybb503372010-05-27 20:51:26 +00003439 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003440 {
cristy4c08aed2011-07-01 19:47:50 +00003441 SetPixelRed(morph_images,ClampToQuantum(alpha*
3442 GetPixelRed(morph_images,q)+beta*GetPixelRed(morph_image,p)),q);
3443 SetPixelGreen(morph_images,ClampToQuantum(alpha*
3444 GetPixelGreen(morph_images,q)+beta*GetPixelGreen(morph_image,p)),q);
3445 SetPixelBlue(morph_images,ClampToQuantum(alpha*
3446 GetPixelBlue(morph_images,q)+beta*GetPixelBlue(morph_image,p)),q);
3447 SetPixelAlpha(morph_images,ClampToQuantum(alpha*
3448 GetPixelAlpha(morph_images,q)+beta*GetPixelAlpha(morph_image,p)),q);
3449 if ((morph_image->colorspace == CMYKColorspace) &&
3450 (morph_images->colorspace == CMYKColorspace))
3451 SetPixelBlack(morph_images,ClampToQuantum(alpha*
3452 GetPixelBlack(morph_images,q)+beta*GetPixelBlack(morph_image,p)),
3453 q);
cristyed231572011-07-14 02:18:59 +00003454 p+=GetPixelChannels(morph_image);
3455 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003456 }
3457 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3458 if (sync == MagickFalse)
3459 status=MagickFalse;
3460 }
3461 morph_view=DestroyCacheView(morph_view);
3462 image_view=DestroyCacheView(image_view);
3463 morph_image=DestroyImage(morph_image);
3464 }
cristybb503372010-05-27 20:51:26 +00003465 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003466 break;
3467 /*
3468 Clone last frame in sequence.
3469 */
3470 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3471 if (morph_image == (Image *) NULL)
3472 {
3473 morph_images=DestroyImageList(morph_images);
3474 return((Image *) NULL);
3475 }
3476 AppendImageToList(&morph_images,morph_image);
3477 morph_images=GetLastImageInList(morph_images);
3478 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3479 {
3480 MagickBooleanType
3481 proceed;
3482
cristyb5d5f722009-11-04 03:03:49 +00003483#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003484 #pragma omp critical (MagickCore_MorphImages)
3485#endif
3486 proceed=SetImageProgress(image,MorphImageTag,scene,
3487 GetImageListLength(image));
3488 if (proceed == MagickFalse)
3489 status=MagickFalse;
3490 }
3491 scene++;
3492 }
3493 if (GetNextImageInList(next) != (Image *) NULL)
3494 {
3495 morph_images=DestroyImageList(morph_images);
3496 return((Image *) NULL);
3497 }
3498 return(GetFirstImageInList(morph_images));
3499}
3500
3501/*
3502%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3503% %
3504% %
3505% %
3506% P l a s m a I m a g e %
3507% %
3508% %
3509% %
3510%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3511%
3512% PlasmaImage() initializes an image with plasma fractal values. The image
3513% must be initialized with a base color and the random number generator
3514% seeded before this method is called.
3515%
3516% The format of the PlasmaImage method is:
3517%
3518% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristybb503372010-05-27 20:51:26 +00003519% size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003520%
3521% A description of each parameter follows:
3522%
3523% o image: the image.
3524%
3525% o segment: Define the region to apply plasma fractals values.
3526%
glennrp7dae1ca2010-09-16 12:17:35 +00003527% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003528%
3529% o depth: Limit the plasma recursion depth.
3530%
3531*/
3532
3533static inline Quantum PlasmaPixel(RandomInfo *random_info,
3534 const MagickRealType pixel,const MagickRealType noise)
3535{
3536 Quantum
3537 plasma;
3538
cristyce70c172010-01-07 17:15:30 +00003539 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003540 noise/2.0);
3541 return(plasma);
3542}
3543
3544MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
cristyc5c6f662010-09-22 14:23:02 +00003545 CacheView *image_view,RandomInfo *random_info,const SegmentInfo *segment,
3546 size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003547{
3548 ExceptionInfo
3549 *exception;
3550
cristy3ed852e2009-09-05 21:47:34 +00003551 MagickRealType
3552 plasma;
3553
3554 PixelPacket
3555 u,
3556 v;
3557
cristy9d314ff2011-03-09 01:30:28 +00003558 ssize_t
3559 x,
3560 x_mid,
3561 y,
3562 y_mid;
3563
cristy3ed852e2009-09-05 21:47:34 +00003564 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3565 return(MagickTrue);
3566 if (depth != 0)
3567 {
3568 SegmentInfo
3569 local_info;
3570
3571 /*
3572 Divide the area into quadrants and recurse.
3573 */
3574 depth--;
3575 attenuate++;
cristybb503372010-05-27 20:51:26 +00003576 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3577 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003578 local_info=(*segment);
3579 local_info.x2=(double) x_mid;
3580 local_info.y2=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003581 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3582 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003583 local_info=(*segment);
3584 local_info.y1=(double) y_mid;
3585 local_info.x2=(double) x_mid;
cristyc5c6f662010-09-22 14:23:02 +00003586 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3587 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003588 local_info=(*segment);
3589 local_info.x1=(double) x_mid;
3590 local_info.y2=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003591 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3592 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003593 local_info=(*segment);
3594 local_info.x1=(double) x_mid;
3595 local_info.y1=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003596 return(PlasmaImageProxy(image,image_view,random_info,&local_info,
3597 attenuate,depth));
cristy3ed852e2009-09-05 21:47:34 +00003598 }
cristybb503372010-05-27 20:51:26 +00003599 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3600 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003601 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3602 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3603 return(MagickFalse);
3604 /*
3605 Average pixels and apply plasma.
3606 */
3607 exception=(&image->exception);
3608 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3609 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3610 {
cristy4c08aed2011-07-01 19:47:50 +00003611 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003612 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003613
3614 /*
3615 Left pixel.
3616 */
cristybb503372010-05-27 20:51:26 +00003617 x=(ssize_t) ceil(segment->x1-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003618 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3619 ceil(segment->y1-0.5),&u,exception);
3620 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3621 ceil(segment->y2-0.5),&v,exception);
3622 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003623 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003624 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003625 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3626 (u.red+v.red)/2.0,plasma),q);
3627 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3628 (u.green+v.green)/2.0,plasma),q);
3629 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3630 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003631 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003632 if (segment->x1 != segment->x2)
3633 {
3634 /*
3635 Right pixel.
3636 */
cristybb503372010-05-27 20:51:26 +00003637 x=(ssize_t) ceil(segment->x2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003638 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3639 ceil(segment->y1-0.5),&u,exception);
3640 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3641 ceil(segment->y2-0.5),&v,exception);
3642 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003643 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003644 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003645 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3646 (u.red+v.red)/2.0,plasma),q);
3647 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3648 (u.green+v.green)/2.0,plasma),q);
3649 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3650 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003651 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003652 }
3653 }
3654 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3655 {
3656 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3657 {
cristy4c08aed2011-07-01 19:47:50 +00003658 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003659 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003660
3661 /*
3662 Bottom pixel.
3663 */
cristybb503372010-05-27 20:51:26 +00003664 y=(ssize_t) ceil(segment->y2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003665 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3666 ceil(segment->x1-0.5),y,&u,exception);
3667 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3668 ceil(segment->x2-0.5),y,&v,exception);
3669 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003670 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003671 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003672 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3673 (u.red+v.red)/2.0,plasma),q);
3674 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3675 (u.green+v.green)/2.0,plasma),q);
3676 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3677 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003678 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003679 }
3680 if (segment->y1 != segment->y2)
3681 {
cristy4c08aed2011-07-01 19:47:50 +00003682 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003683 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003684
3685 /*
3686 Top pixel.
3687 */
cristybb503372010-05-27 20:51:26 +00003688 y=(ssize_t) ceil(segment->y1-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003689 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3690 ceil(segment->x1-0.5),y,&u,exception);
3691 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3692 ceil(segment->x2-0.5),y,&v,exception);
3693 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003694 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003695 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003696 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3697 (u.red+v.red)/2.0,plasma),q);
3698 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3699 (u.green+v.green)/2.0,plasma),q);
3700 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3701 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003702 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003703 }
3704 }
3705 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3706 {
cristy4c08aed2011-07-01 19:47:50 +00003707 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003708 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003709
3710 /*
3711 Middle pixel.
3712 */
cristybb503372010-05-27 20:51:26 +00003713 x=(ssize_t) ceil(segment->x1-0.5);
3714 y=(ssize_t) ceil(segment->y1-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003715 (void) GetOneVirtualPixel(image,x,y,&u,exception);
cristybb503372010-05-27 20:51:26 +00003716 x=(ssize_t) ceil(segment->x2-0.5);
3717 y=(ssize_t) ceil(segment->y2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003718 (void) GetOneCacheViewVirtualPixel(image_view,x,y,&v,exception);
3719 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003720 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003721 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003722 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3723 (u.red+v.red)/2.0,plasma),q);
3724 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3725 (u.green+v.green)/2.0,plasma),q);
3726 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3727 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003728 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003729 }
3730 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3731 return(MagickTrue);
3732 return(MagickFalse);
3733}
3734
3735MagickExport MagickBooleanType PlasmaImage(Image *image,
cristybb503372010-05-27 20:51:26 +00003736 const SegmentInfo *segment,size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003737{
cristyc5c6f662010-09-22 14:23:02 +00003738 CacheView
3739 *image_view;
3740
cristy3ed852e2009-09-05 21:47:34 +00003741 MagickBooleanType
3742 status;
3743
3744 RandomInfo
3745 *random_info;
3746
3747 if (image->debug != MagickFalse)
3748 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3749 assert(image != (Image *) NULL);
3750 assert(image->signature == MagickSignature);
3751 if (image->debug != MagickFalse)
3752 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristyc5c6f662010-09-22 14:23:02 +00003753 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
3754 return(MagickFalse);
3755 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003756 random_info=AcquireRandomInfo();
cristyc5c6f662010-09-22 14:23:02 +00003757 status=PlasmaImageProxy(image,image_view,random_info,segment,attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003758 random_info=DestroyRandomInfo(random_info);
cristyc5c6f662010-09-22 14:23:02 +00003759 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003760 return(status);
3761}
3762
3763/*
3764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3765% %
3766% %
3767% %
3768% P o l a r o i d I m a g e %
3769% %
3770% %
3771% %
3772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3773%
3774% PolaroidImage() simulates a Polaroid picture.
3775%
3776% The format of the AnnotateImage method is:
3777%
3778% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3779% const double angle,ExceptionInfo exception)
3780%
3781% A description of each parameter follows:
3782%
3783% o image: the image.
3784%
3785% o draw_info: the draw info.
3786%
cristycee97112010-05-28 00:44:52 +00003787% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003788%
3789% o exception: return any errors or warnings in this structure.
3790%
3791*/
3792MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3793 const double angle,ExceptionInfo *exception)
3794{
3795 const char
3796 *value;
3797
cristy3ed852e2009-09-05 21:47:34 +00003798 Image
3799 *bend_image,
3800 *caption_image,
3801 *flop_image,
3802 *picture_image,
3803 *polaroid_image,
3804 *rotate_image,
3805 *trim_image;
3806
cristybb503372010-05-27 20:51:26 +00003807 size_t
cristy3ed852e2009-09-05 21:47:34 +00003808 height;
3809
cristy9d314ff2011-03-09 01:30:28 +00003810 ssize_t
3811 quantum;
3812
cristy3ed852e2009-09-05 21:47:34 +00003813 /*
3814 Simulate a Polaroid picture.
3815 */
3816 assert(image != (Image *) NULL);
3817 assert(image->signature == MagickSignature);
3818 if (image->debug != MagickFalse)
3819 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3820 assert(exception != (ExceptionInfo *) NULL);
3821 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003822 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003823 image->rows)/25.0,10.0);
3824 height=image->rows+2*quantum;
3825 caption_image=(Image *) NULL;
3826 value=GetImageProperty(image,"Caption");
3827 if (value != (const char *) NULL)
3828 {
3829 char
3830 *caption,
3831 geometry[MaxTextExtent];
3832
3833 DrawInfo
3834 *annotate_info;
3835
cristy3ed852e2009-09-05 21:47:34 +00003836 MagickBooleanType
3837 status;
3838
cristy9d314ff2011-03-09 01:30:28 +00003839 ssize_t
3840 count;
3841
cristy3ed852e2009-09-05 21:47:34 +00003842 TypeMetric
3843 metrics;
3844
3845 /*
3846 Generate caption image.
3847 */
3848 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3849 if (caption_image == (Image *) NULL)
3850 return((Image *) NULL);
3851 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
3852 caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
3853 value);
3854 (void) CloneString(&annotate_info->text,caption);
cristy6b1d05e2010-09-22 19:17:27 +00003855 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
3856 &caption);
cristybb503372010-05-27 20:51:26 +00003857 status=SetImageExtent(caption_image,image->columns,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00003858 ((count+1)*(metrics.ascent-metrics.descent)+0.5));
3859 if (status == MagickFalse)
3860 caption_image=DestroyImage(caption_image);
3861 else
3862 {
3863 caption_image->background_color=image->border_color;
3864 (void) SetImageBackgroundColor(caption_image);
3865 (void) CloneString(&annotate_info->text,caption);
cristyb51dff52011-05-19 16:55:47 +00003866 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00003867 metrics.ascent);
3868 if (annotate_info->gravity == UndefinedGravity)
3869 (void) CloneString(&annotate_info->geometry,AcquireString(
3870 geometry));
3871 (void) AnnotateImage(caption_image,annotate_info);
3872 height+=caption_image->rows;
3873 }
3874 annotate_info=DestroyDrawInfo(annotate_info);
3875 caption=DestroyString(caption);
3876 }
3877 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3878 exception);
3879 if (picture_image == (Image *) NULL)
3880 {
3881 if (caption_image != (Image *) NULL)
3882 caption_image=DestroyImage(caption_image);
3883 return((Image *) NULL);
3884 }
3885 picture_image->background_color=image->border_color;
3886 (void) SetImageBackgroundColor(picture_image);
3887 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
3888 if (caption_image != (Image *) NULL)
3889 {
3890 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
cristybb503372010-05-27 20:51:26 +00003891 quantum,(ssize_t) (image->rows+3*quantum/2));
cristy3ed852e2009-09-05 21:47:34 +00003892 caption_image=DestroyImage(caption_image);
3893 }
3894 (void) QueryColorDatabase("none",&picture_image->background_color,exception);
3895 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
3896 rotate_image=RotateImage(picture_image,90.0,exception);
3897 picture_image=DestroyImage(picture_image);
3898 if (rotate_image == (Image *) NULL)
3899 return((Image *) NULL);
3900 picture_image=rotate_image;
3901 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
3902 picture_image->columns,exception);
3903 picture_image=DestroyImage(picture_image);
3904 if (bend_image == (Image *) NULL)
3905 return((Image *) NULL);
3906 InheritException(&bend_image->exception,exception);
3907 picture_image=bend_image;
3908 rotate_image=RotateImage(picture_image,-90.0,exception);
3909 picture_image=DestroyImage(picture_image);
3910 if (rotate_image == (Image *) NULL)
3911 return((Image *) NULL);
3912 picture_image=rotate_image;
3913 picture_image->background_color=image->background_color;
3914 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
3915 exception);
3916 if (polaroid_image == (Image *) NULL)
3917 {
3918 picture_image=DestroyImage(picture_image);
3919 return(picture_image);
3920 }
3921 flop_image=FlopImage(polaroid_image,exception);
3922 polaroid_image=DestroyImage(polaroid_image);
3923 if (flop_image == (Image *) NULL)
3924 {
3925 picture_image=DestroyImage(picture_image);
3926 return(picture_image);
3927 }
3928 polaroid_image=flop_image;
3929 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
cristybb503372010-05-27 20:51:26 +00003930 (ssize_t) (-0.01*picture_image->columns/2.0),0L);
cristy3ed852e2009-09-05 21:47:34 +00003931 picture_image=DestroyImage(picture_image);
3932 (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
3933 rotate_image=RotateImage(polaroid_image,angle,exception);
3934 polaroid_image=DestroyImage(polaroid_image);
3935 if (rotate_image == (Image *) NULL)
3936 return((Image *) NULL);
3937 polaroid_image=rotate_image;
3938 trim_image=TrimImage(polaroid_image,exception);
3939 polaroid_image=DestroyImage(polaroid_image);
3940 if (trim_image == (Image *) NULL)
3941 return((Image *) NULL);
3942 polaroid_image=trim_image;
3943 return(polaroid_image);
3944}
3945
3946/*
3947%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3948% %
3949% %
3950% %
cristy3ed852e2009-09-05 21:47:34 +00003951% S e p i a T o n e I m a g e %
3952% %
3953% %
3954% %
3955%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3956%
3957% MagickSepiaToneImage() applies a special effect to the image, similar to the
3958% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
3959% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
3960% threshold of 80% is a good starting point for a reasonable tone.
3961%
3962% The format of the SepiaToneImage method is:
3963%
3964% Image *SepiaToneImage(const Image *image,const double threshold,
3965% ExceptionInfo *exception)
3966%
3967% A description of each parameter follows:
3968%
3969% o image: the image.
3970%
3971% o threshold: the tone threshold.
3972%
3973% o exception: return any errors or warnings in this structure.
3974%
3975*/
3976MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
3977 ExceptionInfo *exception)
3978{
3979#define SepiaToneImageTag "SepiaTone/Image"
3980
cristyc4c8d132010-01-07 01:58:38 +00003981 CacheView
3982 *image_view,
3983 *sepia_view;
3984
cristy3ed852e2009-09-05 21:47:34 +00003985 Image
3986 *sepia_image;
3987
cristy3ed852e2009-09-05 21:47:34 +00003988 MagickBooleanType
3989 status;
3990
cristybb503372010-05-27 20:51:26 +00003991 MagickOffsetType
3992 progress;
3993
3994 ssize_t
3995 y;
3996
cristy3ed852e2009-09-05 21:47:34 +00003997 /*
3998 Initialize sepia-toned image attributes.
3999 */
4000 assert(image != (const Image *) NULL);
4001 assert(image->signature == MagickSignature);
4002 if (image->debug != MagickFalse)
4003 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4004 assert(exception != (ExceptionInfo *) NULL);
4005 assert(exception->signature == MagickSignature);
4006 sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4007 if (sepia_image == (Image *) NULL)
4008 return((Image *) NULL);
4009 if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
4010 {
4011 InheritException(exception,&sepia_image->exception);
4012 sepia_image=DestroyImage(sepia_image);
4013 return((Image *) NULL);
4014 }
4015 /*
4016 Tone each row of the image.
4017 */
4018 status=MagickTrue;
4019 progress=0;
4020 image_view=AcquireCacheView(image);
4021 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004022#if defined(MAGICKCORE_OPENMP_SUPPORT)
4023 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004024#endif
cristybb503372010-05-27 20:51:26 +00004025 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004026 {
cristy4c08aed2011-07-01 19:47:50 +00004027 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004028 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004029
cristybb503372010-05-27 20:51:26 +00004030 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004031 x;
4032
cristy4c08aed2011-07-01 19:47:50 +00004033 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004034 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004035
4036 if (status == MagickFalse)
4037 continue;
4038 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4039 q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4040 exception);
cristy4c08aed2011-07-01 19:47:50 +00004041 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004042 {
4043 status=MagickFalse;
4044 continue;
4045 }
cristybb503372010-05-27 20:51:26 +00004046 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004047 {
4048 MagickRealType
4049 intensity,
4050 tone;
4051
cristy4c08aed2011-07-01 19:47:50 +00004052 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004053 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4054 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004055 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004056 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4057 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004058 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004059 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004060 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004061 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004062 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4063 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4064 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4065 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004066 p+=GetPixelChannels(image);
4067 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004068 }
4069 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4070 status=MagickFalse;
4071 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4072 {
4073 MagickBooleanType
4074 proceed;
4075
cristyb5d5f722009-11-04 03:03:49 +00004076#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004077 #pragma omp critical (MagickCore_SepiaToneImage)
4078#endif
4079 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4080 image->rows);
4081 if (proceed == MagickFalse)
4082 status=MagickFalse;
4083 }
4084 }
4085 sepia_view=DestroyCacheView(sepia_view);
4086 image_view=DestroyCacheView(image_view);
4087 (void) NormalizeImage(sepia_image);
4088 (void) ContrastImage(sepia_image,MagickTrue);
4089 if (status == MagickFalse)
4090 sepia_image=DestroyImage(sepia_image);
4091 return(sepia_image);
4092}
4093
4094/*
4095%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4096% %
4097% %
4098% %
4099% S h a d o w I m a g e %
4100% %
4101% %
4102% %
4103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4104%
4105% ShadowImage() simulates a shadow from the specified image and returns it.
4106%
4107% The format of the ShadowImage method is:
4108%
4109% Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004110% const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004111% ExceptionInfo *exception)
4112%
4113% A description of each parameter follows:
4114%
4115% o image: the image.
4116%
4117% o opacity: percentage transparency.
4118%
4119% o sigma: the standard deviation of the Gaussian, in pixels.
4120%
4121% o x_offset: the shadow x-offset.
4122%
4123% o y_offset: the shadow y-offset.
4124%
4125% o exception: return any errors or warnings in this structure.
4126%
4127*/
4128MagickExport Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004129 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004130 ExceptionInfo *exception)
4131{
4132#define ShadowImageTag "Shadow/Image"
4133
cristyc4c8d132010-01-07 01:58:38 +00004134 CacheView
cristy4c08aed2011-07-01 19:47:50 +00004135 *border_view;
cristyc4c8d132010-01-07 01:58:38 +00004136
cristy3ed852e2009-09-05 21:47:34 +00004137 Image
4138 *border_image,
4139 *clone_image,
4140 *shadow_image;
4141
cristy3ed852e2009-09-05 21:47:34 +00004142 MagickBooleanType
4143 status;
4144
cristybb503372010-05-27 20:51:26 +00004145 MagickOffsetType
4146 progress;
4147
cristy3ed852e2009-09-05 21:47:34 +00004148 RectangleInfo
4149 border_info;
4150
cristybb503372010-05-27 20:51:26 +00004151 ssize_t
4152 y;
4153
cristy3ed852e2009-09-05 21:47:34 +00004154 assert(image != (Image *) NULL);
4155 assert(image->signature == MagickSignature);
4156 if (image->debug != MagickFalse)
4157 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4158 assert(exception != (ExceptionInfo *) NULL);
4159 assert(exception->signature == MagickSignature);
4160 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4161 if (clone_image == (Image *) NULL)
4162 return((Image *) NULL);
4163 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4164 clone_image->compose=OverCompositeOp;
cristybb503372010-05-27 20:51:26 +00004165 border_info.width=(size_t) floor(2.0*sigma+0.5);
4166 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004167 border_info.x=0;
4168 border_info.y=0;
4169 (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4170 border_image=BorderImage(clone_image,&border_info,exception);
4171 clone_image=DestroyImage(clone_image);
4172 if (border_image == (Image *) NULL)
4173 return((Image *) NULL);
4174 if (border_image->matte == MagickFalse)
4175 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4176 /*
4177 Shadow image.
4178 */
4179 status=MagickTrue;
4180 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00004181 border_view=AcquireCacheView(border_image);
cristyb5d5f722009-11-04 03:03:49 +00004182#if defined(MAGICKCORE_OPENMP_SUPPORT)
4183 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004184#endif
cristybb503372010-05-27 20:51:26 +00004185 for (y=0; y < (ssize_t) border_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004186 {
cristybb503372010-05-27 20:51:26 +00004187 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004188 x;
4189
cristy4c08aed2011-07-01 19:47:50 +00004190 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004191 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004192
4193 if (status == MagickFalse)
4194 continue;
cristy4c08aed2011-07-01 19:47:50 +00004195 q=GetCacheViewAuthenticPixels(border_view,0,y,border_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004196 exception);
cristy4c08aed2011-07-01 19:47:50 +00004197 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004198 {
4199 status=MagickFalse;
4200 continue;
4201 }
cristybb503372010-05-27 20:51:26 +00004202 for (x=0; x < (ssize_t) border_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004203 {
cristy4c08aed2011-07-01 19:47:50 +00004204 SetPixelRed(border_image,border_image->background_color.red,q);
4205 SetPixelGreen(border_image,border_image->background_color.green,q);
4206 SetPixelBlue(border_image,border_image->background_color.blue,q);
cristy3ed852e2009-09-05 21:47:34 +00004207 if (border_image->matte == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004208 SetPixelAlpha(border_image,border_image->background_color.alpha,q);
cristy3ed852e2009-09-05 21:47:34 +00004209 else
cristy4c08aed2011-07-01 19:47:50 +00004210 SetPixelAlpha(border_image,ClampToQuantum((MagickRealType)
4211 (GetPixelAlpha(border_image,q)*opacity/100.0)),q);
cristyed231572011-07-14 02:18:59 +00004212 q+=GetPixelChannels(border_image);
cristy3ed852e2009-09-05 21:47:34 +00004213 }
cristy4c08aed2011-07-01 19:47:50 +00004214 if (SyncCacheViewAuthenticPixels(border_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004215 status=MagickFalse;
cristy4c08aed2011-07-01 19:47:50 +00004216 if (border_image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004217 {
4218 MagickBooleanType
4219 proceed;
4220
cristyb5d5f722009-11-04 03:03:49 +00004221#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004222 #pragma omp critical (MagickCore_ShadowImage)
4223#endif
cristy4c08aed2011-07-01 19:47:50 +00004224 proceed=SetImageProgress(border_image,ShadowImageTag,progress++,
cristy3ed852e2009-09-05 21:47:34 +00004225 border_image->rows);
4226 if (proceed == MagickFalse)
4227 status=MagickFalse;
4228 }
4229 }
cristy4c08aed2011-07-01 19:47:50 +00004230 border_view=DestroyCacheView(border_view);
cristyed231572011-07-14 02:18:59 +00004231 PushPixelChannelMap(border_image,AlphaChannel);
cristyf4ad9df2011-07-08 16:49:03 +00004232 shadow_image=BlurImage(border_image,0.0,sigma,exception);
cristyed231572011-07-14 02:18:59 +00004233 PopPixelChannelMap(border_image);
cristy3ed852e2009-09-05 21:47:34 +00004234 border_image=DestroyImage(border_image);
4235 if (shadow_image == (Image *) NULL)
4236 return((Image *) NULL);
4237 if (shadow_image->page.width == 0)
4238 shadow_image->page.width=shadow_image->columns;
4239 if (shadow_image->page.height == 0)
4240 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004241 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4242 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4243 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4244 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004245 return(shadow_image);
4246}
4247
4248/*
4249%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4250% %
4251% %
4252% %
4253% S k e t c h I m a g e %
4254% %
4255% %
4256% %
4257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4258%
4259% SketchImage() simulates a pencil sketch. We convolve the image with a
4260% Gaussian operator of the given radius and standard deviation (sigma). For
4261% reasonable results, radius should be larger than sigma. Use a radius of 0
4262% and SketchImage() selects a suitable radius for you. Angle gives the angle
4263% of the sketch.
4264%
4265% The format of the SketchImage method is:
4266%
4267% Image *SketchImage(const Image *image,const double radius,
4268% const double sigma,const double angle,ExceptionInfo *exception)
4269%
4270% A description of each parameter follows:
4271%
4272% o image: the image.
4273%
4274% o radius: the radius of the Gaussian, in pixels, not counting
4275% the center pixel.
4276%
4277% o sigma: the standard deviation of the Gaussian, in pixels.
4278%
cristycee97112010-05-28 00:44:52 +00004279% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00004280%
4281% o exception: return any errors or warnings in this structure.
4282%
4283*/
4284MagickExport Image *SketchImage(const Image *image,const double radius,
4285 const double sigma,const double angle,ExceptionInfo *exception)
4286{
cristyfa112112010-01-04 17:48:07 +00004287 CacheView
4288 *random_view;
4289
cristy3ed852e2009-09-05 21:47:34 +00004290 Image
4291 *blend_image,
4292 *blur_image,
4293 *dodge_image,
4294 *random_image,
4295 *sketch_image;
4296
cristy3ed852e2009-09-05 21:47:34 +00004297 MagickBooleanType
4298 status;
4299
cristy4c08aed2011-07-01 19:47:50 +00004300 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004301 zero;
4302
4303 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004304 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004305
cristy9d314ff2011-03-09 01:30:28 +00004306 ssize_t
4307 y;
4308
cristy3ed852e2009-09-05 21:47:34 +00004309 /*
4310 Sketch image.
4311 */
4312 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4313 MagickTrue,exception);
4314 if (random_image == (Image *) NULL)
4315 return((Image *) NULL);
4316 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00004317 GetPixelInfo(random_image,&zero);
cristy1b784432009-12-19 02:20:40 +00004318 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004319 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004320#if defined(MAGICKCORE_OPENMP_SUPPORT)
4321 #pragma omp parallel for schedule(dynamic,4) shared(status)
4322#endif
cristybb503372010-05-27 20:51:26 +00004323 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004324 {
cristy5c9e6f22010-09-17 17:31:01 +00004325 const int
4326 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004327
cristy4c08aed2011-07-01 19:47:50 +00004328 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004329 pixel;
4330
cristybb503372010-05-27 20:51:26 +00004331 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004332 x;
4333
cristy4c08aed2011-07-01 19:47:50 +00004334 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004335 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004336
cristy1b784432009-12-19 02:20:40 +00004337 if (status == MagickFalse)
4338 continue;
cristy3ed852e2009-09-05 21:47:34 +00004339 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4340 exception);
cristy4c08aed2011-07-01 19:47:50 +00004341 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004342 {
4343 status=MagickFalse;
4344 continue;
4345 }
cristy3ed852e2009-09-05 21:47:34 +00004346 pixel=zero;
cristybb503372010-05-27 20:51:26 +00004347 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004348 {
4349 pixel.red=(MagickRealType) (QuantumRange*
cristy1b784432009-12-19 02:20:40 +00004350 GetPseudoRandomValue(random_info[id]));
cristy3ed852e2009-09-05 21:47:34 +00004351 pixel.green=pixel.red;
4352 pixel.blue=pixel.red;
4353 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004354 pixel.black=pixel.red;
4355 SetPixelPixelInfo(random_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00004356 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004357 }
4358 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4359 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004360 }
4361 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004362 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004363 if (status == MagickFalse)
4364 {
4365 random_image=DestroyImage(random_image);
4366 return(random_image);
4367 }
4368 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4369 random_image=DestroyImage(random_image);
4370 if (blur_image == (Image *) NULL)
4371 return((Image *) NULL);
4372 dodge_image=EdgeImage(blur_image,radius,exception);
4373 blur_image=DestroyImage(blur_image);
4374 if (dodge_image == (Image *) NULL)
4375 return((Image *) NULL);
4376 (void) NormalizeImage(dodge_image);
cristyb3e7c6c2011-07-24 01:43:55 +00004377 (void) NegateImage(dodge_image,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00004378 (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4379 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4380 if (sketch_image == (Image *) NULL)
4381 {
4382 dodge_image=DestroyImage(dodge_image);
4383 return((Image *) NULL);
4384 }
4385 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4386 dodge_image=DestroyImage(dodge_image);
4387 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4388 if (blend_image == (Image *) NULL)
4389 {
4390 sketch_image=DestroyImage(sketch_image);
4391 return((Image *) NULL);
4392 }
4393 (void) SetImageArtifact(blend_image,"compose:args","20x80");
4394 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4395 blend_image=DestroyImage(blend_image);
4396 return(sketch_image);
4397}
4398
4399/*
4400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4401% %
4402% %
4403% %
4404% S o l a r i z e I m a g e %
4405% %
4406% %
4407% %
4408%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4409%
4410% SolarizeImage() applies a special effect to the image, similar to the effect
4411% achieved in a photo darkroom by selectively exposing areas of photo
4412% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4413% measure of the extent of the solarization.
4414%
4415% The format of the SolarizeImage method is:
4416%
4417% MagickBooleanType SolarizeImage(Image *image,const double threshold)
4418%
4419% A description of each parameter follows:
4420%
4421% o image: the image.
4422%
4423% o threshold: Define the extent of the solarization.
4424%
4425*/
4426MagickExport MagickBooleanType SolarizeImage(Image *image,
4427 const double threshold)
4428{
4429#define SolarizeImageTag "Solarize/Image"
4430
cristyc4c8d132010-01-07 01:58:38 +00004431 CacheView
4432 *image_view;
4433
cristy3ed852e2009-09-05 21:47:34 +00004434 ExceptionInfo
4435 *exception;
4436
cristy3ed852e2009-09-05 21:47:34 +00004437 MagickBooleanType
4438 status;
4439
cristybb503372010-05-27 20:51:26 +00004440 MagickOffsetType
4441 progress;
4442
4443 ssize_t
4444 y;
4445
cristy3ed852e2009-09-05 21:47:34 +00004446 assert(image != (Image *) NULL);
4447 assert(image->signature == MagickSignature);
4448 if (image->debug != MagickFalse)
4449 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4450 if (image->storage_class == PseudoClass)
4451 {
cristybb503372010-05-27 20:51:26 +00004452 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004453 i;
4454
4455 /*
4456 Solarize colormap.
4457 */
cristybb503372010-05-27 20:51:26 +00004458 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004459 {
4460 if ((MagickRealType) image->colormap[i].red > threshold)
4461 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4462 if ((MagickRealType) image->colormap[i].green > threshold)
4463 image->colormap[i].green=(Quantum) QuantumRange-
4464 image->colormap[i].green;
4465 if ((MagickRealType) image->colormap[i].blue > threshold)
4466 image->colormap[i].blue=(Quantum) QuantumRange-
4467 image->colormap[i].blue;
4468 }
4469 }
4470 /*
4471 Solarize image.
4472 */
4473 status=MagickTrue;
4474 progress=0;
4475 exception=(&image->exception);
4476 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004477#if defined(MAGICKCORE_OPENMP_SUPPORT)
4478 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004479#endif
cristybb503372010-05-27 20:51:26 +00004480 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004481 {
cristybb503372010-05-27 20:51:26 +00004482 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004483 x;
4484
cristy4c08aed2011-07-01 19:47:50 +00004485 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004486 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004487
4488 if (status == MagickFalse)
4489 continue;
4490 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4491 exception);
cristy4c08aed2011-07-01 19:47:50 +00004492 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004493 {
4494 status=MagickFalse;
4495 continue;
4496 }
cristybb503372010-05-27 20:51:26 +00004497 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004498 {
cristy4c08aed2011-07-01 19:47:50 +00004499 if ((MagickRealType) GetPixelRed(image,q) > threshold)
4500 SetPixelRed(image,QuantumRange-GetPixelRed(image,q),q);
4501 if ((MagickRealType) GetPixelGreen(image,q) > threshold)
4502 SetPixelGreen(image,QuantumRange-GetPixelGreen(image,q),q);
4503 if ((MagickRealType) GetPixelBlue(image,q) > threshold)
4504 SetPixelBlue(image,QuantumRange-GetPixelBlue(image,q),q);
cristyed231572011-07-14 02:18:59 +00004505 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004506 }
4507 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4508 status=MagickFalse;
4509 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4510 {
4511 MagickBooleanType
4512 proceed;
4513
cristyb5d5f722009-11-04 03:03:49 +00004514#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004515 #pragma omp critical (MagickCore_SolarizeImage)
4516#endif
4517 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4518 if (proceed == MagickFalse)
4519 status=MagickFalse;
4520 }
4521 }
4522 image_view=DestroyCacheView(image_view);
4523 return(status);
4524}
4525
4526/*
4527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4528% %
4529% %
4530% %
4531% S t e g a n o I m a g e %
4532% %
4533% %
4534% %
4535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4536%
4537% SteganoImage() hides a digital watermark within the image. Recover
4538% the hidden watermark later to prove that the authenticity of an image.
4539% Offset defines the start position within the image to hide the watermark.
4540%
4541% The format of the SteganoImage method is:
4542%
4543% Image *SteganoImage(const Image *image,Image *watermark,
4544% ExceptionInfo *exception)
4545%
4546% A description of each parameter follows:
4547%
4548% o image: the image.
4549%
4550% o watermark: the watermark image.
4551%
4552% o exception: return any errors or warnings in this structure.
4553%
4554*/
4555MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4556 ExceptionInfo *exception)
4557{
cristye1bf8ad2010-09-19 17:07:03 +00004558#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004559#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004560 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004561#define SteganoImageTag "Stegano/Image"
4562
cristyb0d3bb92010-09-22 14:37:58 +00004563 CacheView
4564 *stegano_view,
4565 *watermark_view;
4566
cristy3ed852e2009-09-05 21:47:34 +00004567 Image
4568 *stegano_image;
4569
4570 int
4571 c;
4572
cristy3ed852e2009-09-05 21:47:34 +00004573 MagickBooleanType
4574 status;
4575
4576 PixelPacket
4577 pixel;
4578
cristy4c08aed2011-07-01 19:47:50 +00004579 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004580 *q;
4581
cristye1bf8ad2010-09-19 17:07:03 +00004582 register ssize_t
4583 x;
4584
cristybb503372010-05-27 20:51:26 +00004585 size_t
cristyeaedf062010-05-29 22:36:02 +00004586 depth,
4587 one;
cristy3ed852e2009-09-05 21:47:34 +00004588
cristye1bf8ad2010-09-19 17:07:03 +00004589 ssize_t
4590 i,
4591 j,
4592 k,
4593 y;
4594
cristy3ed852e2009-09-05 21:47:34 +00004595 /*
4596 Initialize steganographic image attributes.
4597 */
4598 assert(image != (const Image *) NULL);
4599 assert(image->signature == MagickSignature);
4600 if (image->debug != MagickFalse)
4601 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4602 assert(watermark != (const Image *) NULL);
4603 assert(watermark->signature == MagickSignature);
4604 assert(exception != (ExceptionInfo *) NULL);
4605 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004606 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004607 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4608 if (stegano_image == (Image *) NULL)
4609 return((Image *) NULL);
4610 if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
4611 {
4612 InheritException(exception,&stegano_image->exception);
4613 stegano_image=DestroyImage(stegano_image);
4614 return((Image *) NULL);
4615 }
4616 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4617 /*
4618 Hide watermark in low-order bits of image.
4619 */
4620 c=0;
4621 i=0;
4622 j=0;
4623 depth=stegano_image->depth;
4624 k=image->offset;
cristyda16f162011-02-19 23:52:17 +00004625 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004626 watermark_view=AcquireCacheView(watermark);
4627 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004628 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004629 {
cristybb503372010-05-27 20:51:26 +00004630 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004631 {
cristybb503372010-05-27 20:51:26 +00004632 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004633 {
cristyb0d3bb92010-09-22 14:37:58 +00004634 (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,&pixel,exception);
cristybb503372010-05-27 20:51:26 +00004635 if ((k/(ssize_t) stegano_image->columns) >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004636 break;
cristyb0d3bb92010-09-22 14:37:58 +00004637 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4638 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4639 exception);
cristy4c08aed2011-07-01 19:47:50 +00004640 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004641 break;
4642 switch (c)
4643 {
4644 case 0:
4645 {
cristy4c08aed2011-07-01 19:47:50 +00004646 SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
4647 GetPixelPacketIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004648 break;
4649 }
4650 case 1:
4651 {
cristy4c08aed2011-07-01 19:47:50 +00004652 SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
4653 GetPixelPacketIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004654 break;
4655 }
4656 case 2:
4657 {
cristy4c08aed2011-07-01 19:47:50 +00004658 SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
4659 GetPixelPacketIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004660 break;
4661 }
4662 }
cristyb0d3bb92010-09-22 14:37:58 +00004663 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004664 break;
4665 c++;
4666 if (c == 3)
4667 c=0;
4668 k++;
cristybb503372010-05-27 20:51:26 +00004669 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004670 k=0;
4671 if (k == image->offset)
4672 j++;
4673 }
4674 }
cristy8b27a6d2010-02-14 03:31:15 +00004675 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004676 {
cristy8b27a6d2010-02-14 03:31:15 +00004677 MagickBooleanType
4678 proceed;
4679
4680 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4681 (depth-i),depth);
4682 if (proceed == MagickFalse)
4683 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004684 }
4685 }
cristyb0d3bb92010-09-22 14:37:58 +00004686 stegano_view=DestroyCacheView(stegano_view);
4687 watermark_view=DestroyCacheView(watermark_view);
cristy3ed852e2009-09-05 21:47:34 +00004688 if (stegano_image->storage_class == PseudoClass)
4689 (void) SyncImage(stegano_image);
cristyda16f162011-02-19 23:52:17 +00004690 if (status == MagickFalse)
4691 {
4692 stegano_image=DestroyImage(stegano_image);
4693 return((Image *) NULL);
4694 }
cristy3ed852e2009-09-05 21:47:34 +00004695 return(stegano_image);
4696}
4697
4698/*
4699%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4700% %
4701% %
4702% %
4703% S t e r e o A n a g l y p h I m a g e %
4704% %
4705% %
4706% %
4707%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4708%
4709% StereoAnaglyphImage() combines two images and produces a single image that
4710% is the composite of a left and right image of a stereo pair. Special
4711% red-green stereo glasses are required to view this effect.
4712%
4713% The format of the StereoAnaglyphImage method is:
4714%
4715% Image *StereoImage(const Image *left_image,const Image *right_image,
4716% ExceptionInfo *exception)
4717% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004718% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004719% ExceptionInfo *exception)
4720%
4721% A description of each parameter follows:
4722%
4723% o left_image: the left image.
4724%
4725% o right_image: the right image.
4726%
4727% o exception: return any errors or warnings in this structure.
4728%
4729% o x_offset: amount, in pixels, by which the left image is offset to the
4730% right of the right image.
4731%
4732% o y_offset: amount, in pixels, by which the left image is offset to the
4733% bottom of the right image.
4734%
4735%
4736*/
4737MagickExport Image *StereoImage(const Image *left_image,
4738 const Image *right_image,ExceptionInfo *exception)
4739{
4740 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4741}
4742
4743MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004744 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004745 ExceptionInfo *exception)
4746{
4747#define StereoImageTag "Stereo/Image"
4748
4749 const Image
4750 *image;
4751
4752 Image
4753 *stereo_image;
4754
cristy3ed852e2009-09-05 21:47:34 +00004755 MagickBooleanType
4756 status;
4757
cristy9d314ff2011-03-09 01:30:28 +00004758 ssize_t
4759 y;
4760
cristy3ed852e2009-09-05 21:47:34 +00004761 assert(left_image != (const Image *) NULL);
4762 assert(left_image->signature == MagickSignature);
4763 if (left_image->debug != MagickFalse)
4764 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4765 left_image->filename);
4766 assert(right_image != (const Image *) NULL);
4767 assert(right_image->signature == MagickSignature);
4768 assert(exception != (ExceptionInfo *) NULL);
4769 assert(exception->signature == MagickSignature);
4770 assert(right_image != (const Image *) NULL);
4771 image=left_image;
4772 if ((left_image->columns != right_image->columns) ||
4773 (left_image->rows != right_image->rows))
4774 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4775 /*
4776 Initialize stereo image attributes.
4777 */
4778 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4779 MagickTrue,exception);
4780 if (stereo_image == (Image *) NULL)
4781 return((Image *) NULL);
4782 if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
4783 {
4784 InheritException(exception,&stereo_image->exception);
4785 stereo_image=DestroyImage(stereo_image);
4786 return((Image *) NULL);
4787 }
4788 /*
4789 Copy left image to red channel and right image to blue channel.
4790 */
cristyda16f162011-02-19 23:52:17 +00004791 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004792 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004793 {
cristy4c08aed2011-07-01 19:47:50 +00004794 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004795 *restrict p,
4796 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004797
cristybb503372010-05-27 20:51:26 +00004798 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004799 x;
4800
cristy4c08aed2011-07-01 19:47:50 +00004801 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004802 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004803
4804 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4805 exception);
4806 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4807 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004808 if ((p == (const Quantum *) NULL) ||
4809 (q == (const Quantum *) NULL) || (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004810 break;
cristybb503372010-05-27 20:51:26 +00004811 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004812 {
cristy4c08aed2011-07-01 19:47:50 +00004813 SetPixelRed(image,GetPixelRed(left_image,p),r);
4814 SetPixelGreen(image,GetPixelGreen(left_image,q),r);
4815 SetPixelBlue(image,GetPixelBlue(left_image,q),r);
4816 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4817 GetPixelAlpha(left_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00004818 p+=GetPixelChannels(left_image);
cristy3ed852e2009-09-05 21:47:34 +00004819 q++;
4820 r++;
4821 }
4822 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4823 break;
cristy8b27a6d2010-02-14 03:31:15 +00004824 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004825 {
cristy8b27a6d2010-02-14 03:31:15 +00004826 MagickBooleanType
4827 proceed;
4828
cristybb503372010-05-27 20:51:26 +00004829 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4830 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004831 if (proceed == MagickFalse)
4832 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004833 }
4834 }
cristyda16f162011-02-19 23:52:17 +00004835 if (status == MagickFalse)
4836 {
4837 stereo_image=DestroyImage(stereo_image);
4838 return((Image *) NULL);
4839 }
cristy3ed852e2009-09-05 21:47:34 +00004840 return(stereo_image);
4841}
4842
4843/*
4844%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4845% %
4846% %
4847% %
4848% S w i r l I m a g e %
4849% %
4850% %
4851% %
4852%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4853%
4854% SwirlImage() swirls the pixels about the center of the image, where
4855% degrees indicates the sweep of the arc through which each pixel is moved.
4856% You get a more dramatic effect as the degrees move from 1 to 360.
4857%
4858% The format of the SwirlImage method is:
4859%
4860% Image *SwirlImage(const Image *image,double degrees,
4861% ExceptionInfo *exception)
4862%
4863% A description of each parameter follows:
4864%
4865% o image: the image.
4866%
4867% o degrees: Define the tightness of the swirling effect.
4868%
4869% o exception: return any errors or warnings in this structure.
4870%
4871*/
4872MagickExport Image *SwirlImage(const Image *image,double degrees,
4873 ExceptionInfo *exception)
4874{
4875#define SwirlImageTag "Swirl/Image"
4876
cristyfa112112010-01-04 17:48:07 +00004877 CacheView
4878 *image_view,
4879 *swirl_view;
4880
cristy3ed852e2009-09-05 21:47:34 +00004881 Image
4882 *swirl_image;
4883
cristy3ed852e2009-09-05 21:47:34 +00004884 MagickBooleanType
4885 status;
4886
cristybb503372010-05-27 20:51:26 +00004887 MagickOffsetType
4888 progress;
4889
cristy4c08aed2011-07-01 19:47:50 +00004890 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004891 zero;
4892
4893 MagickRealType
4894 radius;
4895
4896 PointInfo
4897 center,
4898 scale;
4899
cristybb503372010-05-27 20:51:26 +00004900 ssize_t
4901 y;
4902
cristy3ed852e2009-09-05 21:47:34 +00004903 /*
4904 Initialize swirl image attributes.
4905 */
4906 assert(image != (const Image *) NULL);
4907 assert(image->signature == MagickSignature);
4908 if (image->debug != MagickFalse)
4909 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4910 assert(exception != (ExceptionInfo *) NULL);
4911 assert(exception->signature == MagickSignature);
4912 swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4913 if (swirl_image == (Image *) NULL)
4914 return((Image *) NULL);
4915 if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
4916 {
4917 InheritException(exception,&swirl_image->exception);
4918 swirl_image=DestroyImage(swirl_image);
4919 return((Image *) NULL);
4920 }
cristy4c08aed2011-07-01 19:47:50 +00004921 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004922 swirl_image->matte=MagickTrue;
4923 /*
4924 Compute scaling factor.
4925 */
4926 center.x=(double) image->columns/2.0;
4927 center.y=(double) image->rows/2.0;
4928 radius=MagickMax(center.x,center.y);
4929 scale.x=1.0;
4930 scale.y=1.0;
4931 if (image->columns > image->rows)
4932 scale.y=(double) image->columns/(double) image->rows;
4933 else
4934 if (image->columns < image->rows)
4935 scale.x=(double) image->rows/(double) image->columns;
4936 degrees=(double) DegreesToRadians(degrees);
4937 /*
4938 Swirl image.
4939 */
4940 status=MagickTrue;
4941 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00004942 GetPixelInfo(swirl_image,&zero);
cristy3ed852e2009-09-05 21:47:34 +00004943 image_view=AcquireCacheView(image);
4944 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00004945#if defined(MAGICKCORE_OPENMP_SUPPORT)
4946 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004947#endif
cristybb503372010-05-27 20:51:26 +00004948 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004949 {
cristy4c08aed2011-07-01 19:47:50 +00004950 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004951 pixel;
4952
4953 MagickRealType
4954 distance;
4955
4956 PointInfo
4957 delta;
4958
cristybb503372010-05-27 20:51:26 +00004959 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004960 x;
4961
cristy4c08aed2011-07-01 19:47:50 +00004962 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004963 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004964
4965 if (status == MagickFalse)
4966 continue;
4967 q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
4968 exception);
cristy4c08aed2011-07-01 19:47:50 +00004969 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004970 {
4971 status=MagickFalse;
4972 continue;
4973 }
cristy3ed852e2009-09-05 21:47:34 +00004974 delta.y=scale.y*(double) (y-center.y);
4975 pixel=zero;
cristybb503372010-05-27 20:51:26 +00004976 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004977 {
4978 /*
4979 Determine if the pixel is within an ellipse.
4980 */
4981 delta.x=scale.x*(double) (x-center.x);
4982 distance=delta.x*delta.x+delta.y*delta.y;
4983 if (distance < (radius*radius))
4984 {
4985 MagickRealType
4986 cosine,
4987 factor,
4988 sine;
4989
4990 /*
4991 Swirl the pixel.
4992 */
4993 factor=1.0-sqrt((double) distance)/radius;
4994 sine=sin((double) (degrees*factor*factor));
4995 cosine=cos((double) (degrees*factor*factor));
cristy4c08aed2011-07-01 19:47:50 +00004996 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00004997 UndefinedInterpolatePixel,(double) ((cosine*delta.x-sine*delta.y)/
4998 scale.x+center.x),(double) ((sine*delta.x+cosine*delta.y)/scale.y+
cristyd76c51e2011-03-26 00:21:26 +00004999 center.y),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00005000 SetPixelPixelInfo(swirl_image,&pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00005001 }
cristyed231572011-07-14 02:18:59 +00005002 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00005003 }
5004 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5005 status=MagickFalse;
5006 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5007 {
5008 MagickBooleanType
5009 proceed;
5010
cristyb5d5f722009-11-04 03:03:49 +00005011#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005012 #pragma omp critical (MagickCore_SwirlImage)
5013#endif
5014 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5015 if (proceed == MagickFalse)
5016 status=MagickFalse;
5017 }
5018 }
5019 swirl_view=DestroyCacheView(swirl_view);
5020 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005021 if (status == MagickFalse)
5022 swirl_image=DestroyImage(swirl_image);
5023 return(swirl_image);
5024}
5025
5026/*
5027%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5028% %
5029% %
5030% %
5031% T i n t I m a g e %
5032% %
5033% %
5034% %
5035%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5036%
5037% TintImage() applies a color vector to each pixel in the image. The length
5038% of the vector is 0 for black and white and at its maximum for the midtones.
5039% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5040%
5041% The format of the TintImage method is:
5042%
5043% Image *TintImage(const Image *image,const char *opacity,
5044% const PixelPacket tint,ExceptionInfo *exception)
5045%
5046% A description of each parameter follows:
5047%
5048% o image: the image.
5049%
5050% o opacity: A color value used for tinting.
5051%
5052% o tint: A color value used for tinting.
5053%
5054% o exception: return any errors or warnings in this structure.
5055%
5056*/
5057MagickExport Image *TintImage(const Image *image,const char *opacity,
5058 const PixelPacket tint,ExceptionInfo *exception)
5059{
5060#define TintImageTag "Tint/Image"
5061
cristyc4c8d132010-01-07 01:58:38 +00005062 CacheView
5063 *image_view,
5064 *tint_view;
5065
cristy3ed852e2009-09-05 21:47:34 +00005066 GeometryInfo
5067 geometry_info;
5068
5069 Image
5070 *tint_image;
5071
cristy3ed852e2009-09-05 21:47:34 +00005072 MagickBooleanType
5073 status;
5074
cristybb503372010-05-27 20:51:26 +00005075 MagickOffsetType
5076 progress;
cristy3ed852e2009-09-05 21:47:34 +00005077
cristy4c08aed2011-07-01 19:47:50 +00005078 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005079 color_vector,
5080 pixel;
5081
cristybb503372010-05-27 20:51:26 +00005082 MagickStatusType
5083 flags;
5084
5085 ssize_t
5086 y;
5087
cristy3ed852e2009-09-05 21:47:34 +00005088 /*
5089 Allocate tint image.
5090 */
5091 assert(image != (const Image *) NULL);
5092 assert(image->signature == MagickSignature);
5093 if (image->debug != MagickFalse)
5094 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5095 assert(exception != (ExceptionInfo *) NULL);
5096 assert(exception->signature == MagickSignature);
5097 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5098 if (tint_image == (Image *) NULL)
5099 return((Image *) NULL);
5100 if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5101 {
5102 InheritException(exception,&tint_image->exception);
5103 tint_image=DestroyImage(tint_image);
5104 return((Image *) NULL);
5105 }
5106 if (opacity == (const char *) NULL)
5107 return(tint_image);
5108 /*
5109 Determine RGB values of the color.
5110 */
5111 flags=ParseGeometry(opacity,&geometry_info);
5112 pixel.red=geometry_info.rho;
5113 if ((flags & SigmaValue) != 0)
5114 pixel.green=geometry_info.sigma;
5115 else
5116 pixel.green=pixel.red;
5117 if ((flags & XiValue) != 0)
5118 pixel.blue=geometry_info.xi;
5119 else
5120 pixel.blue=pixel.red;
5121 if ((flags & PsiValue) != 0)
cristy4c08aed2011-07-01 19:47:50 +00005122 pixel.alpha=geometry_info.psi;
cristy3ed852e2009-09-05 21:47:34 +00005123 else
cristy4c08aed2011-07-01 19:47:50 +00005124 pixel.alpha=(MagickRealType) OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005125 color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
cristy4c08aed2011-07-01 19:47:50 +00005126 GetPixelPacketIntensity(&tint));
cristy3ed852e2009-09-05 21:47:34 +00005127 color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
cristy4c08aed2011-07-01 19:47:50 +00005128 GetPixelPacketIntensity(&tint));
cristy3ed852e2009-09-05 21:47:34 +00005129 color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
cristy4c08aed2011-07-01 19:47:50 +00005130 GetPixelPacketIntensity(&tint));
cristy3ed852e2009-09-05 21:47:34 +00005131 /*
5132 Tint image.
5133 */
5134 status=MagickTrue;
5135 progress=0;
5136 image_view=AcquireCacheView(image);
5137 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005138#if defined(MAGICKCORE_OPENMP_SUPPORT)
5139 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005140#endif
cristybb503372010-05-27 20:51:26 +00005141 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005142 {
cristy4c08aed2011-07-01 19:47:50 +00005143 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005144 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005145
cristy4c08aed2011-07-01 19:47:50 +00005146 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005147 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005148
cristy6b91acb2011-04-19 12:23:54 +00005149 register ssize_t
5150 x;
5151
cristy3ed852e2009-09-05 21:47:34 +00005152 if (status == MagickFalse)
5153 continue;
5154 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5155 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5156 exception);
cristy4c08aed2011-07-01 19:47:50 +00005157 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005158 {
5159 status=MagickFalse;
5160 continue;
5161 }
cristybb503372010-05-27 20:51:26 +00005162 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005163 {
cristy4c08aed2011-07-01 19:47:50 +00005164 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005165 pixel;
5166
5167 MagickRealType
5168 weight;
5169
cristy4c08aed2011-07-01 19:47:50 +00005170 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5171 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5172 (1.0-(4.0*(weight*weight)));
5173 SetPixelRed(tint_image,ClampToQuantum(pixel.red),q);
5174 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5175 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5176 (1.0-(4.0*(weight*weight)));
5177 SetPixelGreen(tint_image,ClampToQuantum(pixel.green),q);
5178 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5179 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5180 (1.0-(4.0*(weight*weight)));
5181 SetPixelBlue(tint_image,ClampToQuantum(pixel.blue),q);
5182 SetPixelAlpha(tint_image,GetPixelAlpha(image,p),q);
cristyed231572011-07-14 02:18:59 +00005183 p+=GetPixelChannels(image);
5184 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005185 }
5186 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5187 status=MagickFalse;
5188 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5189 {
5190 MagickBooleanType
5191 proceed;
5192
cristyb5d5f722009-11-04 03:03:49 +00005193#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005194 #pragma omp critical (MagickCore_TintImage)
5195#endif
5196 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5197 if (proceed == MagickFalse)
5198 status=MagickFalse;
5199 }
5200 }
5201 tint_view=DestroyCacheView(tint_view);
5202 image_view=DestroyCacheView(image_view);
5203 if (status == MagickFalse)
5204 tint_image=DestroyImage(tint_image);
5205 return(tint_image);
5206}
5207
5208/*
5209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5210% %
5211% %
5212% %
5213% V i g n e t t e I m a g e %
5214% %
5215% %
5216% %
5217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5218%
5219% VignetteImage() softens the edges of the image in vignette style.
5220%
5221% The format of the VignetteImage method is:
5222%
5223% Image *VignetteImage(const Image *image,const double radius,
cristyb3a73b52011-07-26 01:34:43 +00005224% const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005225%
5226% A description of each parameter follows:
5227%
5228% o image: the image.
5229%
5230% o radius: the radius of the pixel neighborhood.
5231%
5232% o sigma: the standard deviation of the Gaussian, in pixels.
5233%
5234% o x, y: Define the x and y ellipse offset.
5235%
5236% o exception: return any errors or warnings in this structure.
5237%
5238*/
5239MagickExport Image *VignetteImage(const Image *image,const double radius,
cristybb503372010-05-27 20:51:26 +00005240 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005241{
5242 char
5243 ellipse[MaxTextExtent];
5244
5245 DrawInfo
5246 *draw_info;
5247
5248 Image
5249 *canvas_image,
5250 *blur_image,
5251 *oval_image,
5252 *vignette_image;
5253
5254 assert(image != (Image *) NULL);
5255 assert(image->signature == MagickSignature);
5256 if (image->debug != MagickFalse)
5257 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5258 assert(exception != (ExceptionInfo *) NULL);
5259 assert(exception->signature == MagickSignature);
5260 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5261 if (canvas_image == (Image *) NULL)
5262 return((Image *) NULL);
5263 if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
5264 {
5265 InheritException(exception,&canvas_image->exception);
5266 canvas_image=DestroyImage(canvas_image);
5267 return((Image *) NULL);
5268 }
5269 canvas_image->matte=MagickTrue;
cristyb3a73b52011-07-26 01:34:43 +00005270 oval_image=CloneImage(canvas_image,canvas_image->columns,
5271 canvas_image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005272 if (oval_image == (Image *) NULL)
5273 {
5274 canvas_image=DestroyImage(canvas_image);
5275 return((Image *) NULL);
5276 }
5277 (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5278 (void) SetImageBackgroundColor(oval_image);
5279 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5280 (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5281 (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
cristyb51dff52011-05-19 16:55:47 +00005282 (void) FormatLocaleString(ellipse,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00005283 "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
cristy8cd5b312010-01-07 01:10:24 +00005284 image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005285 draw_info->primitive=AcquireString(ellipse);
5286 (void) DrawImage(oval_image,draw_info);
5287 draw_info=DestroyDrawInfo(draw_info);
5288 blur_image=BlurImage(oval_image,radius,sigma,exception);
5289 oval_image=DestroyImage(oval_image);
5290 if (blur_image == (Image *) NULL)
5291 {
5292 canvas_image=DestroyImage(canvas_image);
5293 return((Image *) NULL);
5294 }
5295 blur_image->matte=MagickFalse;
5296 (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5297 blur_image=DestroyImage(blur_image);
5298 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5299 canvas_image=DestroyImage(canvas_image);
5300 return(vignette_image);
5301}
5302
5303/*
5304%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5305% %
5306% %
5307% %
5308% W a v e I m a g e %
5309% %
5310% %
5311% %
5312%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5313%
5314% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005315% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005316% by the given parameters.
5317%
5318% The format of the WaveImage method is:
5319%
5320% Image *WaveImage(const Image *image,const double amplitude,
5321% const double wave_length,ExceptionInfo *exception)
5322%
5323% A description of each parameter follows:
5324%
5325% o image: the image.
5326%
5327% o amplitude, wave_length: Define the amplitude and wave length of the
5328% sine wave.
5329%
5330% o exception: return any errors or warnings in this structure.
5331%
5332*/
5333MagickExport Image *WaveImage(const Image *image,const double amplitude,
5334 const double wave_length,ExceptionInfo *exception)
5335{
5336#define WaveImageTag "Wave/Image"
5337
cristyfa112112010-01-04 17:48:07 +00005338 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005339 *image_view,
cristyfa112112010-01-04 17:48:07 +00005340 *wave_view;
5341
cristy3ed852e2009-09-05 21:47:34 +00005342 Image
5343 *wave_image;
5344
cristy3ed852e2009-09-05 21:47:34 +00005345 MagickBooleanType
5346 status;
5347
cristybb503372010-05-27 20:51:26 +00005348 MagickOffsetType
5349 progress;
5350
cristy4c08aed2011-07-01 19:47:50 +00005351 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005352 zero;
5353
5354 MagickRealType
5355 *sine_map;
5356
cristybb503372010-05-27 20:51:26 +00005357 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005358 i;
5359
cristybb503372010-05-27 20:51:26 +00005360 ssize_t
5361 y;
5362
cristy3ed852e2009-09-05 21:47:34 +00005363 /*
5364 Initialize wave image attributes.
5365 */
5366 assert(image != (Image *) NULL);
5367 assert(image->signature == MagickSignature);
5368 if (image->debug != MagickFalse)
5369 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5370 assert(exception != (ExceptionInfo *) NULL);
5371 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005372 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005373 fabs(amplitude)),MagickTrue,exception);
5374 if (wave_image == (Image *) NULL)
5375 return((Image *) NULL);
5376 if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
5377 {
5378 InheritException(exception,&wave_image->exception);
5379 wave_image=DestroyImage(wave_image);
5380 return((Image *) NULL);
5381 }
cristy4c08aed2011-07-01 19:47:50 +00005382 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005383 wave_image->matte=MagickTrue;
5384 /*
5385 Allocate sine map.
5386 */
5387 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5388 sizeof(*sine_map));
5389 if (sine_map == (MagickRealType *) NULL)
5390 {
5391 wave_image=DestroyImage(wave_image);
5392 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5393 }
cristybb503372010-05-27 20:51:26 +00005394 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005395 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5396 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005397 /*
5398 Wave image.
5399 */
5400 status=MagickTrue;
5401 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00005402 GetPixelInfo(wave_image,&zero);
cristyd76c51e2011-03-26 00:21:26 +00005403 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005404 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005405 (void) SetCacheViewVirtualPixelMethod(image_view,
5406 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005407#if defined(MAGICKCORE_OPENMP_SUPPORT)
5408 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005409#endif
cristybb503372010-05-27 20:51:26 +00005410 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005411 {
cristy4c08aed2011-07-01 19:47:50 +00005412 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005413 pixel;
5414
cristy4c08aed2011-07-01 19:47:50 +00005415 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005416 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005417
cristye97bb922011-04-03 01:36:52 +00005418 register ssize_t
5419 x;
5420
cristy3ed852e2009-09-05 21:47:34 +00005421 if (status == MagickFalse)
5422 continue;
5423 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5424 exception);
cristy4c08aed2011-07-01 19:47:50 +00005425 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005426 {
5427 status=MagickFalse;
5428 continue;
5429 }
cristy3ed852e2009-09-05 21:47:34 +00005430 pixel=zero;
cristybb503372010-05-27 20:51:26 +00005431 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005432 {
cristy4c08aed2011-07-01 19:47:50 +00005433 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00005434 UndefinedInterpolatePixel,(double) x,(double) (y-sine_map[x]),&pixel,
5435 exception);
cristy4c08aed2011-07-01 19:47:50 +00005436 SetPixelPixelInfo(wave_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005437 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005438 }
5439 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5440 status=MagickFalse;
5441 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5442 {
5443 MagickBooleanType
5444 proceed;
5445
cristyb5d5f722009-11-04 03:03:49 +00005446#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005447 #pragma omp critical (MagickCore_WaveImage)
5448#endif
5449 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5450 if (proceed == MagickFalse)
5451 status=MagickFalse;
5452 }
5453 }
5454 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005455 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005456 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5457 if (status == MagickFalse)
5458 wave_image=DestroyImage(wave_image);
5459 return(wave_image);
5460}