blob: 9812d2b9975fc88027bc2d54aca62d249107a8bf [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 {
cristy488fa882010-03-01 22:34:24 +00001904 if (LocaleNCompare(expression,"atan2",5) == 0)
1905 {
1906 expression+=5;
1907 break;
1908 }
1909 break;
cristy3ed852e2009-09-05 21:47:34 +00001910 }
cristy488fa882010-03-01 22:34:24 +00001911 case 'J':
1912 case 'j':
1913 {
1914 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1915 (LocaleNCompare(expression,"j1",2) == 0))
1916 {
1917 expression+=2;
1918 break;
1919 }
1920 break;
1921 }
cristy2def9322010-06-18 23:59:37 +00001922 case '#':
1923 {
1924 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1925 expression++;
1926 break;
1927 }
cristy488fa882010-03-01 22:34:24 +00001928 default:
1929 break;
1930 }
cristy3ed852e2009-09-05 21:47:34 +00001931 if ((c == (int) '{') || (c == (int) '['))
1932 level++;
1933 else
1934 if ((c == (int) '}') || (c == (int) ']'))
1935 level--;
1936 if (level == 0)
1937 switch ((unsigned char) *expression)
1938 {
1939 case '~':
1940 case '!':
1941 {
1942 precedence=BitwiseComplementPrecedence;
1943 break;
1944 }
1945 case '^':
cristy6621e252010-08-13 00:42:57 +00001946 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001947 {
1948 precedence=ExponentPrecedence;
1949 break;
1950 }
1951 default:
1952 {
1953 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1954 (strchr(")",c) != (char *) NULL))) &&
1955 (((islower((int) ((char) *expression)) != 0) ||
1956 (strchr("(",(int) *expression) != (char *) NULL)) ||
1957 ((isdigit((int) ((char) c)) == 0) &&
1958 (isdigit((int) ((char) *expression)) != 0))) &&
1959 (strchr("xy",(int) *expression) == (char *) NULL))
1960 precedence=MultiplyPrecedence;
1961 break;
1962 }
1963 case '*':
1964 case '/':
1965 case '%':
1966 {
1967 precedence=MultiplyPrecedence;
1968 break;
1969 }
1970 case '+':
1971 case '-':
1972 {
1973 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1974 (isalpha(c) != 0))
1975 precedence=AdditionPrecedence;
1976 break;
1977 }
1978 case LeftShiftOperator:
1979 case RightShiftOperator:
1980 {
1981 precedence=ShiftPrecedence;
1982 break;
1983 }
1984 case '<':
1985 case LessThanEqualOperator:
1986 case GreaterThanEqualOperator:
1987 case '>':
1988 {
1989 precedence=RelationalPrecedence;
1990 break;
1991 }
1992 case EqualOperator:
1993 case NotEqualOperator:
1994 {
1995 precedence=EquivalencyPrecedence;
1996 break;
1997 }
1998 case '&':
1999 {
2000 precedence=BitwiseAndPrecedence;
2001 break;
2002 }
2003 case '|':
2004 {
2005 precedence=BitwiseOrPrecedence;
2006 break;
2007 }
2008 case LogicalAndOperator:
2009 {
2010 precedence=LogicalAndPrecedence;
2011 break;
2012 }
2013 case LogicalOrOperator:
2014 {
2015 precedence=LogicalOrPrecedence;
2016 break;
2017 }
cristy116af162010-08-13 01:25:47 +00002018 case ExponentialNotation:
2019 {
2020 precedence=ExponentialNotationPrecedence;
2021 break;
2022 }
cristy3ed852e2009-09-05 21:47:34 +00002023 case ':':
2024 case '?':
2025 {
2026 precedence=TernaryPrecedence;
2027 break;
2028 }
2029 case '=':
2030 {
2031 precedence=AssignmentPrecedence;
2032 break;
2033 }
2034 case ',':
2035 {
2036 precedence=CommaPrecedence;
2037 break;
2038 }
2039 case ';':
2040 {
2041 precedence=SeparatorPrecedence;
2042 break;
2043 }
2044 }
2045 if ((precedence == BitwiseComplementPrecedence) ||
2046 (precedence == TernaryPrecedence) ||
2047 (precedence == AssignmentPrecedence))
2048 {
2049 if (precedence > target)
2050 {
2051 /*
2052 Right-to-left associativity.
2053 */
2054 target=precedence;
2055 subexpression=expression;
2056 }
2057 }
2058 else
2059 if (precedence >= target)
2060 {
2061 /*
2062 Left-to-right associativity.
2063 */
2064 target=precedence;
2065 subexpression=expression;
2066 }
2067 if (strchr("(",(int) *expression) != (char *) NULL)
2068 expression=FxSubexpression(expression,exception);
2069 c=(int) (*expression++);
2070 }
2071 return(subexpression);
2072}
2073
2074static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002075 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002076 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002077{
2078 char
2079 *q,
2080 subexpression[MaxTextExtent];
2081
2082 MagickRealType
2083 alpha,
2084 gamma;
2085
2086 register const char
2087 *p;
2088
2089 *beta=0.0;
2090 if (exception->severity != UndefinedException)
2091 return(0.0);
2092 while (isspace((int) *expression) != 0)
2093 expression++;
2094 if (*expression == '\0')
2095 {
2096 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2097 "MissingExpression","`%s'",expression);
2098 return(0.0);
2099 }
cristy66322f02010-05-17 11:40:48 +00002100 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002101 p=FxOperatorPrecedence(expression,exception);
2102 if (p != (const char *) NULL)
2103 {
2104 (void) CopyMagickString(subexpression,expression,(size_t)
2105 (p-expression+1));
2106 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2107 exception);
2108 switch ((unsigned char) *p)
2109 {
2110 case '~':
2111 {
2112 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002113 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002114 return(*beta);
2115 }
2116 case '!':
2117 {
2118 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2119 return(*beta == 0.0 ? 1.0 : 0.0);
2120 }
2121 case '^':
2122 {
2123 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2124 channel,x,y,++p,beta,exception));
2125 return(*beta);
2126 }
2127 case '*':
cristy116af162010-08-13 01:25:47 +00002128 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002129 {
2130 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2131 return(alpha*(*beta));
2132 }
2133 case '/':
2134 {
2135 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2136 if (*beta == 0.0)
2137 {
2138 if (exception->severity == UndefinedException)
2139 (void) ThrowMagickException(exception,GetMagickModule(),
2140 OptionError,"DivideByZero","`%s'",expression);
2141 return(0.0);
2142 }
2143 return(alpha/(*beta));
2144 }
2145 case '%':
2146 {
2147 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2148 *beta=fabs(floor(((double) *beta)+0.5));
2149 if (*beta == 0.0)
2150 {
2151 (void) ThrowMagickException(exception,GetMagickModule(),
2152 OptionError,"DivideByZero","`%s'",expression);
2153 return(0.0);
2154 }
2155 return(fmod((double) alpha,(double) *beta));
2156 }
2157 case '+':
2158 {
2159 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2160 return(alpha+(*beta));
2161 }
2162 case '-':
2163 {
2164 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2165 return(alpha-(*beta));
2166 }
2167 case LeftShiftOperator:
2168 {
2169 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002170 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002171 (gamma+0.5));
2172 return(*beta);
2173 }
2174 case RightShiftOperator:
2175 {
2176 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002177 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002178 (gamma+0.5));
2179 return(*beta);
2180 }
2181 case '<':
2182 {
2183 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2184 return(alpha < *beta ? 1.0 : 0.0);
2185 }
2186 case LessThanEqualOperator:
2187 {
2188 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2189 return(alpha <= *beta ? 1.0 : 0.0);
2190 }
2191 case '>':
2192 {
2193 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2194 return(alpha > *beta ? 1.0 : 0.0);
2195 }
2196 case GreaterThanEqualOperator:
2197 {
2198 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2199 return(alpha >= *beta ? 1.0 : 0.0);
2200 }
2201 case EqualOperator:
2202 {
2203 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2204 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2205 }
2206 case NotEqualOperator:
2207 {
2208 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2209 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2210 }
2211 case '&':
2212 {
2213 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002214 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002215 (gamma+0.5));
2216 return(*beta);
2217 }
2218 case '|':
2219 {
2220 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002221 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002222 (gamma+0.5));
2223 return(*beta);
2224 }
2225 case LogicalAndOperator:
2226 {
2227 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2228 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2229 return(*beta);
2230 }
2231 case LogicalOrOperator:
2232 {
2233 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2234 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2235 return(*beta);
2236 }
2237 case '?':
2238 {
2239 MagickRealType
2240 gamma;
2241
2242 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2243 q=subexpression;
2244 p=StringToken(":",&q);
2245 if (q == (char *) NULL)
2246 {
2247 (void) ThrowMagickException(exception,GetMagickModule(),
2248 OptionError,"UnableToParseExpression","`%s'",subexpression);
2249 return(0.0);
2250 }
2251 if (fabs((double) alpha) > MagickEpsilon)
2252 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2253 else
2254 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2255 return(gamma);
2256 }
2257 case '=':
2258 {
2259 char
2260 numeric[MaxTextExtent];
2261
2262 q=subexpression;
2263 while (isalpha((int) ((unsigned char) *q)) != 0)
2264 q++;
2265 if (*q != '\0')
2266 {
2267 (void) ThrowMagickException(exception,GetMagickModule(),
2268 OptionError,"UnableToParseExpression","`%s'",subexpression);
2269 return(0.0);
2270 }
2271 ClearMagickException(exception);
2272 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002273 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002274 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002275 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2276 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2277 subexpression),ConstantString(numeric));
2278 return(*beta);
2279 }
2280 case ',':
2281 {
2282 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2283 return(alpha);
2284 }
2285 case ';':
2286 {
2287 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2288 return(*beta);
2289 }
2290 default:
2291 {
2292 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2293 exception);
2294 return(gamma);
2295 }
2296 }
2297 }
2298 if (strchr("(",(int) *expression) != (char *) NULL)
2299 {
2300 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2301 subexpression[strlen(subexpression)-1]='\0';
2302 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2303 exception);
2304 return(gamma);
2305 }
cristy8b8a3ae2010-10-23 18:49:46 +00002306 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002307 {
2308 case '+':
2309 {
2310 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2311 exception);
2312 return(1.0*gamma);
2313 }
2314 case '-':
2315 {
2316 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2317 exception);
2318 return(-1.0*gamma);
2319 }
2320 case '~':
2321 {
2322 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2323 exception);
cristybb503372010-05-27 20:51:26 +00002324 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002325 }
2326 case 'A':
2327 case 'a':
2328 {
2329 if (LocaleNCompare(expression,"abs",3) == 0)
2330 {
2331 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2332 exception);
2333 return((MagickRealType) fabs((double) alpha));
2334 }
2335 if (LocaleNCompare(expression,"acos",4) == 0)
2336 {
2337 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2338 exception);
2339 return((MagickRealType) acos((double) alpha));
2340 }
cristy43c22f42010-03-30 12:34:07 +00002341#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002342 if (LocaleNCompare(expression,"airy",4) == 0)
2343 {
2344 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2345 exception);
2346 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002347 return(1.0);
2348 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002349 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002350 }
cristy43c22f42010-03-30 12:34:07 +00002351#endif
cristy3ed852e2009-09-05 21:47:34 +00002352 if (LocaleNCompare(expression,"asin",4) == 0)
2353 {
2354 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2355 exception);
2356 return((MagickRealType) asin((double) alpha));
2357 }
2358 if (LocaleNCompare(expression,"alt",3) == 0)
2359 {
2360 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2361 exception);
cristybb503372010-05-27 20:51:26 +00002362 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002363 }
2364 if (LocaleNCompare(expression,"atan2",5) == 0)
2365 {
2366 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2367 exception);
2368 return((MagickRealType) atan2((double) alpha,(double) *beta));
2369 }
2370 if (LocaleNCompare(expression,"atan",4) == 0)
2371 {
2372 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2373 exception);
2374 return((MagickRealType) atan((double) alpha));
2375 }
2376 if (LocaleCompare(expression,"a") == 0)
2377 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2378 break;
2379 }
2380 case 'B':
2381 case 'b':
2382 {
2383 if (LocaleCompare(expression,"b") == 0)
2384 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2385 break;
2386 }
2387 case 'C':
2388 case 'c':
2389 {
2390 if (LocaleNCompare(expression,"ceil",4) == 0)
2391 {
2392 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2393 exception);
2394 return((MagickRealType) ceil((double) alpha));
2395 }
2396 if (LocaleNCompare(expression,"cosh",4) == 0)
2397 {
2398 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2399 exception);
2400 return((MagickRealType) cosh((double) alpha));
2401 }
2402 if (LocaleNCompare(expression,"cos",3) == 0)
2403 {
2404 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2405 exception);
2406 return((MagickRealType) cos((double) alpha));
2407 }
2408 if (LocaleCompare(expression,"c") == 0)
2409 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2410 break;
2411 }
2412 case 'D':
2413 case 'd':
2414 {
2415 if (LocaleNCompare(expression,"debug",5) == 0)
2416 {
2417 const char
2418 *type;
2419
2420 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2421 exception);
2422 if (fx_info->images->colorspace == CMYKColorspace)
2423 switch (channel)
2424 {
cristy0568ffc2011-07-25 16:54:14 +00002425 case CyanPixelChannel: type="cyan"; break;
2426 case MagentaPixelChannel: type="magenta"; break;
2427 case YellowPixelChannel: type="yellow"; break;
2428 case AlphaPixelChannel: type="opacity"; break;
2429 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002430 default: type="unknown"; break;
2431 }
2432 else
2433 switch (channel)
2434 {
cristy0568ffc2011-07-25 16:54:14 +00002435 case RedPixelChannel: type="red"; break;
2436 case GreenPixelChannel: type="green"; break;
2437 case BluePixelChannel: type="blue"; break;
2438 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002439 default: type="unknown"; break;
2440 }
2441 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2442 if (strlen(subexpression) > 1)
2443 subexpression[strlen(subexpression)-1]='\0';
2444 if (fx_info->file != (FILE *) NULL)
cristy1e604812011-05-19 18:07:50 +00002445 (void) FormatLocaleFile(fx_info->file,
2446 "%s[%.20g,%.20g].%s: %s=%.*g\n",fx_info->images->filename,
2447 (double) x,(double) y,type,subexpression,GetMagickPrecision(),
2448 (double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002449 return(0.0);
2450 }
2451 break;
2452 }
2453 case 'E':
2454 case 'e':
2455 {
2456 if (LocaleCompare(expression,"epsilon") == 0)
2457 return((MagickRealType) MagickEpsilon);
2458 if (LocaleNCompare(expression,"exp",3) == 0)
2459 {
2460 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2461 exception);
2462 return((MagickRealType) exp((double) alpha));
2463 }
2464 if (LocaleCompare(expression,"e") == 0)
2465 return((MagickRealType) 2.7182818284590452354);
2466 break;
2467 }
2468 case 'F':
2469 case 'f':
2470 {
2471 if (LocaleNCompare(expression,"floor",5) == 0)
2472 {
2473 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2474 exception);
2475 return((MagickRealType) floor((double) alpha));
2476 }
2477 break;
2478 }
2479 case 'G':
2480 case 'g':
2481 {
2482 if (LocaleCompare(expression,"g") == 0)
2483 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2484 break;
2485 }
2486 case 'H':
2487 case 'h':
2488 {
2489 if (LocaleCompare(expression,"h") == 0)
2490 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2491 if (LocaleCompare(expression,"hue") == 0)
2492 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2493 if (LocaleNCompare(expression,"hypot",5) == 0)
2494 {
2495 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2496 exception);
2497 return((MagickRealType) hypot((double) alpha,(double) *beta));
2498 }
2499 break;
2500 }
2501 case 'K':
2502 case 'k':
2503 {
2504 if (LocaleCompare(expression,"k") == 0)
2505 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2506 break;
2507 }
2508 case 'I':
2509 case 'i':
2510 {
2511 if (LocaleCompare(expression,"intensity") == 0)
2512 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2513 if (LocaleNCompare(expression,"int",3) == 0)
2514 {
2515 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2516 exception);
cristy16788e42010-08-13 13:44:26 +00002517 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002518 }
2519 if (LocaleCompare(expression,"i") == 0)
2520 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2521 break;
2522 }
2523 case 'J':
2524 case 'j':
2525 {
2526 if (LocaleCompare(expression,"j") == 0)
2527 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002528#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002529 if (LocaleNCompare(expression,"j0",2) == 0)
2530 {
2531 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2532 exception);
2533 return((MagickRealType) j0((double) alpha));
2534 }
cristy161b9262010-03-20 19:34:32 +00002535#endif
2536#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002537 if (LocaleNCompare(expression,"j1",2) == 0)
2538 {
2539 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2540 exception);
2541 return((MagickRealType) j1((double) alpha));
2542 }
cristy161b9262010-03-20 19:34:32 +00002543#endif
cristyaa018fa2010-04-08 23:03:54 +00002544#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002545 if (LocaleNCompare(expression,"jinc",4) == 0)
2546 {
2547 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2548 exception);
cristy0946a822010-03-12 17:14:58 +00002549 if (alpha == 0.0)
2550 return(1.0);
cristy69928f92010-03-12 13:27:09 +00002551 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/
cristyfce2f7b2010-03-12 00:29:49 +00002552 (MagickPI*alpha));
2553 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002554 }
cristyaa018fa2010-04-08 23:03:54 +00002555#endif
cristy3ed852e2009-09-05 21:47:34 +00002556 break;
2557 }
2558 case 'L':
2559 case 'l':
2560 {
2561 if (LocaleNCompare(expression,"ln",2) == 0)
2562 {
2563 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2564 exception);
2565 return((MagickRealType) log((double) alpha));
2566 }
cristyc8ed5322010-08-31 12:07:59 +00002567 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002568 {
cristyc8ed5322010-08-31 12:07:59 +00002569 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002570 exception);
2571 return((MagickRealType) log10((double) alpha))/log10(2.0);
2572 }
2573 if (LocaleNCompare(expression,"log",3) == 0)
2574 {
2575 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2576 exception);
2577 return((MagickRealType) log10((double) alpha));
2578 }
2579 if (LocaleCompare(expression,"lightness") == 0)
2580 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2581 break;
2582 }
2583 case 'M':
2584 case 'm':
2585 {
2586 if (LocaleCompare(expression,"MaxRGB") == 0)
2587 return((MagickRealType) QuantumRange);
2588 if (LocaleNCompare(expression,"maxima",6) == 0)
2589 break;
2590 if (LocaleNCompare(expression,"max",3) == 0)
2591 return(FxMax(fx_info,channel,x,y,expression+3,exception));
2592 if (LocaleNCompare(expression,"minima",6) == 0)
2593 break;
2594 if (LocaleNCompare(expression,"min",3) == 0)
2595 return(FxMin(fx_info,channel,x,y,expression+3,exception));
2596 if (LocaleNCompare(expression,"mod",3) == 0)
2597 {
2598 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2599 exception);
2600 return((MagickRealType) fmod((double) alpha,(double) *beta));
2601 }
2602 if (LocaleCompare(expression,"m") == 0)
2603 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2604 break;
2605 }
2606 case 'N':
2607 case 'n':
2608 {
2609 if (LocaleCompare(expression,"n") == 0)
2610 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2611 break;
2612 }
2613 case 'O':
2614 case 'o':
2615 {
2616 if (LocaleCompare(expression,"Opaque") == 0)
2617 return(1.0);
2618 if (LocaleCompare(expression,"o") == 0)
2619 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2620 break;
2621 }
2622 case 'P':
2623 case 'p':
2624 {
2625 if (LocaleCompare(expression,"pi") == 0)
2626 return((MagickRealType) MagickPI);
2627 if (LocaleNCompare(expression,"pow",3) == 0)
2628 {
2629 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2630 exception);
2631 return((MagickRealType) pow((double) alpha,(double) *beta));
2632 }
2633 if (LocaleCompare(expression,"p") == 0)
2634 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2635 break;
2636 }
2637 case 'Q':
2638 case 'q':
2639 {
2640 if (LocaleCompare(expression,"QuantumRange") == 0)
2641 return((MagickRealType) QuantumRange);
2642 if (LocaleCompare(expression,"QuantumScale") == 0)
2643 return((MagickRealType) QuantumScale);
2644 break;
2645 }
2646 case 'R':
2647 case 'r':
2648 {
2649 if (LocaleNCompare(expression,"rand",4) == 0)
2650 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2651 if (LocaleNCompare(expression,"round",5) == 0)
2652 {
2653 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2654 exception);
cristy16788e42010-08-13 13:44:26 +00002655 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002656 }
2657 if (LocaleCompare(expression,"r") == 0)
2658 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2659 break;
2660 }
2661 case 'S':
2662 case 's':
2663 {
2664 if (LocaleCompare(expression,"saturation") == 0)
2665 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2666 if (LocaleNCompare(expression,"sign",4) == 0)
2667 {
2668 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2669 exception);
2670 return(alpha < 0.0 ? -1.0 : 1.0);
2671 }
cristya6a09e72010-03-02 14:51:02 +00002672 if (LocaleNCompare(expression,"sinc",4) == 0)
2673 {
2674 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2675 exception);
2676 if (alpha == 0)
2677 return(1.0);
2678 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2679 (MagickPI*alpha));
2680 return(gamma);
2681 }
cristy3ed852e2009-09-05 21:47:34 +00002682 if (LocaleNCompare(expression,"sinh",4) == 0)
2683 {
2684 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2685 exception);
2686 return((MagickRealType) sinh((double) alpha));
2687 }
2688 if (LocaleNCompare(expression,"sin",3) == 0)
2689 {
2690 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2691 exception);
2692 return((MagickRealType) sin((double) alpha));
2693 }
2694 if (LocaleNCompare(expression,"sqrt",4) == 0)
2695 {
2696 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2697 exception);
2698 return((MagickRealType) sqrt((double) alpha));
2699 }
2700 if (LocaleCompare(expression,"s") == 0)
2701 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2702 break;
2703 }
2704 case 'T':
2705 case 't':
2706 {
2707 if (LocaleNCompare(expression,"tanh",4) == 0)
2708 {
2709 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2710 exception);
2711 return((MagickRealType) tanh((double) alpha));
2712 }
2713 if (LocaleNCompare(expression,"tan",3) == 0)
2714 {
2715 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2716 exception);
2717 return((MagickRealType) tan((double) alpha));
2718 }
2719 if (LocaleCompare(expression,"Transparent") == 0)
2720 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002721 if (LocaleNCompare(expression,"trunc",5) == 0)
2722 {
2723 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2724 exception);
2725 if (alpha >= 0.0)
2726 return((MagickRealType) floor((double) alpha));
2727 return((MagickRealType) ceil((double) alpha));
2728 }
cristy3ed852e2009-09-05 21:47:34 +00002729 if (LocaleCompare(expression,"t") == 0)
2730 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2731 break;
2732 }
2733 case 'U':
2734 case 'u':
2735 {
2736 if (LocaleCompare(expression,"u") == 0)
2737 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2738 break;
2739 }
2740 case 'V':
2741 case 'v':
2742 {
2743 if (LocaleCompare(expression,"v") == 0)
2744 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2745 break;
2746 }
2747 case 'W':
2748 case 'w':
2749 {
2750 if (LocaleCompare(expression,"w") == 0)
2751 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2752 break;
2753 }
2754 case 'Y':
2755 case 'y':
2756 {
2757 if (LocaleCompare(expression,"y") == 0)
2758 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2759 break;
2760 }
2761 case 'Z':
2762 case 'z':
2763 {
2764 if (LocaleCompare(expression,"z") == 0)
2765 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2766 break;
2767 }
2768 default:
2769 break;
2770 }
2771 q=(char *) expression;
cristyc1acd842011-05-19 23:05:47 +00002772 alpha=InterpretLocaleValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002773 if (q == expression)
2774 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2775 return(alpha);
2776}
2777
2778MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2779 MagickRealType *alpha,ExceptionInfo *exception)
2780{
2781 MagickBooleanType
2782 status;
2783
cristy30539862010-11-10 14:14:14 +00002784 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002785 return(status);
2786}
2787
2788MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2789 MagickRealType *alpha,ExceptionInfo *exception)
2790{
2791 FILE
2792 *file;
2793
2794 MagickBooleanType
2795 status;
2796
2797 file=fx_info->file;
2798 fx_info->file=(FILE *) NULL;
2799 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2800 fx_info->file=file;
2801 return(status);
2802}
2803
2804MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002805 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002806 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002807{
2808 MagickRealType
2809 beta;
2810
2811 beta=0.0;
2812 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2813 exception);
2814 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2815}
2816
2817/*
2818%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2819% %
2820% %
2821% %
2822% F x I m a g e %
2823% %
2824% %
2825% %
2826%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2827%
2828% FxImage() applies a mathematical expression to the specified image.
2829%
2830% The format of the FxImage method is:
2831%
2832% Image *FxImage(const Image *image,const char *expression,
2833% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002834%
2835% A description of each parameter follows:
2836%
2837% o image: the image.
2838%
cristy3ed852e2009-09-05 21:47:34 +00002839% o expression: A mathematical expression.
2840%
2841% o exception: return any errors or warnings in this structure.
2842%
2843*/
2844
2845static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2846{
cristybb503372010-05-27 20:51:26 +00002847 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002848 i;
2849
2850 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002851 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002852 if (fx_info[i] != (FxInfo *) NULL)
2853 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002854 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002855 return(fx_info);
2856}
2857
2858static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2859 ExceptionInfo *exception)
2860{
2861 char
2862 *fx_expression;
2863
2864 FxInfo
2865 **fx_info;
2866
2867 MagickRealType
2868 alpha;
2869
cristybb503372010-05-27 20:51:26 +00002870 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002871 i;
2872
cristybb503372010-05-27 20:51:26 +00002873 size_t
cristy3ed852e2009-09-05 21:47:34 +00002874 number_threads;
2875
2876 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002877 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002878 if (fx_info == (FxInfo **) NULL)
2879 return((FxInfo **) NULL);
2880 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2881 if (*expression != '@')
2882 fx_expression=ConstantString(expression);
2883 else
2884 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00002885 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002886 {
2887 fx_info[i]=AcquireFxInfo(image,fx_expression);
2888 if (fx_info[i] == (FxInfo *) NULL)
2889 return(DestroyFxThreadSet(fx_info));
2890 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2891 }
2892 fx_expression=DestroyString(fx_expression);
2893 return(fx_info);
2894}
2895
2896MagickExport Image *FxImage(const Image *image,const char *expression,
2897 ExceptionInfo *exception)
2898{
cristy3ed852e2009-09-05 21:47:34 +00002899#define FxImageTag "Fx/Image"
2900
cristyfa112112010-01-04 17:48:07 +00002901 CacheView
cristy79cedc72011-07-25 00:41:15 +00002902 *fx_view,
2903 *image_view;
cristyfa112112010-01-04 17:48:07 +00002904
cristy3ed852e2009-09-05 21:47:34 +00002905 FxInfo
cristyfa112112010-01-04 17:48:07 +00002906 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00002907
2908 Image
2909 *fx_image;
2910
cristy3ed852e2009-09-05 21:47:34 +00002911 MagickBooleanType
2912 status;
2913
cristybb503372010-05-27 20:51:26 +00002914 MagickOffsetType
2915 progress;
2916
cristy3ed852e2009-09-05 21:47:34 +00002917 MagickRealType
2918 alpha;
2919
cristybb503372010-05-27 20:51:26 +00002920 ssize_t
2921 y;
2922
cristy3ed852e2009-09-05 21:47:34 +00002923 assert(image != (Image *) NULL);
2924 assert(image->signature == MagickSignature);
2925 if (image->debug != MagickFalse)
2926 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00002927 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00002928 if (fx_image == (Image *) NULL)
2929 return((Image *) NULL);
2930 if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
2931 {
2932 InheritException(exception,&fx_image->exception);
2933 fx_image=DestroyImage(fx_image);
2934 return((Image *) NULL);
2935 }
2936 fx_info=AcquireFxThreadSet(image,expression,exception);
2937 if (fx_info == (FxInfo **) NULL)
2938 {
2939 fx_image=DestroyImage(fx_image);
2940 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2941 }
2942 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
2943 if (status == MagickFalse)
2944 {
2945 fx_image=DestroyImage(fx_image);
2946 fx_info=DestroyFxThreadSet(fx_info);
2947 return((Image *) NULL);
2948 }
2949 /*
2950 Fx image.
2951 */
2952 status=MagickTrue;
2953 progress=0;
cristy79cedc72011-07-25 00:41:15 +00002954 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00002955 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00002956#if defined(MAGICKCORE_OPENMP_SUPPORT)
2957 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002958#endif
cristybb503372010-05-27 20:51:26 +00002959 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002960 {
cristy5c9e6f22010-09-17 17:31:01 +00002961 const int
2962 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00002963
cristy79cedc72011-07-25 00:41:15 +00002964 register const Quantum
2965 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002966
cristy4c08aed2011-07-01 19:47:50 +00002967 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002968 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002969
cristy79cedc72011-07-25 00:41:15 +00002970 register ssize_t
2971 x;
2972
cristy3ed852e2009-09-05 21:47:34 +00002973 if (status == MagickFalse)
2974 continue;
cristy79cedc72011-07-25 00:41:15 +00002975 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00002976 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00002977 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002978 {
2979 status=MagickFalse;
2980 continue;
2981 }
cristybb503372010-05-27 20:51:26 +00002982 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002983 {
cristy79cedc72011-07-25 00:41:15 +00002984 register ssize_t
2985 i;
2986
2987 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2988 {
2989 MagickRealType
2990 alpha;
2991
2992 PixelChannel
2993 channel;
2994
2995 PixelTrait
2996 fx_traits,
2997 traits;
2998
2999 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3000 if (traits == UndefinedPixelTrait)
3001 continue;
3002 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3003 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
3004 if (fx_traits == UndefinedPixelTrait)
3005 continue;
3006 if ((fx_traits & CopyPixelTrait) != 0)
3007 {
3008 q[channel]=p[i];
3009 continue;
3010 }
3011 alpha=0.0;
cristy0568ffc2011-07-25 16:54:14 +00003012 (void) FxEvaluateChannelExpression(fx_info[id],(PixelChannel) i,x,y,
3013 &alpha,exception);
cristyb3a73b52011-07-26 01:34:43 +00003014 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003015 }
3016 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003017 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003018 }
3019 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3020 status=MagickFalse;
3021 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3022 {
3023 MagickBooleanType
3024 proceed;
3025
cristyb5d5f722009-11-04 03:03:49 +00003026#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy490408a2011-07-07 14:42:05 +00003027 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003028#endif
3029 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3030 if (proceed == MagickFalse)
3031 status=MagickFalse;
3032 }
3033 }
cristy3ed852e2009-09-05 21:47:34 +00003034 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003035 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003036 fx_info=DestroyFxThreadSet(fx_info);
3037 if (status == MagickFalse)
3038 fx_image=DestroyImage(fx_image);
3039 return(fx_image);
3040}
3041
3042/*
3043%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3044% %
3045% %
3046% %
3047% I m p l o d e I m a g e %
3048% %
3049% %
3050% %
3051%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3052%
3053% ImplodeImage() creates a new image that is a copy of an existing
3054% one with the image pixels "implode" by the specified percentage. It
3055% allocates the memory necessary for the new Image structure and returns a
3056% pointer to the new image.
3057%
3058% The format of the ImplodeImage method is:
3059%
3060% Image *ImplodeImage(const Image *image,const double amount,
3061% ExceptionInfo *exception)
3062%
3063% A description of each parameter follows:
3064%
3065% o implode_image: Method ImplodeImage returns a pointer to the image
3066% after it is implode. A null image is returned if there is a memory
3067% shortage.
3068%
3069% o image: the image.
3070%
3071% o amount: Define the extent of the implosion.
3072%
3073% o exception: return any errors or warnings in this structure.
3074%
3075*/
3076MagickExport Image *ImplodeImage(const Image *image,const double amount,
3077 ExceptionInfo *exception)
3078{
3079#define ImplodeImageTag "Implode/Image"
3080
cristyfa112112010-01-04 17:48:07 +00003081 CacheView
3082 *image_view,
3083 *implode_view;
3084
cristy3ed852e2009-09-05 21:47:34 +00003085 Image
3086 *implode_image;
3087
cristy3ed852e2009-09-05 21:47:34 +00003088 MagickBooleanType
3089 status;
3090
cristybb503372010-05-27 20:51:26 +00003091 MagickOffsetType
3092 progress;
3093
cristy4c08aed2011-07-01 19:47:50 +00003094 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003095 zero;
3096
3097 MagickRealType
3098 radius;
3099
3100 PointInfo
3101 center,
3102 scale;
3103
cristybb503372010-05-27 20:51:26 +00003104 ssize_t
3105 y;
3106
cristy3ed852e2009-09-05 21:47:34 +00003107 /*
3108 Initialize implode image attributes.
3109 */
3110 assert(image != (Image *) NULL);
3111 assert(image->signature == MagickSignature);
3112 if (image->debug != MagickFalse)
3113 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3114 assert(exception != (ExceptionInfo *) NULL);
3115 assert(exception->signature == MagickSignature);
3116 implode_image=CloneImage(image,0,0,MagickTrue,exception);
3117 if (implode_image == (Image *) NULL)
3118 return((Image *) NULL);
3119 if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
3120 {
3121 InheritException(exception,&implode_image->exception);
3122 implode_image=DestroyImage(implode_image);
3123 return((Image *) NULL);
3124 }
cristy4c08aed2011-07-01 19:47:50 +00003125 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003126 implode_image->matte=MagickTrue;
3127 /*
3128 Compute scaling factor.
3129 */
3130 scale.x=1.0;
3131 scale.y=1.0;
3132 center.x=0.5*image->columns;
3133 center.y=0.5*image->rows;
3134 radius=center.x;
3135 if (image->columns > image->rows)
3136 scale.y=(double) image->columns/(double) image->rows;
3137 else
3138 if (image->columns < image->rows)
3139 {
3140 scale.x=(double) image->rows/(double) image->columns;
3141 radius=center.y;
3142 }
3143 /*
3144 Implode image.
3145 */
3146 status=MagickTrue;
3147 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003148 GetPixelInfo(implode_image,&zero);
cristy3ed852e2009-09-05 21:47:34 +00003149 image_view=AcquireCacheView(image);
3150 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003151#if defined(MAGICKCORE_OPENMP_SUPPORT)
3152 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003153#endif
cristybb503372010-05-27 20:51:26 +00003154 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003155 {
cristy4c08aed2011-07-01 19:47:50 +00003156 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003157 pixel;
3158
3159 MagickRealType
3160 distance;
3161
3162 PointInfo
3163 delta;
3164
cristybb503372010-05-27 20:51:26 +00003165 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003166 x;
3167
cristy4c08aed2011-07-01 19:47:50 +00003168 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003169 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003170
3171 if (status == MagickFalse)
3172 continue;
3173 q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3174 exception);
cristy4c08aed2011-07-01 19:47:50 +00003175 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003176 {
3177 status=MagickFalse;
3178 continue;
3179 }
cristy3ed852e2009-09-05 21:47:34 +00003180 delta.y=scale.y*(double) (y-center.y);
3181 pixel=zero;
cristybb503372010-05-27 20:51:26 +00003182 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003183 {
3184 /*
3185 Determine if the pixel is within an ellipse.
3186 */
3187 delta.x=scale.x*(double) (x-center.x);
3188 distance=delta.x*delta.x+delta.y*delta.y;
3189 if (distance < (radius*radius))
3190 {
3191 double
3192 factor;
3193
3194 /*
3195 Implode the pixel.
3196 */
3197 factor=1.0;
3198 if (distance > 0.0)
3199 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3200 radius/2)),-amount);
cristy4c08aed2011-07-01 19:47:50 +00003201 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00003202 UndefinedInterpolatePixel,(double) (factor*delta.x/scale.x+
3203 center.x),(double) (factor*delta.y/scale.y+center.y),&pixel,
3204 exception);
cristy4c08aed2011-07-01 19:47:50 +00003205 SetPixelPixelInfo(implode_image,&pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00003206 }
cristyed231572011-07-14 02:18:59 +00003207 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003208 }
3209 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3210 status=MagickFalse;
3211 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3212 {
3213 MagickBooleanType
3214 proceed;
3215
cristyb5d5f722009-11-04 03:03:49 +00003216#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003217 #pragma omp critical (MagickCore_ImplodeImage)
3218#endif
3219 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3220 if (proceed == MagickFalse)
3221 status=MagickFalse;
3222 }
3223 }
3224 implode_view=DestroyCacheView(implode_view);
3225 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003226 if (status == MagickFalse)
3227 implode_image=DestroyImage(implode_image);
3228 return(implode_image);
3229}
3230
3231/*
3232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3233% %
3234% %
3235% %
3236% M o r p h I m a g e s %
3237% %
3238% %
3239% %
3240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3241%
3242% The MorphImages() method requires a minimum of two images. The first
3243% image is transformed into the second by a number of intervening images
3244% as specified by frames.
3245%
3246% The format of the MorphImage method is:
3247%
cristybb503372010-05-27 20:51:26 +00003248% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003249% ExceptionInfo *exception)
3250%
3251% A description of each parameter follows:
3252%
3253% o image: the image.
3254%
3255% o number_frames: Define the number of in-between image to generate.
3256% The more in-between frames, the smoother the morph.
3257%
3258% o exception: return any errors or warnings in this structure.
3259%
3260*/
3261MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003262 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003263{
3264#define MorphImageTag "Morph/Image"
3265
3266 Image
3267 *morph_image,
3268 *morph_images;
3269
cristy9d314ff2011-03-09 01:30:28 +00003270 MagickBooleanType
3271 status;
cristy3ed852e2009-09-05 21:47:34 +00003272
3273 MagickOffsetType
3274 scene;
3275
3276 MagickRealType
3277 alpha,
3278 beta;
3279
3280 register const Image
3281 *next;
3282
cristybb503372010-05-27 20:51:26 +00003283 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003284 i;
3285
cristy9d314ff2011-03-09 01:30:28 +00003286 ssize_t
3287 y;
cristy3ed852e2009-09-05 21:47:34 +00003288
3289 /*
3290 Clone first frame in sequence.
3291 */
3292 assert(image != (Image *) NULL);
3293 assert(image->signature == MagickSignature);
3294 if (image->debug != MagickFalse)
3295 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3296 assert(exception != (ExceptionInfo *) NULL);
3297 assert(exception->signature == MagickSignature);
3298 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3299 if (morph_images == (Image *) NULL)
3300 return((Image *) NULL);
3301 if (GetNextImageInList(image) == (Image *) NULL)
3302 {
3303 /*
3304 Morph single image.
3305 */
cristybb503372010-05-27 20:51:26 +00003306 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003307 {
3308 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3309 if (morph_image == (Image *) NULL)
3310 {
3311 morph_images=DestroyImageList(morph_images);
3312 return((Image *) NULL);
3313 }
3314 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003315 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003316 {
cristy8b27a6d2010-02-14 03:31:15 +00003317 MagickBooleanType
3318 proceed;
3319
cristybb503372010-05-27 20:51:26 +00003320 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3321 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003322 if (proceed == MagickFalse)
3323 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003324 }
3325 }
3326 return(GetFirstImageInList(morph_images));
3327 }
3328 /*
3329 Morph image sequence.
3330 */
3331 status=MagickTrue;
3332 scene=0;
3333 next=image;
3334 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3335 {
cristybb503372010-05-27 20:51:26 +00003336 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003337 {
3338 CacheView
3339 *image_view,
3340 *morph_view;
3341
3342 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3343 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003344 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristybb503372010-05-27 20:51:26 +00003345 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
cristy15b98cd2010-09-12 19:42:50 +00003346 next->rows+beta*GetNextImageInList(next)->rows+0.5),
3347 next->filter,next->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003348 if (morph_image == (Image *) NULL)
3349 {
3350 morph_images=DestroyImageList(morph_images);
3351 return((Image *) NULL);
3352 }
3353 if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
3354 {
3355 InheritException(exception,&morph_image->exception);
3356 morph_image=DestroyImage(morph_image);
3357 return((Image *) NULL);
3358 }
3359 AppendImageToList(&morph_images,morph_image);
3360 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003361 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3362 morph_images->rows,GetNextImageInList(next)->filter,
3363 GetNextImageInList(next)->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003364 if (morph_image == (Image *) NULL)
3365 {
3366 morph_images=DestroyImageList(morph_images);
3367 return((Image *) NULL);
3368 }
3369 image_view=AcquireCacheView(morph_image);
3370 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003371#if defined(MAGICKCORE_OPENMP_SUPPORT)
3372 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003373#endif
cristybb503372010-05-27 20:51:26 +00003374 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003375 {
3376 MagickBooleanType
3377 sync;
3378
cristy4c08aed2011-07-01 19:47:50 +00003379 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003380 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003381
cristybb503372010-05-27 20:51:26 +00003382 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003383 x;
3384
cristy4c08aed2011-07-01 19:47:50 +00003385 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003386 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003387
3388 if (status == MagickFalse)
3389 continue;
3390 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3391 exception);
3392 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3393 exception);
cristy4c08aed2011-07-01 19:47:50 +00003394 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003395 {
3396 status=MagickFalse;
3397 continue;
3398 }
cristybb503372010-05-27 20:51:26 +00003399 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003400 {
cristy4c08aed2011-07-01 19:47:50 +00003401 SetPixelRed(morph_images,ClampToQuantum(alpha*
3402 GetPixelRed(morph_images,q)+beta*GetPixelRed(morph_image,p)),q);
3403 SetPixelGreen(morph_images,ClampToQuantum(alpha*
3404 GetPixelGreen(morph_images,q)+beta*GetPixelGreen(morph_image,p)),q);
3405 SetPixelBlue(morph_images,ClampToQuantum(alpha*
3406 GetPixelBlue(morph_images,q)+beta*GetPixelBlue(morph_image,p)),q);
3407 SetPixelAlpha(morph_images,ClampToQuantum(alpha*
3408 GetPixelAlpha(morph_images,q)+beta*GetPixelAlpha(morph_image,p)),q);
3409 if ((morph_image->colorspace == CMYKColorspace) &&
3410 (morph_images->colorspace == CMYKColorspace))
3411 SetPixelBlack(morph_images,ClampToQuantum(alpha*
3412 GetPixelBlack(morph_images,q)+beta*GetPixelBlack(morph_image,p)),
3413 q);
cristyed231572011-07-14 02:18:59 +00003414 p+=GetPixelChannels(morph_image);
3415 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003416 }
3417 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3418 if (sync == MagickFalse)
3419 status=MagickFalse;
3420 }
3421 morph_view=DestroyCacheView(morph_view);
3422 image_view=DestroyCacheView(image_view);
3423 morph_image=DestroyImage(morph_image);
3424 }
cristybb503372010-05-27 20:51:26 +00003425 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003426 break;
3427 /*
3428 Clone last frame in sequence.
3429 */
3430 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3431 if (morph_image == (Image *) NULL)
3432 {
3433 morph_images=DestroyImageList(morph_images);
3434 return((Image *) NULL);
3435 }
3436 AppendImageToList(&morph_images,morph_image);
3437 morph_images=GetLastImageInList(morph_images);
3438 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3439 {
3440 MagickBooleanType
3441 proceed;
3442
cristyb5d5f722009-11-04 03:03:49 +00003443#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003444 #pragma omp critical (MagickCore_MorphImages)
3445#endif
3446 proceed=SetImageProgress(image,MorphImageTag,scene,
3447 GetImageListLength(image));
3448 if (proceed == MagickFalse)
3449 status=MagickFalse;
3450 }
3451 scene++;
3452 }
3453 if (GetNextImageInList(next) != (Image *) NULL)
3454 {
3455 morph_images=DestroyImageList(morph_images);
3456 return((Image *) NULL);
3457 }
3458 return(GetFirstImageInList(morph_images));
3459}
3460
3461/*
3462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3463% %
3464% %
3465% %
3466% P l a s m a I m a g e %
3467% %
3468% %
3469% %
3470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3471%
3472% PlasmaImage() initializes an image with plasma fractal values. The image
3473% must be initialized with a base color and the random number generator
3474% seeded before this method is called.
3475%
3476% The format of the PlasmaImage method is:
3477%
3478% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristybb503372010-05-27 20:51:26 +00003479% size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003480%
3481% A description of each parameter follows:
3482%
3483% o image: the image.
3484%
3485% o segment: Define the region to apply plasma fractals values.
3486%
glennrp7dae1ca2010-09-16 12:17:35 +00003487% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003488%
3489% o depth: Limit the plasma recursion depth.
3490%
3491*/
3492
3493static inline Quantum PlasmaPixel(RandomInfo *random_info,
3494 const MagickRealType pixel,const MagickRealType noise)
3495{
3496 Quantum
3497 plasma;
3498
cristyce70c172010-01-07 17:15:30 +00003499 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003500 noise/2.0);
3501 return(plasma);
3502}
3503
3504MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
cristyc5c6f662010-09-22 14:23:02 +00003505 CacheView *image_view,RandomInfo *random_info,const SegmentInfo *segment,
3506 size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003507{
3508 ExceptionInfo
3509 *exception;
3510
cristy3ed852e2009-09-05 21:47:34 +00003511 MagickRealType
3512 plasma;
3513
3514 PixelPacket
3515 u,
3516 v;
3517
cristy9d314ff2011-03-09 01:30:28 +00003518 ssize_t
3519 x,
3520 x_mid,
3521 y,
3522 y_mid;
3523
cristy3ed852e2009-09-05 21:47:34 +00003524 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3525 return(MagickTrue);
3526 if (depth != 0)
3527 {
3528 SegmentInfo
3529 local_info;
3530
3531 /*
3532 Divide the area into quadrants and recurse.
3533 */
3534 depth--;
3535 attenuate++;
cristybb503372010-05-27 20:51:26 +00003536 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3537 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003538 local_info=(*segment);
3539 local_info.x2=(double) x_mid;
3540 local_info.y2=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003541 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3542 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003543 local_info=(*segment);
3544 local_info.y1=(double) y_mid;
3545 local_info.x2=(double) x_mid;
cristyc5c6f662010-09-22 14:23:02 +00003546 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3547 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003548 local_info=(*segment);
3549 local_info.x1=(double) x_mid;
3550 local_info.y2=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003551 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3552 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003553 local_info=(*segment);
3554 local_info.x1=(double) x_mid;
3555 local_info.y1=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003556 return(PlasmaImageProxy(image,image_view,random_info,&local_info,
3557 attenuate,depth));
cristy3ed852e2009-09-05 21:47:34 +00003558 }
cristybb503372010-05-27 20:51:26 +00003559 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3560 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003561 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3562 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3563 return(MagickFalse);
3564 /*
3565 Average pixels and apply plasma.
3566 */
3567 exception=(&image->exception);
3568 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3569 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3570 {
cristy4c08aed2011-07-01 19:47:50 +00003571 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003572 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003573
3574 /*
3575 Left pixel.
3576 */
cristybb503372010-05-27 20:51:26 +00003577 x=(ssize_t) ceil(segment->x1-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003578 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3579 ceil(segment->y1-0.5),&u,exception);
3580 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3581 ceil(segment->y2-0.5),&v,exception);
3582 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003583 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003584 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003585 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3586 (u.red+v.red)/2.0,plasma),q);
3587 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3588 (u.green+v.green)/2.0,plasma),q);
3589 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3590 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003591 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003592 if (segment->x1 != segment->x2)
3593 {
3594 /*
3595 Right pixel.
3596 */
cristybb503372010-05-27 20:51:26 +00003597 x=(ssize_t) ceil(segment->x2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003598 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3599 ceil(segment->y1-0.5),&u,exception);
3600 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3601 ceil(segment->y2-0.5),&v,exception);
3602 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003603 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003604 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003605 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3606 (u.red+v.red)/2.0,plasma),q);
3607 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3608 (u.green+v.green)/2.0,plasma),q);
3609 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3610 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003611 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003612 }
3613 }
3614 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3615 {
3616 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3617 {
cristy4c08aed2011-07-01 19:47:50 +00003618 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003619 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003620
3621 /*
3622 Bottom pixel.
3623 */
cristybb503372010-05-27 20:51:26 +00003624 y=(ssize_t) ceil(segment->y2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003625 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3626 ceil(segment->x1-0.5),y,&u,exception);
3627 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3628 ceil(segment->x2-0.5),y,&v,exception);
3629 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003630 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003631 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003632 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3633 (u.red+v.red)/2.0,plasma),q);
3634 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3635 (u.green+v.green)/2.0,plasma),q);
3636 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3637 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003638 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003639 }
3640 if (segment->y1 != segment->y2)
3641 {
cristy4c08aed2011-07-01 19:47:50 +00003642 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003643 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003644
3645 /*
3646 Top pixel.
3647 */
cristybb503372010-05-27 20:51:26 +00003648 y=(ssize_t) ceil(segment->y1-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003649 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3650 ceil(segment->x1-0.5),y,&u,exception);
3651 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3652 ceil(segment->x2-0.5),y,&v,exception);
3653 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003654 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003655 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003656 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3657 (u.red+v.red)/2.0,plasma),q);
3658 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3659 (u.green+v.green)/2.0,plasma),q);
3660 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3661 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003662 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003663 }
3664 }
3665 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3666 {
cristy4c08aed2011-07-01 19:47:50 +00003667 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003668 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003669
3670 /*
3671 Middle pixel.
3672 */
cristybb503372010-05-27 20:51:26 +00003673 x=(ssize_t) ceil(segment->x1-0.5);
3674 y=(ssize_t) ceil(segment->y1-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003675 (void) GetOneVirtualPixel(image,x,y,&u,exception);
cristybb503372010-05-27 20:51:26 +00003676 x=(ssize_t) ceil(segment->x2-0.5);
3677 y=(ssize_t) ceil(segment->y2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003678 (void) GetOneCacheViewVirtualPixel(image_view,x,y,&v,exception);
3679 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003680 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003681 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003682 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3683 (u.red+v.red)/2.0,plasma),q);
3684 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3685 (u.green+v.green)/2.0,plasma),q);
3686 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3687 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003688 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003689 }
3690 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3691 return(MagickTrue);
3692 return(MagickFalse);
3693}
3694
3695MagickExport MagickBooleanType PlasmaImage(Image *image,
cristybb503372010-05-27 20:51:26 +00003696 const SegmentInfo *segment,size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003697{
cristyc5c6f662010-09-22 14:23:02 +00003698 CacheView
3699 *image_view;
3700
cristy3ed852e2009-09-05 21:47:34 +00003701 MagickBooleanType
3702 status;
3703
3704 RandomInfo
3705 *random_info;
3706
3707 if (image->debug != MagickFalse)
3708 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3709 assert(image != (Image *) NULL);
3710 assert(image->signature == MagickSignature);
3711 if (image->debug != MagickFalse)
3712 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristyc5c6f662010-09-22 14:23:02 +00003713 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
3714 return(MagickFalse);
3715 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003716 random_info=AcquireRandomInfo();
cristyc5c6f662010-09-22 14:23:02 +00003717 status=PlasmaImageProxy(image,image_view,random_info,segment,attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003718 random_info=DestroyRandomInfo(random_info);
cristyc5c6f662010-09-22 14:23:02 +00003719 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003720 return(status);
3721}
3722
3723/*
3724%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3725% %
3726% %
3727% %
3728% P o l a r o i d I m a g e %
3729% %
3730% %
3731% %
3732%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3733%
3734% PolaroidImage() simulates a Polaroid picture.
3735%
3736% The format of the AnnotateImage method is:
3737%
3738% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3739% const double angle,ExceptionInfo exception)
3740%
3741% A description of each parameter follows:
3742%
3743% o image: the image.
3744%
3745% o draw_info: the draw info.
3746%
cristycee97112010-05-28 00:44:52 +00003747% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003748%
3749% o exception: return any errors or warnings in this structure.
3750%
3751*/
3752MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3753 const double angle,ExceptionInfo *exception)
3754{
3755 const char
3756 *value;
3757
cristy3ed852e2009-09-05 21:47:34 +00003758 Image
3759 *bend_image,
3760 *caption_image,
3761 *flop_image,
3762 *picture_image,
3763 *polaroid_image,
3764 *rotate_image,
3765 *trim_image;
3766
cristybb503372010-05-27 20:51:26 +00003767 size_t
cristy3ed852e2009-09-05 21:47:34 +00003768 height;
3769
cristy9d314ff2011-03-09 01:30:28 +00003770 ssize_t
3771 quantum;
3772
cristy3ed852e2009-09-05 21:47:34 +00003773 /*
3774 Simulate a Polaroid picture.
3775 */
3776 assert(image != (Image *) NULL);
3777 assert(image->signature == MagickSignature);
3778 if (image->debug != MagickFalse)
3779 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3780 assert(exception != (ExceptionInfo *) NULL);
3781 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003782 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003783 image->rows)/25.0,10.0);
3784 height=image->rows+2*quantum;
3785 caption_image=(Image *) NULL;
3786 value=GetImageProperty(image,"Caption");
3787 if (value != (const char *) NULL)
3788 {
3789 char
3790 *caption,
3791 geometry[MaxTextExtent];
3792
3793 DrawInfo
3794 *annotate_info;
3795
cristy3ed852e2009-09-05 21:47:34 +00003796 MagickBooleanType
3797 status;
3798
cristy9d314ff2011-03-09 01:30:28 +00003799 ssize_t
3800 count;
3801
cristy3ed852e2009-09-05 21:47:34 +00003802 TypeMetric
3803 metrics;
3804
3805 /*
3806 Generate caption image.
3807 */
3808 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3809 if (caption_image == (Image *) NULL)
3810 return((Image *) NULL);
3811 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
3812 caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
3813 value);
3814 (void) CloneString(&annotate_info->text,caption);
cristy6b1d05e2010-09-22 19:17:27 +00003815 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
3816 &caption);
cristybb503372010-05-27 20:51:26 +00003817 status=SetImageExtent(caption_image,image->columns,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00003818 ((count+1)*(metrics.ascent-metrics.descent)+0.5));
3819 if (status == MagickFalse)
3820 caption_image=DestroyImage(caption_image);
3821 else
3822 {
3823 caption_image->background_color=image->border_color;
3824 (void) SetImageBackgroundColor(caption_image);
3825 (void) CloneString(&annotate_info->text,caption);
cristyb51dff52011-05-19 16:55:47 +00003826 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00003827 metrics.ascent);
3828 if (annotate_info->gravity == UndefinedGravity)
3829 (void) CloneString(&annotate_info->geometry,AcquireString(
3830 geometry));
3831 (void) AnnotateImage(caption_image,annotate_info);
3832 height+=caption_image->rows;
3833 }
3834 annotate_info=DestroyDrawInfo(annotate_info);
3835 caption=DestroyString(caption);
3836 }
3837 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3838 exception);
3839 if (picture_image == (Image *) NULL)
3840 {
3841 if (caption_image != (Image *) NULL)
3842 caption_image=DestroyImage(caption_image);
3843 return((Image *) NULL);
3844 }
3845 picture_image->background_color=image->border_color;
3846 (void) SetImageBackgroundColor(picture_image);
3847 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
3848 if (caption_image != (Image *) NULL)
3849 {
3850 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
cristybb503372010-05-27 20:51:26 +00003851 quantum,(ssize_t) (image->rows+3*quantum/2));
cristy3ed852e2009-09-05 21:47:34 +00003852 caption_image=DestroyImage(caption_image);
3853 }
3854 (void) QueryColorDatabase("none",&picture_image->background_color,exception);
3855 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
3856 rotate_image=RotateImage(picture_image,90.0,exception);
3857 picture_image=DestroyImage(picture_image);
3858 if (rotate_image == (Image *) NULL)
3859 return((Image *) NULL);
3860 picture_image=rotate_image;
3861 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
3862 picture_image->columns,exception);
3863 picture_image=DestroyImage(picture_image);
3864 if (bend_image == (Image *) NULL)
3865 return((Image *) NULL);
3866 InheritException(&bend_image->exception,exception);
3867 picture_image=bend_image;
3868 rotate_image=RotateImage(picture_image,-90.0,exception);
3869 picture_image=DestroyImage(picture_image);
3870 if (rotate_image == (Image *) NULL)
3871 return((Image *) NULL);
3872 picture_image=rotate_image;
3873 picture_image->background_color=image->background_color;
3874 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
3875 exception);
3876 if (polaroid_image == (Image *) NULL)
3877 {
3878 picture_image=DestroyImage(picture_image);
3879 return(picture_image);
3880 }
3881 flop_image=FlopImage(polaroid_image,exception);
3882 polaroid_image=DestroyImage(polaroid_image);
3883 if (flop_image == (Image *) NULL)
3884 {
3885 picture_image=DestroyImage(picture_image);
3886 return(picture_image);
3887 }
3888 polaroid_image=flop_image;
3889 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
cristybb503372010-05-27 20:51:26 +00003890 (ssize_t) (-0.01*picture_image->columns/2.0),0L);
cristy3ed852e2009-09-05 21:47:34 +00003891 picture_image=DestroyImage(picture_image);
3892 (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
3893 rotate_image=RotateImage(polaroid_image,angle,exception);
3894 polaroid_image=DestroyImage(polaroid_image);
3895 if (rotate_image == (Image *) NULL)
3896 return((Image *) NULL);
3897 polaroid_image=rotate_image;
3898 trim_image=TrimImage(polaroid_image,exception);
3899 polaroid_image=DestroyImage(polaroid_image);
3900 if (trim_image == (Image *) NULL)
3901 return((Image *) NULL);
3902 polaroid_image=trim_image;
3903 return(polaroid_image);
3904}
3905
3906/*
3907%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3908% %
3909% %
3910% %
cristy3ed852e2009-09-05 21:47:34 +00003911% S e p i a T o n e I m a g e %
3912% %
3913% %
3914% %
3915%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3916%
3917% MagickSepiaToneImage() applies a special effect to the image, similar to the
3918% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
3919% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
3920% threshold of 80% is a good starting point for a reasonable tone.
3921%
3922% The format of the SepiaToneImage method is:
3923%
3924% Image *SepiaToneImage(const Image *image,const double threshold,
3925% ExceptionInfo *exception)
3926%
3927% A description of each parameter follows:
3928%
3929% o image: the image.
3930%
3931% o threshold: the tone threshold.
3932%
3933% o exception: return any errors or warnings in this structure.
3934%
3935*/
3936MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
3937 ExceptionInfo *exception)
3938{
3939#define SepiaToneImageTag "SepiaTone/Image"
3940
cristyc4c8d132010-01-07 01:58:38 +00003941 CacheView
3942 *image_view,
3943 *sepia_view;
3944
cristy3ed852e2009-09-05 21:47:34 +00003945 Image
3946 *sepia_image;
3947
cristy3ed852e2009-09-05 21:47:34 +00003948 MagickBooleanType
3949 status;
3950
cristybb503372010-05-27 20:51:26 +00003951 MagickOffsetType
3952 progress;
3953
3954 ssize_t
3955 y;
3956
cristy3ed852e2009-09-05 21:47:34 +00003957 /*
3958 Initialize sepia-toned image attributes.
3959 */
3960 assert(image != (const Image *) NULL);
3961 assert(image->signature == MagickSignature);
3962 if (image->debug != MagickFalse)
3963 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3964 assert(exception != (ExceptionInfo *) NULL);
3965 assert(exception->signature == MagickSignature);
3966 sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3967 if (sepia_image == (Image *) NULL)
3968 return((Image *) NULL);
3969 if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
3970 {
3971 InheritException(exception,&sepia_image->exception);
3972 sepia_image=DestroyImage(sepia_image);
3973 return((Image *) NULL);
3974 }
3975 /*
3976 Tone each row of the image.
3977 */
3978 status=MagickTrue;
3979 progress=0;
3980 image_view=AcquireCacheView(image);
3981 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00003982#if defined(MAGICKCORE_OPENMP_SUPPORT)
3983 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003984#endif
cristybb503372010-05-27 20:51:26 +00003985 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003986 {
cristy4c08aed2011-07-01 19:47:50 +00003987 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003988 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003989
cristybb503372010-05-27 20:51:26 +00003990 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003991 x;
3992
cristy4c08aed2011-07-01 19:47:50 +00003993 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003994 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003995
3996 if (status == MagickFalse)
3997 continue;
3998 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3999 q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4000 exception);
cristy4c08aed2011-07-01 19:47:50 +00004001 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004002 {
4003 status=MagickFalse;
4004 continue;
4005 }
cristybb503372010-05-27 20:51:26 +00004006 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004007 {
4008 MagickRealType
4009 intensity,
4010 tone;
4011
cristy4c08aed2011-07-01 19:47:50 +00004012 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004013 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4014 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004015 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004016 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4017 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004018 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004019 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004020 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004021 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004022 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4023 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4024 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4025 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004026 p+=GetPixelChannels(image);
4027 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004028 }
4029 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4030 status=MagickFalse;
4031 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4032 {
4033 MagickBooleanType
4034 proceed;
4035
cristyb5d5f722009-11-04 03:03:49 +00004036#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004037 #pragma omp critical (MagickCore_SepiaToneImage)
4038#endif
4039 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4040 image->rows);
4041 if (proceed == MagickFalse)
4042 status=MagickFalse;
4043 }
4044 }
4045 sepia_view=DestroyCacheView(sepia_view);
4046 image_view=DestroyCacheView(image_view);
4047 (void) NormalizeImage(sepia_image);
4048 (void) ContrastImage(sepia_image,MagickTrue);
4049 if (status == MagickFalse)
4050 sepia_image=DestroyImage(sepia_image);
4051 return(sepia_image);
4052}
4053
4054/*
4055%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4056% %
4057% %
4058% %
4059% S h a d o w I m a g e %
4060% %
4061% %
4062% %
4063%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4064%
4065% ShadowImage() simulates a shadow from the specified image and returns it.
4066%
4067% The format of the ShadowImage method is:
4068%
4069% Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004070% const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004071% ExceptionInfo *exception)
4072%
4073% A description of each parameter follows:
4074%
4075% o image: the image.
4076%
4077% o opacity: percentage transparency.
4078%
4079% o sigma: the standard deviation of the Gaussian, in pixels.
4080%
4081% o x_offset: the shadow x-offset.
4082%
4083% o y_offset: the shadow y-offset.
4084%
4085% o exception: return any errors or warnings in this structure.
4086%
4087*/
4088MagickExport Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004089 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004090 ExceptionInfo *exception)
4091{
4092#define ShadowImageTag "Shadow/Image"
4093
cristyc4c8d132010-01-07 01:58:38 +00004094 CacheView
cristy4c08aed2011-07-01 19:47:50 +00004095 *border_view;
cristyc4c8d132010-01-07 01:58:38 +00004096
cristy3ed852e2009-09-05 21:47:34 +00004097 Image
4098 *border_image,
4099 *clone_image,
4100 *shadow_image;
4101
cristy3ed852e2009-09-05 21:47:34 +00004102 MagickBooleanType
4103 status;
4104
cristybb503372010-05-27 20:51:26 +00004105 MagickOffsetType
4106 progress;
4107
cristy3ed852e2009-09-05 21:47:34 +00004108 RectangleInfo
4109 border_info;
4110
cristybb503372010-05-27 20:51:26 +00004111 ssize_t
4112 y;
4113
cristy3ed852e2009-09-05 21:47:34 +00004114 assert(image != (Image *) NULL);
4115 assert(image->signature == MagickSignature);
4116 if (image->debug != MagickFalse)
4117 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4118 assert(exception != (ExceptionInfo *) NULL);
4119 assert(exception->signature == MagickSignature);
4120 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4121 if (clone_image == (Image *) NULL)
4122 return((Image *) NULL);
4123 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4124 clone_image->compose=OverCompositeOp;
cristybb503372010-05-27 20:51:26 +00004125 border_info.width=(size_t) floor(2.0*sigma+0.5);
4126 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004127 border_info.x=0;
4128 border_info.y=0;
4129 (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4130 border_image=BorderImage(clone_image,&border_info,exception);
4131 clone_image=DestroyImage(clone_image);
4132 if (border_image == (Image *) NULL)
4133 return((Image *) NULL);
4134 if (border_image->matte == MagickFalse)
4135 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4136 /*
4137 Shadow image.
4138 */
4139 status=MagickTrue;
4140 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00004141 border_view=AcquireCacheView(border_image);
cristyb5d5f722009-11-04 03:03:49 +00004142#if defined(MAGICKCORE_OPENMP_SUPPORT)
4143 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004144#endif
cristybb503372010-05-27 20:51:26 +00004145 for (y=0; y < (ssize_t) border_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004146 {
cristybb503372010-05-27 20:51:26 +00004147 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004148 x;
4149
cristy4c08aed2011-07-01 19:47:50 +00004150 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004151 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004152
4153 if (status == MagickFalse)
4154 continue;
cristy4c08aed2011-07-01 19:47:50 +00004155 q=GetCacheViewAuthenticPixels(border_view,0,y,border_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004156 exception);
cristy4c08aed2011-07-01 19:47:50 +00004157 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004158 {
4159 status=MagickFalse;
4160 continue;
4161 }
cristybb503372010-05-27 20:51:26 +00004162 for (x=0; x < (ssize_t) border_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004163 {
cristy4c08aed2011-07-01 19:47:50 +00004164 SetPixelRed(border_image,border_image->background_color.red,q);
4165 SetPixelGreen(border_image,border_image->background_color.green,q);
4166 SetPixelBlue(border_image,border_image->background_color.blue,q);
cristy3ed852e2009-09-05 21:47:34 +00004167 if (border_image->matte == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004168 SetPixelAlpha(border_image,border_image->background_color.alpha,q);
cristy3ed852e2009-09-05 21:47:34 +00004169 else
cristy4c08aed2011-07-01 19:47:50 +00004170 SetPixelAlpha(border_image,ClampToQuantum((MagickRealType)
4171 (GetPixelAlpha(border_image,q)*opacity/100.0)),q);
cristyed231572011-07-14 02:18:59 +00004172 q+=GetPixelChannels(border_image);
cristy3ed852e2009-09-05 21:47:34 +00004173 }
cristy4c08aed2011-07-01 19:47:50 +00004174 if (SyncCacheViewAuthenticPixels(border_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004175 status=MagickFalse;
cristy4c08aed2011-07-01 19:47:50 +00004176 if (border_image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004177 {
4178 MagickBooleanType
4179 proceed;
4180
cristyb5d5f722009-11-04 03:03:49 +00004181#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004182 #pragma omp critical (MagickCore_ShadowImage)
4183#endif
cristy4c08aed2011-07-01 19:47:50 +00004184 proceed=SetImageProgress(border_image,ShadowImageTag,progress++,
cristy3ed852e2009-09-05 21:47:34 +00004185 border_image->rows);
4186 if (proceed == MagickFalse)
4187 status=MagickFalse;
4188 }
4189 }
cristy4c08aed2011-07-01 19:47:50 +00004190 border_view=DestroyCacheView(border_view);
cristyed231572011-07-14 02:18:59 +00004191 PushPixelChannelMap(border_image,AlphaChannel);
cristyf4ad9df2011-07-08 16:49:03 +00004192 shadow_image=BlurImage(border_image,0.0,sigma,exception);
cristyed231572011-07-14 02:18:59 +00004193 PopPixelChannelMap(border_image);
cristy3ed852e2009-09-05 21:47:34 +00004194 border_image=DestroyImage(border_image);
4195 if (shadow_image == (Image *) NULL)
4196 return((Image *) NULL);
4197 if (shadow_image->page.width == 0)
4198 shadow_image->page.width=shadow_image->columns;
4199 if (shadow_image->page.height == 0)
4200 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004201 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4202 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4203 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4204 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004205 return(shadow_image);
4206}
4207
4208/*
4209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4210% %
4211% %
4212% %
4213% S k e t c h I m a g e %
4214% %
4215% %
4216% %
4217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4218%
4219% SketchImage() simulates a pencil sketch. We convolve the image with a
4220% Gaussian operator of the given radius and standard deviation (sigma). For
4221% reasonable results, radius should be larger than sigma. Use a radius of 0
4222% and SketchImage() selects a suitable radius for you. Angle gives the angle
4223% of the sketch.
4224%
4225% The format of the SketchImage method is:
4226%
4227% Image *SketchImage(const Image *image,const double radius,
4228% const double sigma,const double angle,ExceptionInfo *exception)
4229%
4230% A description of each parameter follows:
4231%
4232% o image: the image.
4233%
4234% o radius: the radius of the Gaussian, in pixels, not counting
4235% the center pixel.
4236%
4237% o sigma: the standard deviation of the Gaussian, in pixels.
4238%
cristycee97112010-05-28 00:44:52 +00004239% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00004240%
4241% o exception: return any errors or warnings in this structure.
4242%
4243*/
4244MagickExport Image *SketchImage(const Image *image,const double radius,
4245 const double sigma,const double angle,ExceptionInfo *exception)
4246{
cristyfa112112010-01-04 17:48:07 +00004247 CacheView
4248 *random_view;
4249
cristy3ed852e2009-09-05 21:47:34 +00004250 Image
4251 *blend_image,
4252 *blur_image,
4253 *dodge_image,
4254 *random_image,
4255 *sketch_image;
4256
cristy3ed852e2009-09-05 21:47:34 +00004257 MagickBooleanType
4258 status;
4259
cristy4c08aed2011-07-01 19:47:50 +00004260 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004261 zero;
4262
4263 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004264 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004265
cristy9d314ff2011-03-09 01:30:28 +00004266 ssize_t
4267 y;
4268
cristy3ed852e2009-09-05 21:47:34 +00004269 /*
4270 Sketch image.
4271 */
4272 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4273 MagickTrue,exception);
4274 if (random_image == (Image *) NULL)
4275 return((Image *) NULL);
4276 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00004277 GetPixelInfo(random_image,&zero);
cristy1b784432009-12-19 02:20:40 +00004278 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004279 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004280#if defined(MAGICKCORE_OPENMP_SUPPORT)
4281 #pragma omp parallel for schedule(dynamic,4) shared(status)
4282#endif
cristybb503372010-05-27 20:51:26 +00004283 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004284 {
cristy5c9e6f22010-09-17 17:31:01 +00004285 const int
4286 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004287
cristy4c08aed2011-07-01 19:47:50 +00004288 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004289 pixel;
4290
cristybb503372010-05-27 20:51:26 +00004291 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004292 x;
4293
cristy4c08aed2011-07-01 19:47:50 +00004294 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004295 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004296
cristy1b784432009-12-19 02:20:40 +00004297 if (status == MagickFalse)
4298 continue;
cristy3ed852e2009-09-05 21:47:34 +00004299 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4300 exception);
cristy4c08aed2011-07-01 19:47:50 +00004301 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004302 {
4303 status=MagickFalse;
4304 continue;
4305 }
cristy3ed852e2009-09-05 21:47:34 +00004306 pixel=zero;
cristybb503372010-05-27 20:51:26 +00004307 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004308 {
4309 pixel.red=(MagickRealType) (QuantumRange*
cristy1b784432009-12-19 02:20:40 +00004310 GetPseudoRandomValue(random_info[id]));
cristy3ed852e2009-09-05 21:47:34 +00004311 pixel.green=pixel.red;
4312 pixel.blue=pixel.red;
4313 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004314 pixel.black=pixel.red;
4315 SetPixelPixelInfo(random_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00004316 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004317 }
4318 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4319 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004320 }
4321 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004322 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004323 if (status == MagickFalse)
4324 {
4325 random_image=DestroyImage(random_image);
4326 return(random_image);
4327 }
4328 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4329 random_image=DestroyImage(random_image);
4330 if (blur_image == (Image *) NULL)
4331 return((Image *) NULL);
4332 dodge_image=EdgeImage(blur_image,radius,exception);
4333 blur_image=DestroyImage(blur_image);
4334 if (dodge_image == (Image *) NULL)
4335 return((Image *) NULL);
4336 (void) NormalizeImage(dodge_image);
cristyb3e7c6c2011-07-24 01:43:55 +00004337 (void) NegateImage(dodge_image,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00004338 (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4339 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4340 if (sketch_image == (Image *) NULL)
4341 {
4342 dodge_image=DestroyImage(dodge_image);
4343 return((Image *) NULL);
4344 }
4345 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4346 dodge_image=DestroyImage(dodge_image);
4347 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4348 if (blend_image == (Image *) NULL)
4349 {
4350 sketch_image=DestroyImage(sketch_image);
4351 return((Image *) NULL);
4352 }
4353 (void) SetImageArtifact(blend_image,"compose:args","20x80");
4354 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4355 blend_image=DestroyImage(blend_image);
4356 return(sketch_image);
4357}
4358
4359/*
4360%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4361% %
4362% %
4363% %
4364% S o l a r i z e I m a g e %
4365% %
4366% %
4367% %
4368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4369%
4370% SolarizeImage() applies a special effect to the image, similar to the effect
4371% achieved in a photo darkroom by selectively exposing areas of photo
4372% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4373% measure of the extent of the solarization.
4374%
4375% The format of the SolarizeImage method is:
4376%
4377% MagickBooleanType SolarizeImage(Image *image,const double threshold)
4378%
4379% A description of each parameter follows:
4380%
4381% o image: the image.
4382%
4383% o threshold: Define the extent of the solarization.
4384%
4385*/
4386MagickExport MagickBooleanType SolarizeImage(Image *image,
4387 const double threshold)
4388{
4389#define SolarizeImageTag "Solarize/Image"
4390
cristyc4c8d132010-01-07 01:58:38 +00004391 CacheView
4392 *image_view;
4393
cristy3ed852e2009-09-05 21:47:34 +00004394 ExceptionInfo
4395 *exception;
4396
cristy3ed852e2009-09-05 21:47:34 +00004397 MagickBooleanType
4398 status;
4399
cristybb503372010-05-27 20:51:26 +00004400 MagickOffsetType
4401 progress;
4402
4403 ssize_t
4404 y;
4405
cristy3ed852e2009-09-05 21:47:34 +00004406 assert(image != (Image *) NULL);
4407 assert(image->signature == MagickSignature);
4408 if (image->debug != MagickFalse)
4409 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4410 if (image->storage_class == PseudoClass)
4411 {
cristybb503372010-05-27 20:51:26 +00004412 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004413 i;
4414
4415 /*
4416 Solarize colormap.
4417 */
cristybb503372010-05-27 20:51:26 +00004418 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004419 {
4420 if ((MagickRealType) image->colormap[i].red > threshold)
4421 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4422 if ((MagickRealType) image->colormap[i].green > threshold)
4423 image->colormap[i].green=(Quantum) QuantumRange-
4424 image->colormap[i].green;
4425 if ((MagickRealType) image->colormap[i].blue > threshold)
4426 image->colormap[i].blue=(Quantum) QuantumRange-
4427 image->colormap[i].blue;
4428 }
4429 }
4430 /*
4431 Solarize image.
4432 */
4433 status=MagickTrue;
4434 progress=0;
4435 exception=(&image->exception);
4436 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004437#if defined(MAGICKCORE_OPENMP_SUPPORT)
4438 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004439#endif
cristybb503372010-05-27 20:51:26 +00004440 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004441 {
cristybb503372010-05-27 20:51:26 +00004442 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004443 x;
4444
cristy4c08aed2011-07-01 19:47:50 +00004445 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004446 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004447
4448 if (status == MagickFalse)
4449 continue;
4450 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4451 exception);
cristy4c08aed2011-07-01 19:47:50 +00004452 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004453 {
4454 status=MagickFalse;
4455 continue;
4456 }
cristybb503372010-05-27 20:51:26 +00004457 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004458 {
cristy4c08aed2011-07-01 19:47:50 +00004459 if ((MagickRealType) GetPixelRed(image,q) > threshold)
4460 SetPixelRed(image,QuantumRange-GetPixelRed(image,q),q);
4461 if ((MagickRealType) GetPixelGreen(image,q) > threshold)
4462 SetPixelGreen(image,QuantumRange-GetPixelGreen(image,q),q);
4463 if ((MagickRealType) GetPixelBlue(image,q) > threshold)
4464 SetPixelBlue(image,QuantumRange-GetPixelBlue(image,q),q);
cristyed231572011-07-14 02:18:59 +00004465 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004466 }
4467 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4468 status=MagickFalse;
4469 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4470 {
4471 MagickBooleanType
4472 proceed;
4473
cristyb5d5f722009-11-04 03:03:49 +00004474#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004475 #pragma omp critical (MagickCore_SolarizeImage)
4476#endif
4477 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4478 if (proceed == MagickFalse)
4479 status=MagickFalse;
4480 }
4481 }
4482 image_view=DestroyCacheView(image_view);
4483 return(status);
4484}
4485
4486/*
4487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4488% %
4489% %
4490% %
4491% S t e g a n o I m a g e %
4492% %
4493% %
4494% %
4495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4496%
4497% SteganoImage() hides a digital watermark within the image. Recover
4498% the hidden watermark later to prove that the authenticity of an image.
4499% Offset defines the start position within the image to hide the watermark.
4500%
4501% The format of the SteganoImage method is:
4502%
4503% Image *SteganoImage(const Image *image,Image *watermark,
4504% ExceptionInfo *exception)
4505%
4506% A description of each parameter follows:
4507%
4508% o image: the image.
4509%
4510% o watermark: the watermark image.
4511%
4512% o exception: return any errors or warnings in this structure.
4513%
4514*/
4515MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4516 ExceptionInfo *exception)
4517{
cristye1bf8ad2010-09-19 17:07:03 +00004518#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004519#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004520 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004521#define SteganoImageTag "Stegano/Image"
4522
cristyb0d3bb92010-09-22 14:37:58 +00004523 CacheView
4524 *stegano_view,
4525 *watermark_view;
4526
cristy3ed852e2009-09-05 21:47:34 +00004527 Image
4528 *stegano_image;
4529
4530 int
4531 c;
4532
cristy3ed852e2009-09-05 21:47:34 +00004533 MagickBooleanType
4534 status;
4535
4536 PixelPacket
4537 pixel;
4538
cristy4c08aed2011-07-01 19:47:50 +00004539 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004540 *q;
4541
cristye1bf8ad2010-09-19 17:07:03 +00004542 register ssize_t
4543 x;
4544
cristybb503372010-05-27 20:51:26 +00004545 size_t
cristyeaedf062010-05-29 22:36:02 +00004546 depth,
4547 one;
cristy3ed852e2009-09-05 21:47:34 +00004548
cristye1bf8ad2010-09-19 17:07:03 +00004549 ssize_t
4550 i,
4551 j,
4552 k,
4553 y;
4554
cristy3ed852e2009-09-05 21:47:34 +00004555 /*
4556 Initialize steganographic image attributes.
4557 */
4558 assert(image != (const Image *) NULL);
4559 assert(image->signature == MagickSignature);
4560 if (image->debug != MagickFalse)
4561 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4562 assert(watermark != (const Image *) NULL);
4563 assert(watermark->signature == MagickSignature);
4564 assert(exception != (ExceptionInfo *) NULL);
4565 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004566 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004567 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4568 if (stegano_image == (Image *) NULL)
4569 return((Image *) NULL);
4570 if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
4571 {
4572 InheritException(exception,&stegano_image->exception);
4573 stegano_image=DestroyImage(stegano_image);
4574 return((Image *) NULL);
4575 }
4576 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4577 /*
4578 Hide watermark in low-order bits of image.
4579 */
4580 c=0;
4581 i=0;
4582 j=0;
4583 depth=stegano_image->depth;
4584 k=image->offset;
cristyda16f162011-02-19 23:52:17 +00004585 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004586 watermark_view=AcquireCacheView(watermark);
4587 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004588 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004589 {
cristybb503372010-05-27 20:51:26 +00004590 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004591 {
cristybb503372010-05-27 20:51:26 +00004592 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004593 {
cristyb0d3bb92010-09-22 14:37:58 +00004594 (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,&pixel,exception);
cristybb503372010-05-27 20:51:26 +00004595 if ((k/(ssize_t) stegano_image->columns) >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004596 break;
cristyb0d3bb92010-09-22 14:37:58 +00004597 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4598 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4599 exception);
cristy4c08aed2011-07-01 19:47:50 +00004600 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004601 break;
4602 switch (c)
4603 {
4604 case 0:
4605 {
cristy4c08aed2011-07-01 19:47:50 +00004606 SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
4607 GetPixelPacketIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004608 break;
4609 }
4610 case 1:
4611 {
cristy4c08aed2011-07-01 19:47:50 +00004612 SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
4613 GetPixelPacketIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004614 break;
4615 }
4616 case 2:
4617 {
cristy4c08aed2011-07-01 19:47:50 +00004618 SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
4619 GetPixelPacketIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004620 break;
4621 }
4622 }
cristyb0d3bb92010-09-22 14:37:58 +00004623 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004624 break;
4625 c++;
4626 if (c == 3)
4627 c=0;
4628 k++;
cristybb503372010-05-27 20:51:26 +00004629 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004630 k=0;
4631 if (k == image->offset)
4632 j++;
4633 }
4634 }
cristy8b27a6d2010-02-14 03:31:15 +00004635 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004636 {
cristy8b27a6d2010-02-14 03:31:15 +00004637 MagickBooleanType
4638 proceed;
4639
4640 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4641 (depth-i),depth);
4642 if (proceed == MagickFalse)
4643 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004644 }
4645 }
cristyb0d3bb92010-09-22 14:37:58 +00004646 stegano_view=DestroyCacheView(stegano_view);
4647 watermark_view=DestroyCacheView(watermark_view);
cristy3ed852e2009-09-05 21:47:34 +00004648 if (stegano_image->storage_class == PseudoClass)
4649 (void) SyncImage(stegano_image);
cristyda16f162011-02-19 23:52:17 +00004650 if (status == MagickFalse)
4651 {
4652 stegano_image=DestroyImage(stegano_image);
4653 return((Image *) NULL);
4654 }
cristy3ed852e2009-09-05 21:47:34 +00004655 return(stegano_image);
4656}
4657
4658/*
4659%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4660% %
4661% %
4662% %
4663% S t e r e o A n a g l y p h I m a g e %
4664% %
4665% %
4666% %
4667%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4668%
4669% StereoAnaglyphImage() combines two images and produces a single image that
4670% is the composite of a left and right image of a stereo pair. Special
4671% red-green stereo glasses are required to view this effect.
4672%
4673% The format of the StereoAnaglyphImage method is:
4674%
4675% Image *StereoImage(const Image *left_image,const Image *right_image,
4676% ExceptionInfo *exception)
4677% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004678% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004679% ExceptionInfo *exception)
4680%
4681% A description of each parameter follows:
4682%
4683% o left_image: the left image.
4684%
4685% o right_image: the right image.
4686%
4687% o exception: return any errors or warnings in this structure.
4688%
4689% o x_offset: amount, in pixels, by which the left image is offset to the
4690% right of the right image.
4691%
4692% o y_offset: amount, in pixels, by which the left image is offset to the
4693% bottom of the right image.
4694%
4695%
4696*/
4697MagickExport Image *StereoImage(const Image *left_image,
4698 const Image *right_image,ExceptionInfo *exception)
4699{
4700 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4701}
4702
4703MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004704 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004705 ExceptionInfo *exception)
4706{
4707#define StereoImageTag "Stereo/Image"
4708
4709 const Image
4710 *image;
4711
4712 Image
4713 *stereo_image;
4714
cristy3ed852e2009-09-05 21:47:34 +00004715 MagickBooleanType
4716 status;
4717
cristy9d314ff2011-03-09 01:30:28 +00004718 ssize_t
4719 y;
4720
cristy3ed852e2009-09-05 21:47:34 +00004721 assert(left_image != (const Image *) NULL);
4722 assert(left_image->signature == MagickSignature);
4723 if (left_image->debug != MagickFalse)
4724 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4725 left_image->filename);
4726 assert(right_image != (const Image *) NULL);
4727 assert(right_image->signature == MagickSignature);
4728 assert(exception != (ExceptionInfo *) NULL);
4729 assert(exception->signature == MagickSignature);
4730 assert(right_image != (const Image *) NULL);
4731 image=left_image;
4732 if ((left_image->columns != right_image->columns) ||
4733 (left_image->rows != right_image->rows))
4734 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4735 /*
4736 Initialize stereo image attributes.
4737 */
4738 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4739 MagickTrue,exception);
4740 if (stereo_image == (Image *) NULL)
4741 return((Image *) NULL);
4742 if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
4743 {
4744 InheritException(exception,&stereo_image->exception);
4745 stereo_image=DestroyImage(stereo_image);
4746 return((Image *) NULL);
4747 }
4748 /*
4749 Copy left image to red channel and right image to blue channel.
4750 */
cristyda16f162011-02-19 23:52:17 +00004751 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004752 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004753 {
cristy4c08aed2011-07-01 19:47:50 +00004754 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004755 *restrict p,
4756 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004757
cristybb503372010-05-27 20:51:26 +00004758 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004759 x;
4760
cristy4c08aed2011-07-01 19:47:50 +00004761 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004762 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004763
4764 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4765 exception);
4766 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4767 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004768 if ((p == (const Quantum *) NULL) ||
4769 (q == (const Quantum *) NULL) || (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004770 break;
cristybb503372010-05-27 20:51:26 +00004771 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004772 {
cristy4c08aed2011-07-01 19:47:50 +00004773 SetPixelRed(image,GetPixelRed(left_image,p),r);
4774 SetPixelGreen(image,GetPixelGreen(left_image,q),r);
4775 SetPixelBlue(image,GetPixelBlue(left_image,q),r);
4776 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4777 GetPixelAlpha(left_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00004778 p+=GetPixelChannels(left_image);
cristy3ed852e2009-09-05 21:47:34 +00004779 q++;
4780 r++;
4781 }
4782 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4783 break;
cristy8b27a6d2010-02-14 03:31:15 +00004784 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004785 {
cristy8b27a6d2010-02-14 03:31:15 +00004786 MagickBooleanType
4787 proceed;
4788
cristybb503372010-05-27 20:51:26 +00004789 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4790 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004791 if (proceed == MagickFalse)
4792 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004793 }
4794 }
cristyda16f162011-02-19 23:52:17 +00004795 if (status == MagickFalse)
4796 {
4797 stereo_image=DestroyImage(stereo_image);
4798 return((Image *) NULL);
4799 }
cristy3ed852e2009-09-05 21:47:34 +00004800 return(stereo_image);
4801}
4802
4803/*
4804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4805% %
4806% %
4807% %
4808% S w i r l I m a g e %
4809% %
4810% %
4811% %
4812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4813%
4814% SwirlImage() swirls the pixels about the center of the image, where
4815% degrees indicates the sweep of the arc through which each pixel is moved.
4816% You get a more dramatic effect as the degrees move from 1 to 360.
4817%
4818% The format of the SwirlImage method is:
4819%
4820% Image *SwirlImage(const Image *image,double degrees,
4821% ExceptionInfo *exception)
4822%
4823% A description of each parameter follows:
4824%
4825% o image: the image.
4826%
4827% o degrees: Define the tightness of the swirling effect.
4828%
4829% o exception: return any errors or warnings in this structure.
4830%
4831*/
4832MagickExport Image *SwirlImage(const Image *image,double degrees,
4833 ExceptionInfo *exception)
4834{
4835#define SwirlImageTag "Swirl/Image"
4836
cristyfa112112010-01-04 17:48:07 +00004837 CacheView
4838 *image_view,
4839 *swirl_view;
4840
cristy3ed852e2009-09-05 21:47:34 +00004841 Image
4842 *swirl_image;
4843
cristy3ed852e2009-09-05 21:47:34 +00004844 MagickBooleanType
4845 status;
4846
cristybb503372010-05-27 20:51:26 +00004847 MagickOffsetType
4848 progress;
4849
cristy4c08aed2011-07-01 19:47:50 +00004850 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004851 zero;
4852
4853 MagickRealType
4854 radius;
4855
4856 PointInfo
4857 center,
4858 scale;
4859
cristybb503372010-05-27 20:51:26 +00004860 ssize_t
4861 y;
4862
cristy3ed852e2009-09-05 21:47:34 +00004863 /*
4864 Initialize swirl image attributes.
4865 */
4866 assert(image != (const Image *) NULL);
4867 assert(image->signature == MagickSignature);
4868 if (image->debug != MagickFalse)
4869 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4870 assert(exception != (ExceptionInfo *) NULL);
4871 assert(exception->signature == MagickSignature);
4872 swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4873 if (swirl_image == (Image *) NULL)
4874 return((Image *) NULL);
4875 if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
4876 {
4877 InheritException(exception,&swirl_image->exception);
4878 swirl_image=DestroyImage(swirl_image);
4879 return((Image *) NULL);
4880 }
cristy4c08aed2011-07-01 19:47:50 +00004881 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004882 swirl_image->matte=MagickTrue;
4883 /*
4884 Compute scaling factor.
4885 */
4886 center.x=(double) image->columns/2.0;
4887 center.y=(double) image->rows/2.0;
4888 radius=MagickMax(center.x,center.y);
4889 scale.x=1.0;
4890 scale.y=1.0;
4891 if (image->columns > image->rows)
4892 scale.y=(double) image->columns/(double) image->rows;
4893 else
4894 if (image->columns < image->rows)
4895 scale.x=(double) image->rows/(double) image->columns;
4896 degrees=(double) DegreesToRadians(degrees);
4897 /*
4898 Swirl image.
4899 */
4900 status=MagickTrue;
4901 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00004902 GetPixelInfo(swirl_image,&zero);
cristy3ed852e2009-09-05 21:47:34 +00004903 image_view=AcquireCacheView(image);
4904 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00004905#if defined(MAGICKCORE_OPENMP_SUPPORT)
4906 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004907#endif
cristybb503372010-05-27 20:51:26 +00004908 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004909 {
cristy4c08aed2011-07-01 19:47:50 +00004910 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004911 pixel;
4912
4913 MagickRealType
4914 distance;
4915
4916 PointInfo
4917 delta;
4918
cristybb503372010-05-27 20:51:26 +00004919 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004920 x;
4921
cristy4c08aed2011-07-01 19:47:50 +00004922 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004923 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004924
4925 if (status == MagickFalse)
4926 continue;
4927 q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
4928 exception);
cristy4c08aed2011-07-01 19:47:50 +00004929 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004930 {
4931 status=MagickFalse;
4932 continue;
4933 }
cristy3ed852e2009-09-05 21:47:34 +00004934 delta.y=scale.y*(double) (y-center.y);
4935 pixel=zero;
cristybb503372010-05-27 20:51:26 +00004936 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004937 {
4938 /*
4939 Determine if the pixel is within an ellipse.
4940 */
4941 delta.x=scale.x*(double) (x-center.x);
4942 distance=delta.x*delta.x+delta.y*delta.y;
4943 if (distance < (radius*radius))
4944 {
4945 MagickRealType
4946 cosine,
4947 factor,
4948 sine;
4949
4950 /*
4951 Swirl the pixel.
4952 */
4953 factor=1.0-sqrt((double) distance)/radius;
4954 sine=sin((double) (degrees*factor*factor));
4955 cosine=cos((double) (degrees*factor*factor));
cristy4c08aed2011-07-01 19:47:50 +00004956 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00004957 UndefinedInterpolatePixel,(double) ((cosine*delta.x-sine*delta.y)/
4958 scale.x+center.x),(double) ((sine*delta.x+cosine*delta.y)/scale.y+
cristyd76c51e2011-03-26 00:21:26 +00004959 center.y),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00004960 SetPixelPixelInfo(swirl_image,&pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00004961 }
cristyed231572011-07-14 02:18:59 +00004962 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00004963 }
4964 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
4965 status=MagickFalse;
4966 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4967 {
4968 MagickBooleanType
4969 proceed;
4970
cristyb5d5f722009-11-04 03:03:49 +00004971#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004972 #pragma omp critical (MagickCore_SwirlImage)
4973#endif
4974 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
4975 if (proceed == MagickFalse)
4976 status=MagickFalse;
4977 }
4978 }
4979 swirl_view=DestroyCacheView(swirl_view);
4980 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00004981 if (status == MagickFalse)
4982 swirl_image=DestroyImage(swirl_image);
4983 return(swirl_image);
4984}
4985
4986/*
4987%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4988% %
4989% %
4990% %
4991% T i n t I m a g e %
4992% %
4993% %
4994% %
4995%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4996%
4997% TintImage() applies a color vector to each pixel in the image. The length
4998% of the vector is 0 for black and white and at its maximum for the midtones.
4999% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5000%
5001% The format of the TintImage method is:
5002%
5003% Image *TintImage(const Image *image,const char *opacity,
5004% const PixelPacket tint,ExceptionInfo *exception)
5005%
5006% A description of each parameter follows:
5007%
5008% o image: the image.
5009%
5010% o opacity: A color value used for tinting.
5011%
5012% o tint: A color value used for tinting.
5013%
5014% o exception: return any errors or warnings in this structure.
5015%
5016*/
5017MagickExport Image *TintImage(const Image *image,const char *opacity,
5018 const PixelPacket tint,ExceptionInfo *exception)
5019{
5020#define TintImageTag "Tint/Image"
5021
cristyc4c8d132010-01-07 01:58:38 +00005022 CacheView
5023 *image_view,
5024 *tint_view;
5025
cristy3ed852e2009-09-05 21:47:34 +00005026 GeometryInfo
5027 geometry_info;
5028
5029 Image
5030 *tint_image;
5031
cristy3ed852e2009-09-05 21:47:34 +00005032 MagickBooleanType
5033 status;
5034
cristybb503372010-05-27 20:51:26 +00005035 MagickOffsetType
5036 progress;
cristy3ed852e2009-09-05 21:47:34 +00005037
cristy4c08aed2011-07-01 19:47:50 +00005038 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005039 color_vector,
5040 pixel;
5041
cristybb503372010-05-27 20:51:26 +00005042 MagickStatusType
5043 flags;
5044
5045 ssize_t
5046 y;
5047
cristy3ed852e2009-09-05 21:47:34 +00005048 /*
5049 Allocate tint image.
5050 */
5051 assert(image != (const Image *) NULL);
5052 assert(image->signature == MagickSignature);
5053 if (image->debug != MagickFalse)
5054 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5055 assert(exception != (ExceptionInfo *) NULL);
5056 assert(exception->signature == MagickSignature);
5057 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5058 if (tint_image == (Image *) NULL)
5059 return((Image *) NULL);
5060 if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5061 {
5062 InheritException(exception,&tint_image->exception);
5063 tint_image=DestroyImage(tint_image);
5064 return((Image *) NULL);
5065 }
5066 if (opacity == (const char *) NULL)
5067 return(tint_image);
5068 /*
5069 Determine RGB values of the color.
5070 */
5071 flags=ParseGeometry(opacity,&geometry_info);
5072 pixel.red=geometry_info.rho;
5073 if ((flags & SigmaValue) != 0)
5074 pixel.green=geometry_info.sigma;
5075 else
5076 pixel.green=pixel.red;
5077 if ((flags & XiValue) != 0)
5078 pixel.blue=geometry_info.xi;
5079 else
5080 pixel.blue=pixel.red;
5081 if ((flags & PsiValue) != 0)
cristy4c08aed2011-07-01 19:47:50 +00005082 pixel.alpha=geometry_info.psi;
cristy3ed852e2009-09-05 21:47:34 +00005083 else
cristy4c08aed2011-07-01 19:47:50 +00005084 pixel.alpha=(MagickRealType) OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005085 color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
cristy4c08aed2011-07-01 19:47:50 +00005086 GetPixelPacketIntensity(&tint));
cristy3ed852e2009-09-05 21:47:34 +00005087 color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
cristy4c08aed2011-07-01 19:47:50 +00005088 GetPixelPacketIntensity(&tint));
cristy3ed852e2009-09-05 21:47:34 +00005089 color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
cristy4c08aed2011-07-01 19:47:50 +00005090 GetPixelPacketIntensity(&tint));
cristy3ed852e2009-09-05 21:47:34 +00005091 /*
5092 Tint image.
5093 */
5094 status=MagickTrue;
5095 progress=0;
5096 image_view=AcquireCacheView(image);
5097 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005098#if defined(MAGICKCORE_OPENMP_SUPPORT)
5099 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005100#endif
cristybb503372010-05-27 20:51:26 +00005101 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005102 {
cristy4c08aed2011-07-01 19:47:50 +00005103 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005104 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005105
cristy4c08aed2011-07-01 19:47:50 +00005106 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005107 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005108
cristy6b91acb2011-04-19 12:23:54 +00005109 register ssize_t
5110 x;
5111
cristy3ed852e2009-09-05 21:47:34 +00005112 if (status == MagickFalse)
5113 continue;
5114 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5115 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5116 exception);
cristy4c08aed2011-07-01 19:47:50 +00005117 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005118 {
5119 status=MagickFalse;
5120 continue;
5121 }
cristybb503372010-05-27 20:51:26 +00005122 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005123 {
cristy4c08aed2011-07-01 19:47:50 +00005124 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005125 pixel;
5126
5127 MagickRealType
5128 weight;
5129
cristy4c08aed2011-07-01 19:47:50 +00005130 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5131 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5132 (1.0-(4.0*(weight*weight)));
5133 SetPixelRed(tint_image,ClampToQuantum(pixel.red),q);
5134 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5135 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5136 (1.0-(4.0*(weight*weight)));
5137 SetPixelGreen(tint_image,ClampToQuantum(pixel.green),q);
5138 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5139 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5140 (1.0-(4.0*(weight*weight)));
5141 SetPixelBlue(tint_image,ClampToQuantum(pixel.blue),q);
5142 SetPixelAlpha(tint_image,GetPixelAlpha(image,p),q);
cristyed231572011-07-14 02:18:59 +00005143 p+=GetPixelChannels(image);
5144 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005145 }
5146 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5147 status=MagickFalse;
5148 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5149 {
5150 MagickBooleanType
5151 proceed;
5152
cristyb5d5f722009-11-04 03:03:49 +00005153#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005154 #pragma omp critical (MagickCore_TintImage)
5155#endif
5156 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5157 if (proceed == MagickFalse)
5158 status=MagickFalse;
5159 }
5160 }
5161 tint_view=DestroyCacheView(tint_view);
5162 image_view=DestroyCacheView(image_view);
5163 if (status == MagickFalse)
5164 tint_image=DestroyImage(tint_image);
5165 return(tint_image);
5166}
5167
5168/*
5169%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5170% %
5171% %
5172% %
5173% V i g n e t t e I m a g e %
5174% %
5175% %
5176% %
5177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5178%
5179% VignetteImage() softens the edges of the image in vignette style.
5180%
5181% The format of the VignetteImage method is:
5182%
5183% Image *VignetteImage(const Image *image,const double radius,
cristyb3a73b52011-07-26 01:34:43 +00005184% const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005185%
5186% A description of each parameter follows:
5187%
5188% o image: the image.
5189%
5190% o radius: the radius of the pixel neighborhood.
5191%
5192% o sigma: the standard deviation of the Gaussian, in pixels.
5193%
5194% o x, y: Define the x and y ellipse offset.
5195%
5196% o exception: return any errors or warnings in this structure.
5197%
5198*/
5199MagickExport Image *VignetteImage(const Image *image,const double radius,
cristybb503372010-05-27 20:51:26 +00005200 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005201{
5202 char
5203 ellipse[MaxTextExtent];
5204
5205 DrawInfo
5206 *draw_info;
5207
5208 Image
5209 *canvas_image,
5210 *blur_image,
5211 *oval_image,
5212 *vignette_image;
5213
5214 assert(image != (Image *) NULL);
5215 assert(image->signature == MagickSignature);
5216 if (image->debug != MagickFalse)
5217 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5218 assert(exception != (ExceptionInfo *) NULL);
5219 assert(exception->signature == MagickSignature);
5220 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5221 if (canvas_image == (Image *) NULL)
5222 return((Image *) NULL);
5223 if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
5224 {
5225 InheritException(exception,&canvas_image->exception);
5226 canvas_image=DestroyImage(canvas_image);
5227 return((Image *) NULL);
5228 }
5229 canvas_image->matte=MagickTrue;
cristyb3a73b52011-07-26 01:34:43 +00005230 oval_image=CloneImage(canvas_image,canvas_image->columns,
5231 canvas_image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005232 if (oval_image == (Image *) NULL)
5233 {
5234 canvas_image=DestroyImage(canvas_image);
5235 return((Image *) NULL);
5236 }
5237 (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5238 (void) SetImageBackgroundColor(oval_image);
5239 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5240 (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5241 (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
cristyb51dff52011-05-19 16:55:47 +00005242 (void) FormatLocaleString(ellipse,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00005243 "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
cristy8cd5b312010-01-07 01:10:24 +00005244 image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005245 draw_info->primitive=AcquireString(ellipse);
5246 (void) DrawImage(oval_image,draw_info);
5247 draw_info=DestroyDrawInfo(draw_info);
5248 blur_image=BlurImage(oval_image,radius,sigma,exception);
5249 oval_image=DestroyImage(oval_image);
5250 if (blur_image == (Image *) NULL)
5251 {
5252 canvas_image=DestroyImage(canvas_image);
5253 return((Image *) NULL);
5254 }
5255 blur_image->matte=MagickFalse;
5256 (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5257 blur_image=DestroyImage(blur_image);
5258 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5259 canvas_image=DestroyImage(canvas_image);
5260 return(vignette_image);
5261}
5262
5263/*
5264%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5265% %
5266% %
5267% %
5268% W a v e I m a g e %
5269% %
5270% %
5271% %
5272%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5273%
5274% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005275% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005276% by the given parameters.
5277%
5278% The format of the WaveImage method is:
5279%
5280% Image *WaveImage(const Image *image,const double amplitude,
5281% const double wave_length,ExceptionInfo *exception)
5282%
5283% A description of each parameter follows:
5284%
5285% o image: the image.
5286%
5287% o amplitude, wave_length: Define the amplitude and wave length of the
5288% sine wave.
5289%
5290% o exception: return any errors or warnings in this structure.
5291%
5292*/
5293MagickExport Image *WaveImage(const Image *image,const double amplitude,
5294 const double wave_length,ExceptionInfo *exception)
5295{
5296#define WaveImageTag "Wave/Image"
5297
cristyfa112112010-01-04 17:48:07 +00005298 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005299 *image_view,
cristyfa112112010-01-04 17:48:07 +00005300 *wave_view;
5301
cristy3ed852e2009-09-05 21:47:34 +00005302 Image
5303 *wave_image;
5304
cristy3ed852e2009-09-05 21:47:34 +00005305 MagickBooleanType
5306 status;
5307
cristybb503372010-05-27 20:51:26 +00005308 MagickOffsetType
5309 progress;
5310
cristy4c08aed2011-07-01 19:47:50 +00005311 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005312 zero;
5313
5314 MagickRealType
5315 *sine_map;
5316
cristybb503372010-05-27 20:51:26 +00005317 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005318 i;
5319
cristybb503372010-05-27 20:51:26 +00005320 ssize_t
5321 y;
5322
cristy3ed852e2009-09-05 21:47:34 +00005323 /*
5324 Initialize wave image attributes.
5325 */
5326 assert(image != (Image *) NULL);
5327 assert(image->signature == MagickSignature);
5328 if (image->debug != MagickFalse)
5329 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5330 assert(exception != (ExceptionInfo *) NULL);
5331 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005332 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005333 fabs(amplitude)),MagickTrue,exception);
5334 if (wave_image == (Image *) NULL)
5335 return((Image *) NULL);
5336 if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
5337 {
5338 InheritException(exception,&wave_image->exception);
5339 wave_image=DestroyImage(wave_image);
5340 return((Image *) NULL);
5341 }
cristy4c08aed2011-07-01 19:47:50 +00005342 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005343 wave_image->matte=MagickTrue;
5344 /*
5345 Allocate sine map.
5346 */
5347 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5348 sizeof(*sine_map));
5349 if (sine_map == (MagickRealType *) NULL)
5350 {
5351 wave_image=DestroyImage(wave_image);
5352 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5353 }
cristybb503372010-05-27 20:51:26 +00005354 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005355 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5356 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005357 /*
5358 Wave image.
5359 */
5360 status=MagickTrue;
5361 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00005362 GetPixelInfo(wave_image,&zero);
cristyd76c51e2011-03-26 00:21:26 +00005363 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005364 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005365 (void) SetCacheViewVirtualPixelMethod(image_view,
5366 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005367#if defined(MAGICKCORE_OPENMP_SUPPORT)
5368 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005369#endif
cristybb503372010-05-27 20:51:26 +00005370 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005371 {
cristy4c08aed2011-07-01 19:47:50 +00005372 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005373 pixel;
5374
cristy4c08aed2011-07-01 19:47:50 +00005375 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005376 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005377
cristye97bb922011-04-03 01:36:52 +00005378 register ssize_t
5379 x;
5380
cristy3ed852e2009-09-05 21:47:34 +00005381 if (status == MagickFalse)
5382 continue;
5383 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5384 exception);
cristy4c08aed2011-07-01 19:47:50 +00005385 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005386 {
5387 status=MagickFalse;
5388 continue;
5389 }
cristy3ed852e2009-09-05 21:47:34 +00005390 pixel=zero;
cristybb503372010-05-27 20:51:26 +00005391 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005392 {
cristy4c08aed2011-07-01 19:47:50 +00005393 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00005394 UndefinedInterpolatePixel,(double) x,(double) (y-sine_map[x]),&pixel,
5395 exception);
cristy4c08aed2011-07-01 19:47:50 +00005396 SetPixelPixelInfo(wave_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005397 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005398 }
5399 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5400 status=MagickFalse;
5401 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5402 {
5403 MagickBooleanType
5404 proceed;
5405
cristyb5d5f722009-11-04 03:03:49 +00005406#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005407 #pragma omp critical (MagickCore_WaveImage)
5408#endif
5409 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5410 if (proceed == MagickFalse)
5411 status=MagickFalse;
5412 }
5413 }
5414 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005415 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005416 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5417 if (status == MagickFalse)
5418 wave_image=DestroyImage(wave_image);
5419 return(wave_image);
5420}