blob: 552062efddf3cd2fe6a5c2c6ad52127e001c6b59 [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);
cristy574cc262011-08-05 01:23:58 +0000346 if (SetImageStorageClass(noise_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000347 {
cristy3ed852e2009-09-05 21:47:34 +0000348 noise_image=DestroyImage(noise_image);
349 return((Image *) NULL);
350 }
351 /*
352 Add noise in each row.
353 */
354 attenuate=1.0;
355 option=GetImageArtifact(image,"attenuate");
356 if (option != (char *) NULL)
cristyc1acd842011-05-19 23:05:47 +0000357 attenuate=InterpretLocaleValue(option,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000358 status=MagickTrue;
359 progress=0;
360 random_info=AcquireRandomInfoThreadSet();
361 image_view=AcquireCacheView(image);
362 noise_view=AcquireCacheView(noise_image);
cristy319a1e72010-02-21 15:13:11 +0000363#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000364 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000365#endif
cristybb503372010-05-27 20:51:26 +0000366 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000367 {
cristy5c9e6f22010-09-17 17:31:01 +0000368 const int
369 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000370
cristy3ed852e2009-09-05 21:47:34 +0000371 MagickBooleanType
372 sync;
373
cristy4c08aed2011-07-01 19:47:50 +0000374 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000375 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000376
cristybb503372010-05-27 20:51:26 +0000377 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000378 x;
379
cristy4c08aed2011-07-01 19:47:50 +0000380 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000381 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000382
383 if (status == MagickFalse)
384 continue;
385 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
386 q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
387 exception);
cristy4c08aed2011-07-01 19:47:50 +0000388 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000389 {
390 status=MagickFalse;
391 continue;
392 }
cristybb503372010-05-27 20:51:26 +0000393 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000394 {
cristyb3a73b52011-07-26 01:34:43 +0000395 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
396 SetPixelRed(noise_image,ClampToQuantum(GenerateDifferentialNoise(
397 random_info[id],GetPixelRed(image,p),noise_type,attenuate)),q);
398 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
399 SetPixelGreen(noise_image,ClampToQuantum(GenerateDifferentialNoise(
400 random_info[id],GetPixelGreen(image,p),noise_type,attenuate)),q);
401 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
402 SetPixelBlue(noise_image,ClampToQuantum(GenerateDifferentialNoise(
403 random_info[id],GetPixelBlue(image,p),noise_type,attenuate)),q);
404 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
405 (image->colorspace == CMYKColorspace))
406 SetPixelBlack(noise_image,ClampToQuantum(GenerateDifferentialNoise(
407 random_info[id],GetPixelBlack(image,p),noise_type,attenuate)),q);
408 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
409 SetPixelAlpha(noise_image,ClampToQuantum(GenerateDifferentialNoise(
410 random_info[id],GetPixelAlpha(image,p),noise_type,attenuate)),q);
cristyed231572011-07-14 02:18:59 +0000411 p+=GetPixelChannels(image);
412 q+=GetPixelChannels(noise_image);
cristy3ed852e2009-09-05 21:47:34 +0000413 }
414 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
415 if (sync == MagickFalse)
416 status=MagickFalse;
417 if (image->progress_monitor != (MagickProgressMonitor) NULL)
418 {
419 MagickBooleanType
420 proceed;
421
cristyb5d5f722009-11-04 03:03:49 +0000422#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy319a1e72010-02-21 15:13:11 +0000423 #pragma omp critical (MagickCore_AddNoiseImage)
cristy3ed852e2009-09-05 21:47:34 +0000424#endif
425 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
426 image->rows);
427 if (proceed == MagickFalse)
428 status=MagickFalse;
429 }
430 }
431 noise_view=DestroyCacheView(noise_view);
432 image_view=DestroyCacheView(image_view);
433 random_info=DestroyRandomInfoThreadSet(random_info);
434 if (status == MagickFalse)
435 noise_image=DestroyImage(noise_image);
436 return(noise_image);
437}
438
439/*
440%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441% %
442% %
443% %
444% B l u e S h i f t I m a g e %
445% %
446% %
447% %
448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449%
450% BlueShiftImage() mutes the colors of the image to simulate a scene at
451% nighttime in the moonlight.
452%
453% The format of the BlueShiftImage method is:
454%
455% Image *BlueShiftImage(const Image *image,const double factor,
456% ExceptionInfo *exception)
457%
458% A description of each parameter follows:
459%
460% o image: the image.
461%
462% o factor: the shift factor.
463%
464% o exception: return any errors or warnings in this structure.
465%
466*/
467MagickExport Image *BlueShiftImage(const Image *image,const double factor,
468 ExceptionInfo *exception)
469{
470#define BlueShiftImageTag "BlueShift/Image"
471
cristyc4c8d132010-01-07 01:58:38 +0000472 CacheView
473 *image_view,
474 *shift_view;
475
cristy3ed852e2009-09-05 21:47:34 +0000476 Image
477 *shift_image;
478
cristy3ed852e2009-09-05 21:47:34 +0000479 MagickBooleanType
480 status;
481
cristybb503372010-05-27 20:51:26 +0000482 MagickOffsetType
483 progress;
484
485 ssize_t
486 y;
487
cristy3ed852e2009-09-05 21:47:34 +0000488 /*
489 Allocate blue shift image.
490 */
491 assert(image != (const Image *) NULL);
492 assert(image->signature == MagickSignature);
493 if (image->debug != MagickFalse)
494 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
495 assert(exception != (ExceptionInfo *) NULL);
496 assert(exception->signature == MagickSignature);
497 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,
498 exception);
499 if (shift_image == (Image *) NULL)
500 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000501 if (SetImageStorageClass(shift_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000502 {
cristy3ed852e2009-09-05 21:47:34 +0000503 shift_image=DestroyImage(shift_image);
504 return((Image *) NULL);
505 }
506 /*
507 Blue-shift DirectClass image.
508 */
509 status=MagickTrue;
510 progress=0;
511 image_view=AcquireCacheView(image);
512 shift_view=AcquireCacheView(shift_image);
cristy319a1e72010-02-21 15:13:11 +0000513#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000514 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000515#endif
cristybb503372010-05-27 20:51:26 +0000516 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000517 {
518 MagickBooleanType
519 sync;
520
cristy4c08aed2011-07-01 19:47:50 +0000521 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000522 pixel;
523
524 Quantum
525 quantum;
526
cristy4c08aed2011-07-01 19:47:50 +0000527 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000528 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000529
cristybb503372010-05-27 20:51:26 +0000530 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000531 x;
532
cristy4c08aed2011-07-01 19:47:50 +0000533 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000534 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000535
536 if (status == MagickFalse)
537 continue;
538 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
539 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
540 exception);
cristy4c08aed2011-07-01 19:47:50 +0000541 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000542 {
543 status=MagickFalse;
544 continue;
545 }
cristybb503372010-05-27 20:51:26 +0000546 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000547 {
cristy4c08aed2011-07-01 19:47:50 +0000548 quantum=GetPixelRed(image,p);
549 if (GetPixelGreen(image,p) < quantum)
550 quantum=GetPixelGreen(image,p);
551 if (GetPixelBlue(image,p) < quantum)
552 quantum=GetPixelBlue(image,p);
553 pixel.red=0.5*(GetPixelRed(image,p)+factor*quantum);
554 pixel.green=0.5*(GetPixelGreen(image,p)+factor*quantum);
555 pixel.blue=0.5*(GetPixelBlue(image,p)+factor*quantum);
556 quantum=GetPixelRed(image,p);
557 if (GetPixelGreen(image,p) > quantum)
558 quantum=GetPixelGreen(image,p);
559 if (GetPixelBlue(image,p) > quantum)
560 quantum=GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000561 pixel.red=0.5*(pixel.red+factor*quantum);
562 pixel.green=0.5*(pixel.green+factor*quantum);
563 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristy4c08aed2011-07-01 19:47:50 +0000564 SetPixelRed(shift_image,ClampToQuantum(pixel.red),q);
565 SetPixelGreen(shift_image,ClampToQuantum(pixel.green),q);
566 SetPixelBlue(shift_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000567 p+=GetPixelChannels(image);
568 q+=GetPixelChannels(shift_image);
cristy3ed852e2009-09-05 21:47:34 +0000569 }
570 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
571 if (sync == MagickFalse)
572 status=MagickFalse;
573 if (image->progress_monitor != (MagickProgressMonitor) NULL)
574 {
575 MagickBooleanType
576 proceed;
577
cristy319a1e72010-02-21 15:13:11 +0000578#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000579 #pragma omp critical (MagickCore_BlueShiftImage)
580#endif
581 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
582 image->rows);
583 if (proceed == MagickFalse)
584 status=MagickFalse;
585 }
586 }
587 image_view=DestroyCacheView(image_view);
588 shift_view=DestroyCacheView(shift_view);
589 if (status == MagickFalse)
590 shift_image=DestroyImage(shift_image);
591 return(shift_image);
592}
593
594/*
595%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
596% %
597% %
598% %
599% C h a r c o a l I m a g e %
600% %
601% %
602% %
603%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
604%
605% CharcoalImage() creates a new image that is a copy of an existing one with
606% the edge highlighted. It allocates the memory necessary for the new Image
607% structure and returns a pointer to the new image.
608%
609% The format of the CharcoalImage method is:
610%
611% Image *CharcoalImage(const Image *image,const double radius,
612% const double sigma,ExceptionInfo *exception)
613%
614% A description of each parameter follows:
615%
616% o image: the image.
617%
618% o radius: the radius of the pixel neighborhood.
619%
620% o sigma: the standard deviation of the Gaussian, in pixels.
621%
622% o exception: return any errors or warnings in this structure.
623%
624*/
625MagickExport Image *CharcoalImage(const Image *image,const double radius,
626 const double sigma,ExceptionInfo *exception)
627{
628 Image
629 *charcoal_image,
630 *clone_image,
631 *edge_image;
632
633 assert(image != (Image *) NULL);
634 assert(image->signature == MagickSignature);
635 if (image->debug != MagickFalse)
636 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
637 assert(exception != (ExceptionInfo *) NULL);
638 assert(exception->signature == MagickSignature);
639 clone_image=CloneImage(image,0,0,MagickTrue,exception);
640 if (clone_image == (Image *) NULL)
641 return((Image *) NULL);
642 (void) SetImageType(clone_image,GrayscaleType);
643 edge_image=EdgeImage(clone_image,radius,exception);
644 clone_image=DestroyImage(clone_image);
645 if (edge_image == (Image *) NULL)
646 return((Image *) NULL);
647 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
648 edge_image=DestroyImage(edge_image);
649 if (charcoal_image == (Image *) NULL)
650 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +0000651 (void) NormalizeImage(charcoal_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +0000652 (void) NegateImage(charcoal_image,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +0000653 (void) SetImageType(charcoal_image,GrayscaleType);
654 return(charcoal_image);
655}
656
657/*
658%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
659% %
660% %
661% %
662% C o l o r i z e I m a g e %
663% %
664% %
665% %
666%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667%
668% ColorizeImage() blends the fill color with each pixel in the image.
669% A percentage blend is specified with opacity. Control the application
670% of different color components by specifying a different percentage for
671% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
672%
673% The format of the ColorizeImage method is:
674%
675% Image *ColorizeImage(const Image *image,const char *opacity,
676% const PixelPacket colorize,ExceptionInfo *exception)
677%
678% A description of each parameter follows:
679%
680% o image: the image.
681%
682% o opacity: A character string indicating the level of opacity as a
683% percentage.
684%
685% o colorize: A color value.
686%
687% o exception: return any errors or warnings in this structure.
688%
689*/
690MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
691 const PixelPacket colorize,ExceptionInfo *exception)
692{
693#define ColorizeImageTag "Colorize/Image"
694
cristyc4c8d132010-01-07 01:58:38 +0000695 CacheView
696 *colorize_view,
697 *image_view;
698
cristy3ed852e2009-09-05 21:47:34 +0000699 GeometryInfo
700 geometry_info;
701
702 Image
703 *colorize_image;
704
cristy3ed852e2009-09-05 21:47:34 +0000705 MagickBooleanType
706 status;
707
cristybb503372010-05-27 20:51:26 +0000708 MagickOffsetType
709 progress;
710
cristy4c08aed2011-07-01 19:47:50 +0000711 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000712 pixel;
713
714 MagickStatusType
715 flags;
716
cristybb503372010-05-27 20:51:26 +0000717 ssize_t
718 y;
719
cristy3ed852e2009-09-05 21:47:34 +0000720 /*
721 Allocate colorized image.
722 */
723 assert(image != (const Image *) NULL);
724 assert(image->signature == MagickSignature);
725 if (image->debug != MagickFalse)
726 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
727 assert(exception != (ExceptionInfo *) NULL);
728 assert(exception->signature == MagickSignature);
729 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
730 exception);
731 if (colorize_image == (Image *) NULL)
732 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000733 if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000734 {
cristy3ed852e2009-09-05 21:47:34 +0000735 colorize_image=DestroyImage(colorize_image);
736 return((Image *) NULL);
737 }
738 if (opacity == (const char *) NULL)
739 return(colorize_image);
740 /*
741 Determine RGB values of the pen color.
742 */
743 flags=ParseGeometry(opacity,&geometry_info);
744 pixel.red=geometry_info.rho;
745 pixel.green=geometry_info.rho;
746 pixel.blue=geometry_info.rho;
cristy4c08aed2011-07-01 19:47:50 +0000747 pixel.alpha=(MagickRealType) OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +0000748 if ((flags & SigmaValue) != 0)
749 pixel.green=geometry_info.sigma;
750 if ((flags & XiValue) != 0)
751 pixel.blue=geometry_info.xi;
752 if ((flags & PsiValue) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000753 pixel.alpha=geometry_info.psi;
cristy3ed852e2009-09-05 21:47:34 +0000754 /*
755 Colorize DirectClass image.
756 */
757 status=MagickTrue;
758 progress=0;
759 image_view=AcquireCacheView(image);
760 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000761#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000762 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000763#endif
cristybb503372010-05-27 20:51:26 +0000764 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000765 {
766 MagickBooleanType
767 sync;
768
cristy4c08aed2011-07-01 19:47:50 +0000769 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000770 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000771
cristybb503372010-05-27 20:51:26 +0000772 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000773 x;
774
cristy4c08aed2011-07-01 19:47:50 +0000775 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000776 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000777
778 if (status == MagickFalse)
779 continue;
780 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
781 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
782 exception);
cristy4c08aed2011-07-01 19:47:50 +0000783 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000784 {
785 status=MagickFalse;
786 continue;
787 }
cristybb503372010-05-27 20:51:26 +0000788 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000789 {
cristy4c08aed2011-07-01 19:47:50 +0000790 SetPixelRed(colorize_image,ClampToQuantum((GetPixelRed(image,p)*
791 (100.0-pixel.red)+colorize.red*pixel.red)/100.0),q);
792 SetPixelGreen(colorize_image,ClampToQuantum((GetPixelGreen(image,p)*
793 (100.0-pixel.green)+colorize.green*pixel.green)/100.0),q);
794 SetPixelBlue(colorize_image,ClampToQuantum((GetPixelBlue(image,p)*
795 (100.0-pixel.blue)+colorize.blue*pixel.blue)/100.0),q);
796 SetPixelAlpha(colorize_image,ClampToQuantum((GetPixelAlpha(image,p)*
797 (100.0-pixel.alpha)+colorize.alpha*pixel.alpha)/100.0),q);
cristyed231572011-07-14 02:18:59 +0000798 p+=GetPixelChannels(image);
799 q+=GetPixelChannels(colorize_image);
cristy3ed852e2009-09-05 21:47:34 +0000800 }
801 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
802 if (sync == MagickFalse)
803 status=MagickFalse;
804 if (image->progress_monitor != (MagickProgressMonitor) NULL)
805 {
806 MagickBooleanType
807 proceed;
808
cristy319a1e72010-02-21 15:13:11 +0000809#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000810 #pragma omp critical (MagickCore_ColorizeImage)
811#endif
812 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
813 if (proceed == MagickFalse)
814 status=MagickFalse;
815 }
816 }
817 image_view=DestroyCacheView(image_view);
818 colorize_view=DestroyCacheView(colorize_view);
819 if (status == MagickFalse)
820 colorize_image=DestroyImage(colorize_image);
821 return(colorize_image);
822}
823
824/*
825%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
826% %
827% %
828% %
cristye6365592010-04-02 17:31:23 +0000829% C o l o r M a t r i x I m a g e %
830% %
831% %
832% %
833%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
834%
835% ColorMatrixImage() applies color transformation to an image. This method
836% permits saturation changes, hue rotation, luminance to alpha, and various
837% other effects. Although variable-sized transformation matrices can be used,
838% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
839% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
840% except offsets are in column 6 rather than 5 (in support of CMYKA images)
841% and offsets are normalized (divide Flash offset by 255).
842%
843% The format of the ColorMatrixImage method is:
844%
845% Image *ColorMatrixImage(const Image *image,
846% const KernelInfo *color_matrix,ExceptionInfo *exception)
847%
848% A description of each parameter follows:
849%
850% o image: the image.
851%
852% o color_matrix: the color matrix.
853%
854% o exception: return any errors or warnings in this structure.
855%
856*/
857MagickExport Image *ColorMatrixImage(const Image *image,
858 const KernelInfo *color_matrix,ExceptionInfo *exception)
859{
860#define ColorMatrixImageTag "ColorMatrix/Image"
861
862 CacheView
863 *color_view,
864 *image_view;
865
866 double
867 ColorMatrix[6][6] =
868 {
869 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
870 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
871 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
872 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
873 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
874 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
875 };
876
877 Image
878 *color_image;
879
cristye6365592010-04-02 17:31:23 +0000880 MagickBooleanType
881 status;
882
cristybb503372010-05-27 20:51:26 +0000883 MagickOffsetType
884 progress;
885
886 register ssize_t
cristye6365592010-04-02 17:31:23 +0000887 i;
888
cristybb503372010-05-27 20:51:26 +0000889 ssize_t
890 u,
891 v,
892 y;
893
cristye6365592010-04-02 17:31:23 +0000894 /*
895 Create color matrix.
896 */
897 assert(image != (Image *) NULL);
898 assert(image->signature == MagickSignature);
899 if (image->debug != MagickFalse)
900 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
901 assert(exception != (ExceptionInfo *) NULL);
902 assert(exception->signature == MagickSignature);
903 i=0;
cristybb503372010-05-27 20:51:26 +0000904 for (v=0; v < (ssize_t) color_matrix->height; v++)
905 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000906 {
907 if ((v < 6) && (u < 6))
908 ColorMatrix[v][u]=color_matrix->values[i];
909 i++;
910 }
911 /*
912 Initialize color image.
913 */
cristy12550e62010-06-07 12:46:40 +0000914 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000915 if (color_image == (Image *) NULL)
916 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000917 if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
cristye6365592010-04-02 17:31:23 +0000918 {
cristye6365592010-04-02 17:31:23 +0000919 color_image=DestroyImage(color_image);
920 return((Image *) NULL);
921 }
922 if (image->debug != MagickFalse)
923 {
924 char
925 format[MaxTextExtent],
926 *message;
927
928 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
929 " ColorMatrix image with color matrix:");
930 message=AcquireString("");
931 for (v=0; v < 6; v++)
932 {
933 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000934 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000935 (void) ConcatenateString(&message,format);
936 for (u=0; u < 6; u++)
937 {
cristyb51dff52011-05-19 16:55:47 +0000938 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
cristye6365592010-04-02 17:31:23 +0000939 ColorMatrix[v][u]);
940 (void) ConcatenateString(&message,format);
941 }
942 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
943 }
944 message=DestroyString(message);
945 }
946 /*
947 ColorMatrix image.
948 */
949 status=MagickTrue;
950 progress=0;
951 image_view=AcquireCacheView(image);
952 color_view=AcquireCacheView(color_image);
953#if defined(MAGICKCORE_OPENMP_SUPPORT)
954 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
955#endif
cristybb503372010-05-27 20:51:26 +0000956 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000957 {
958 MagickRealType
959 pixel;
960
cristy4c08aed2011-07-01 19:47:50 +0000961 register const Quantum
cristye6365592010-04-02 17:31:23 +0000962 *restrict p;
963
cristy4c08aed2011-07-01 19:47:50 +0000964 register Quantum
965 *restrict q;
966
cristybb503372010-05-27 20:51:26 +0000967 register ssize_t
cristye6365592010-04-02 17:31:23 +0000968 x;
969
cristye6365592010-04-02 17:31:23 +0000970 if (status == MagickFalse)
971 continue;
972 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
973 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
974 exception);
cristy4c08aed2011-07-01 19:47:50 +0000975 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristye6365592010-04-02 17:31:23 +0000976 {
977 status=MagickFalse;
978 continue;
979 }
cristybb503372010-05-27 20:51:26 +0000980 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +0000981 {
cristybb503372010-05-27 20:51:26 +0000982 register ssize_t
cristye6365592010-04-02 17:31:23 +0000983 v;
984
cristybb503372010-05-27 20:51:26 +0000985 size_t
cristye6365592010-04-02 17:31:23 +0000986 height;
987
988 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +0000989 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +0000990 {
cristy4c08aed2011-07-01 19:47:50 +0000991 pixel=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
992 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +0000993 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000994 pixel+=ColorMatrix[v][3]*GetPixelBlack(image,p);
995 if (image->matte != MagickFalse)
996 pixel+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
cristye6365592010-04-02 17:31:23 +0000997 pixel+=QuantumRange*ColorMatrix[v][5];
998 switch (v)
999 {
cristy4c08aed2011-07-01 19:47:50 +00001000 case 0: SetPixelRed(color_image,ClampToQuantum(pixel),q); break;
1001 case 1: SetPixelGreen(color_image,ClampToQuantum(pixel),q); break;
1002 case 2: SetPixelBlue(color_image,ClampToQuantum(pixel),q); break;
cristye6365592010-04-02 17:31:23 +00001003 case 3:
1004 {
cristy4c08aed2011-07-01 19:47:50 +00001005 if (image->colorspace == CMYKColorspace)
1006 SetPixelBlack(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001007 break;
1008 }
1009 case 4:
1010 {
cristy4c08aed2011-07-01 19:47:50 +00001011 if (image->matte != MagickFalse)
1012 SetPixelAlpha(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001013 break;
1014 }
1015 }
1016 }
cristyed231572011-07-14 02:18:59 +00001017 p+=GetPixelChannels(image);
1018 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +00001019 }
1020 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1021 status=MagickFalse;
1022 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1023 {
1024 MagickBooleanType
1025 proceed;
1026
1027#if defined(MAGICKCORE_OPENMP_SUPPORT)
1028 #pragma omp critical (MagickCore_ColorMatrixImage)
1029#endif
1030 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1031 image->rows);
1032 if (proceed == MagickFalse)
1033 status=MagickFalse;
1034 }
1035 }
1036 color_view=DestroyCacheView(color_view);
1037 image_view=DestroyCacheView(image_view);
1038 if (status == MagickFalse)
1039 color_image=DestroyImage(color_image);
1040 return(color_image);
1041}
1042
1043/*
1044%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1045% %
1046% %
1047% %
cristy3ed852e2009-09-05 21:47:34 +00001048+ D e s t r o y F x I n f o %
1049% %
1050% %
1051% %
1052%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1053%
1054% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1055%
1056% The format of the DestroyFxInfo method is:
1057%
1058% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1059%
1060% A description of each parameter follows:
1061%
1062% o fx_info: the fx info.
1063%
1064*/
1065MagickExport FxInfo *DestroyFxInfo(FxInfo *fx_info)
1066{
cristybb503372010-05-27 20:51:26 +00001067 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001068 i;
1069
1070 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1071 fx_info->expression=DestroyString(fx_info->expression);
1072 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1073 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001074 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001075 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1076 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001077 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1078 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1079 return(fx_info);
1080}
1081
1082/*
1083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1084% %
1085% %
1086% %
cristy3ed852e2009-09-05 21:47:34 +00001087+ F x E v a l u a t e C h a n n e l E x p r e s s i o n %
1088% %
1089% %
1090% %
1091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1092%
1093% FxEvaluateChannelExpression() evaluates an expression and returns the
1094% results.
1095%
1096% The format of the FxEvaluateExpression method is:
1097%
1098% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001099% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001100% MagickRealType *alpha,Exceptioninfo *exception)
1101% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1102% MagickRealType *alpha,Exceptioninfo *exception)
1103%
1104% A description of each parameter follows:
1105%
1106% o fx_info: the fx info.
1107%
1108% o channel: the channel.
1109%
1110% o x,y: the pixel position.
1111%
1112% o alpha: the result.
1113%
1114% o exception: return any errors or warnings in this structure.
1115%
1116*/
1117
cristy351842f2010-03-07 15:27:38 +00001118static inline double MagickMax(const double x,const double y)
1119{
1120 if (x > y)
1121 return(x);
1122 return(y);
1123}
1124
1125static inline double MagickMin(const double x,const double y)
1126{
1127 if (x < y)
1128 return(x);
1129 return(y);
1130}
1131
cristy3ed852e2009-09-05 21:47:34 +00001132static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001133 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001134{
1135 char
1136 key[MaxTextExtent],
1137 statistic[MaxTextExtent];
1138
1139 const char
1140 *value;
1141
1142 register const char
1143 *p;
1144
1145 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1146 if (*p == '.')
1147 switch (*++p) /* e.g. depth.r */
1148 {
cristy541ae572011-08-05 19:08:59 +00001149 case 'r': channel=RedPixelChannel; break;
1150 case 'g': channel=GreenPixelChannel; break;
1151 case 'b': channel=BluePixelChannel; break;
1152 case 'c': channel=CyanPixelChannel; break;
1153 case 'm': channel=MagentaPixelChannel; break;
1154 case 'y': channel=YellowPixelChannel; break;
1155 case 'k': channel=BlackPixelChannel; break;
cristy3ed852e2009-09-05 21:47:34 +00001156 default: break;
1157 }
cristyb51dff52011-05-19 16:55:47 +00001158 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001159 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001160 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1161 if (value != (const char *) NULL)
cristyc1acd842011-05-19 23:05:47 +00001162 return(QuantumScale*InterpretLocaleValue(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001163 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1164 if (LocaleNCompare(symbol,"depth",5) == 0)
1165 {
cristybb503372010-05-27 20:51:26 +00001166 size_t
cristy3ed852e2009-09-05 21:47:34 +00001167 depth;
1168
cristyfefab1b2011-07-05 00:33:22 +00001169 depth=GetImageDepth(image,exception);
1170 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001171 }
1172 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1173 {
1174 double
1175 kurtosis,
1176 skewness;
1177
cristyd42d9952011-07-08 14:21:50 +00001178 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001179 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001180 }
1181 if (LocaleNCompare(symbol,"maxima",6) == 0)
1182 {
1183 double
1184 maxima,
1185 minima;
1186
cristyd42d9952011-07-08 14:21:50 +00001187 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001188 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001189 }
1190 if (LocaleNCompare(symbol,"mean",4) == 0)
1191 {
1192 double
1193 mean,
1194 standard_deviation;
1195
cristyd42d9952011-07-08 14:21:50 +00001196 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001197 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001198 }
1199 if (LocaleNCompare(symbol,"minima",6) == 0)
1200 {
1201 double
1202 maxima,
1203 minima;
1204
cristyd42d9952011-07-08 14:21:50 +00001205 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001206 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001207 }
1208 if (LocaleNCompare(symbol,"skewness",8) == 0)
1209 {
1210 double
1211 kurtosis,
1212 skewness;
1213
cristyd42d9952011-07-08 14:21:50 +00001214 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001215 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001216 }
1217 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1218 {
1219 double
1220 mean,
1221 standard_deviation;
1222
cristyd42d9952011-07-08 14:21:50 +00001223 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001224 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001225 standard_deviation);
1226 }
1227 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1228 ConstantString(statistic));
cristyc1acd842011-05-19 23:05:47 +00001229 return(QuantumScale*InterpretLocaleValue(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001230}
1231
1232static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001233 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001234 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001235
cristy0568ffc2011-07-25 16:54:14 +00001236static inline MagickRealType FxMax(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001237 const ssize_t x,const ssize_t y,const char *expression,
1238 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001239{
1240 MagickRealType
1241 alpha,
1242 beta;
1243
1244 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1245 return((MagickRealType) MagickMax((double) alpha,(double) beta));
1246}
1247
cristy0568ffc2011-07-25 16:54:14 +00001248static inline MagickRealType FxMin(FxInfo *fx_info,PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001249 const ssize_t x,const ssize_t y,const char *expression,
1250 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001251{
1252 MagickRealType
1253 alpha,
1254 beta;
1255
1256 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1257 return((MagickRealType) MagickMin((double) alpha,(double) beta));
1258}
1259
1260static inline const char *FxSubexpression(const char *expression,
1261 ExceptionInfo *exception)
1262{
1263 const char
1264 *subexpression;
1265
cristybb503372010-05-27 20:51:26 +00001266 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001267 level;
1268
1269 level=0;
1270 subexpression=expression;
1271 while ((*subexpression != '\0') &&
1272 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1273 {
1274 if (strchr("(",(int) *subexpression) != (char *) NULL)
1275 level++;
1276 else
1277 if (strchr(")",(int) *subexpression) != (char *) NULL)
1278 level--;
1279 subexpression++;
1280 }
1281 if (*subexpression == '\0')
1282 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1283 "UnbalancedParenthesis","`%s'",expression);
1284 return(subexpression);
1285}
1286
cristy0568ffc2011-07-25 16:54:14 +00001287static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001288 const ssize_t x,const ssize_t y,const char *expression,
1289 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001290{
1291 char
1292 *q,
1293 subexpression[MaxTextExtent],
1294 symbol[MaxTextExtent];
1295
1296 const char
1297 *p,
1298 *value;
1299
1300 Image
1301 *image;
1302
cristy4c08aed2011-07-01 19:47:50 +00001303 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001304 pixel;
1305
1306 MagickRealType
1307 alpha,
1308 beta;
1309
1310 PointInfo
1311 point;
1312
cristybb503372010-05-27 20:51:26 +00001313 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001314 i;
1315
1316 size_t
1317 length;
1318
cristybb503372010-05-27 20:51:26 +00001319 size_t
cristy3ed852e2009-09-05 21:47:34 +00001320 level;
1321
1322 p=expression;
1323 i=GetImageIndexInList(fx_info->images);
1324 level=0;
1325 point.x=(double) x;
1326 point.y=(double) y;
1327 if (isalpha((int) *(p+1)) == 0)
1328 {
1329 if (strchr("suv",(int) *p) != (char *) NULL)
1330 {
1331 switch (*p)
1332 {
1333 case 's':
1334 default:
1335 {
1336 i=GetImageIndexInList(fx_info->images);
1337 break;
1338 }
1339 case 'u': i=0; break;
1340 case 'v': i=1; break;
1341 }
1342 p++;
1343 if (*p == '[')
1344 {
1345 level++;
1346 q=subexpression;
1347 for (p++; *p != '\0'; )
1348 {
1349 if (*p == '[')
1350 level++;
1351 else
1352 if (*p == ']')
1353 {
1354 level--;
1355 if (level == 0)
1356 break;
1357 }
1358 *q++=(*p++);
1359 }
1360 *q='\0';
1361 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1362 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001363 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001364 p++;
1365 }
1366 if (*p == '.')
1367 p++;
1368 }
1369 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1370 {
1371 p++;
1372 if (*p == '{')
1373 {
1374 level++;
1375 q=subexpression;
1376 for (p++; *p != '\0'; )
1377 {
1378 if (*p == '{')
1379 level++;
1380 else
1381 if (*p == '}')
1382 {
1383 level--;
1384 if (level == 0)
1385 break;
1386 }
1387 *q++=(*p++);
1388 }
1389 *q='\0';
1390 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1391 &beta,exception);
1392 point.x=alpha;
1393 point.y=beta;
1394 p++;
1395 }
1396 else
1397 if (*p == '[')
1398 {
1399 level++;
1400 q=subexpression;
1401 for (p++; *p != '\0'; )
1402 {
1403 if (*p == '[')
1404 level++;
1405 else
1406 if (*p == ']')
1407 {
1408 level--;
1409 if (level == 0)
1410 break;
1411 }
1412 *q++=(*p++);
1413 }
1414 *q='\0';
1415 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1416 &beta,exception);
1417 point.x+=alpha;
1418 point.y+=beta;
1419 p++;
1420 }
1421 if (*p == '.')
1422 p++;
1423 }
1424 }
1425 length=GetImageListLength(fx_info->images);
1426 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001427 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001428 i%=length;
1429 image=GetImageFromList(fx_info->images,i);
1430 if (image == (Image *) NULL)
1431 {
1432 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1433 "NoSuchImage","`%s'",expression);
1434 return(0.0);
1435 }
cristy4c08aed2011-07-01 19:47:50 +00001436 GetPixelInfo(image,&pixel);
1437 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001438 point.x,point.y,&pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00001439 if ((strlen(p) > 2) &&
1440 (LocaleCompare(p,"intensity") != 0) &&
1441 (LocaleCompare(p,"luminance") != 0) &&
1442 (LocaleCompare(p,"hue") != 0) &&
1443 (LocaleCompare(p,"saturation") != 0) &&
1444 (LocaleCompare(p,"lightness") != 0))
1445 {
1446 char
1447 name[MaxTextExtent];
1448
1449 (void) CopyMagickString(name,p,MaxTextExtent);
1450 for (q=name+(strlen(name)-1); q > name; q--)
1451 {
1452 if (*q == ')')
1453 break;
1454 if (*q == '.')
1455 {
1456 *q='\0';
1457 break;
1458 }
1459 }
1460 if ((strlen(name) > 2) &&
1461 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1462 {
cristy4c08aed2011-07-01 19:47:50 +00001463 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001464 *color;
1465
cristy4c08aed2011-07-01 19:47:50 +00001466 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1467 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001468 {
1469 pixel=(*color);
1470 p+=strlen(name);
1471 }
1472 else
1473 if (QueryMagickColor(name,&pixel,fx_info->exception) != MagickFalse)
1474 {
1475 (void) AddValueToSplayTree(fx_info->colors,ConstantString(name),
cristy4c08aed2011-07-01 19:47:50 +00001476 ClonePixelInfo(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001477 p+=strlen(name);
1478 }
1479 }
1480 }
1481 (void) CopyMagickString(symbol,p,MaxTextExtent);
1482 StripString(symbol);
1483 if (*symbol == '\0')
1484 {
1485 switch (channel)
1486 {
cristy0568ffc2011-07-25 16:54:14 +00001487 case RedPixelChannel: return(QuantumScale*pixel.red);
1488 case GreenPixelChannel: return(QuantumScale*pixel.green);
1489 case BluePixelChannel: return(QuantumScale*pixel.blue);
1490 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001491 {
1492 if (image->colorspace != CMYKColorspace)
1493 {
1494 (void) ThrowMagickException(exception,GetMagickModule(),
1495 OptionError,"ColorSeparatedImageRequired","`%s'",
1496 image->filename);
1497 return(0.0);
1498 }
cristy4c08aed2011-07-01 19:47:50 +00001499 return(QuantumScale*pixel.black);
1500 }
cristy0568ffc2011-07-25 16:54:14 +00001501 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001502 {
1503 MagickRealType
1504 alpha;
1505
1506 if (pixel.matte == MagickFalse)
1507 return(1.0);
1508 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1509 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001510 }
cristyb3a73b52011-07-26 01:34:43 +00001511 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001512 {
cristy4c08aed2011-07-01 19:47:50 +00001513 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001514 }
cristy3ed852e2009-09-05 21:47:34 +00001515 default:
1516 break;
1517 }
1518 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1519 "UnableToParseExpression","`%s'",p);
1520 return(0.0);
1521 }
1522 switch (*symbol)
1523 {
1524 case 'A':
1525 case 'a':
1526 {
1527 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001528 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001529 break;
1530 }
1531 case 'B':
1532 case 'b':
1533 {
1534 if (LocaleCompare(symbol,"b") == 0)
1535 return(QuantumScale*pixel.blue);
1536 break;
1537 }
1538 case 'C':
1539 case 'c':
1540 {
1541 if (LocaleNCompare(symbol,"channel",7) == 0)
1542 {
1543 GeometryInfo
1544 channel_info;
1545
1546 MagickStatusType
1547 flags;
1548
1549 flags=ParseGeometry(symbol+7,&channel_info);
1550 if (image->colorspace == CMYKColorspace)
1551 switch (channel)
1552 {
cristy0568ffc2011-07-25 16:54:14 +00001553 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001554 {
1555 if ((flags & RhoValue) == 0)
1556 return(0.0);
1557 return(channel_info.rho);
1558 }
cristy0568ffc2011-07-25 16:54:14 +00001559 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001560 {
1561 if ((flags & SigmaValue) == 0)
1562 return(0.0);
1563 return(channel_info.sigma);
1564 }
cristy0568ffc2011-07-25 16:54:14 +00001565 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001566 {
1567 if ((flags & XiValue) == 0)
1568 return(0.0);
1569 return(channel_info.xi);
1570 }
cristy0568ffc2011-07-25 16:54:14 +00001571 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001572 {
1573 if ((flags & PsiValue) == 0)
1574 return(0.0);
1575 return(channel_info.psi);
1576 }
cristy0568ffc2011-07-25 16:54:14 +00001577 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001578 {
1579 if ((flags & ChiValue) == 0)
1580 return(0.0);
1581 return(channel_info.chi);
1582 }
1583 default:
1584 return(0.0);
1585 }
1586 switch (channel)
1587 {
cristy0568ffc2011-07-25 16:54:14 +00001588 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((flags & RhoValue) == 0)
1591 return(0.0);
1592 return(channel_info.rho);
1593 }
cristy0568ffc2011-07-25 16:54:14 +00001594 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001595 {
1596 if ((flags & SigmaValue) == 0)
1597 return(0.0);
1598 return(channel_info.sigma);
1599 }
cristy0568ffc2011-07-25 16:54:14 +00001600 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001601 {
1602 if ((flags & XiValue) == 0)
1603 return(0.0);
1604 return(channel_info.xi);
1605 }
cristy0568ffc2011-07-25 16:54:14 +00001606 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001607 {
1608 if ((flags & ChiValue) == 0)
1609 return(0.0);
1610 return(channel_info.chi);
1611 }
cristy0568ffc2011-07-25 16:54:14 +00001612 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001613 {
1614 if ((flags & PsiValue) == 0)
1615 return(0.0);
1616 return(channel_info.psi);
1617 }
cristy3ed852e2009-09-05 21:47:34 +00001618 default:
1619 return(0.0);
1620 }
1621 return(0.0);
1622 }
1623 if (LocaleCompare(symbol,"c") == 0)
1624 return(QuantumScale*pixel.red);
1625 break;
1626 }
1627 case 'D':
1628 case 'd':
1629 {
1630 if (LocaleNCompare(symbol,"depth",5) == 0)
1631 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1632 break;
1633 }
1634 case 'G':
1635 case 'g':
1636 {
1637 if (LocaleCompare(symbol,"g") == 0)
1638 return(QuantumScale*pixel.green);
1639 break;
1640 }
1641 case 'K':
1642 case 'k':
1643 {
1644 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1645 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1646 if (LocaleCompare(symbol,"k") == 0)
1647 {
1648 if (image->colorspace != CMYKColorspace)
1649 {
1650 (void) ThrowMagickException(exception,GetMagickModule(),
1651 OptionError,"ColorSeparatedImageRequired","`%s'",
1652 image->filename);
1653 return(0.0);
1654 }
cristy4c08aed2011-07-01 19:47:50 +00001655 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001656 }
1657 break;
1658 }
1659 case 'H':
1660 case 'h':
1661 {
1662 if (LocaleCompare(symbol,"h") == 0)
1663 return((MagickRealType) image->rows);
1664 if (LocaleCompare(symbol,"hue") == 0)
1665 {
1666 double
1667 hue,
1668 lightness,
1669 saturation;
1670
cristyce70c172010-01-07 17:15:30 +00001671 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1672 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001673 return(hue);
1674 }
1675 break;
1676 }
1677 case 'I':
1678 case 'i':
1679 {
1680 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1681 (LocaleCompare(symbol,"image.minima") == 0) ||
1682 (LocaleCompare(symbol,"image.maxima") == 0) ||
1683 (LocaleCompare(symbol,"image.mean") == 0) ||
1684 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1685 (LocaleCompare(symbol,"image.skewness") == 0) ||
1686 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1687 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1688 if (LocaleCompare(symbol,"image.resolution.x") == 0)
1689 return(image->x_resolution);
1690 if (LocaleCompare(symbol,"image.resolution.y") == 0)
1691 return(image->y_resolution);
1692 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001693 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001694 if (LocaleCompare(symbol,"i") == 0)
1695 return((MagickRealType) x);
1696 break;
1697 }
1698 case 'J':
1699 case 'j':
1700 {
1701 if (LocaleCompare(symbol,"j") == 0)
1702 return((MagickRealType) y);
1703 break;
1704 }
1705 case 'L':
1706 case 'l':
1707 {
1708 if (LocaleCompare(symbol,"lightness") == 0)
1709 {
1710 double
1711 hue,
1712 lightness,
1713 saturation;
1714
cristyce70c172010-01-07 17:15:30 +00001715 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1716 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001717 return(lightness);
1718 }
1719 if (LocaleCompare(symbol,"luminance") == 0)
1720 {
1721 double
1722 luminence;
1723
1724 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1725 return(QuantumScale*luminence);
1726 }
1727 break;
1728 }
1729 case 'M':
1730 case 'm':
1731 {
1732 if (LocaleNCompare(symbol,"maxima",6) == 0)
1733 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1734 if (LocaleNCompare(symbol,"mean",4) == 0)
1735 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1736 if (LocaleNCompare(symbol,"minima",6) == 0)
1737 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1738 if (LocaleCompare(symbol,"m") == 0)
1739 return(QuantumScale*pixel.blue);
1740 break;
1741 }
1742 case 'N':
1743 case 'n':
1744 {
1745 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001746 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001747 break;
1748 }
1749 case 'O':
1750 case 'o':
1751 {
1752 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001753 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001754 break;
1755 }
1756 case 'P':
1757 case 'p':
1758 {
1759 if (LocaleCompare(symbol,"page.height") == 0)
1760 return((MagickRealType) image->page.height);
1761 if (LocaleCompare(symbol,"page.width") == 0)
1762 return((MagickRealType) image->page.width);
1763 if (LocaleCompare(symbol,"page.x") == 0)
1764 return((MagickRealType) image->page.x);
1765 if (LocaleCompare(symbol,"page.y") == 0)
1766 return((MagickRealType) image->page.y);
1767 break;
1768 }
1769 case 'R':
1770 case 'r':
1771 {
1772 if (LocaleCompare(symbol,"resolution.x") == 0)
1773 return(image->x_resolution);
1774 if (LocaleCompare(symbol,"resolution.y") == 0)
1775 return(image->y_resolution);
1776 if (LocaleCompare(symbol,"r") == 0)
1777 return(QuantumScale*pixel.red);
1778 break;
1779 }
1780 case 'S':
1781 case 's':
1782 {
1783 if (LocaleCompare(symbol,"saturation") == 0)
1784 {
1785 double
1786 hue,
1787 lightness,
1788 saturation;
1789
cristyce70c172010-01-07 17:15:30 +00001790 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1791 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001792 return(saturation);
1793 }
1794 if (LocaleNCompare(symbol,"skewness",8) == 0)
1795 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1796 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1797 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1798 break;
1799 }
1800 case 'T':
1801 case 't':
1802 {
1803 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001804 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001805 break;
1806 }
1807 case 'W':
1808 case 'w':
1809 {
1810 if (LocaleCompare(symbol,"w") == 0)
1811 return((MagickRealType) image->columns);
1812 break;
1813 }
1814 case 'Y':
1815 case 'y':
1816 {
1817 if (LocaleCompare(symbol,"y") == 0)
1818 return(QuantumScale*pixel.green);
1819 break;
1820 }
1821 case 'Z':
1822 case 'z':
1823 {
1824 if (LocaleCompare(symbol,"z") == 0)
1825 {
1826 MagickRealType
1827 depth;
1828
cristyfefab1b2011-07-05 00:33:22 +00001829 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001830 return(depth);
1831 }
1832 break;
1833 }
1834 default:
1835 break;
1836 }
1837 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1838 if (value != (const char *) NULL)
cristyc1acd842011-05-19 23:05:47 +00001839 return((MagickRealType) InterpretLocaleValue(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001840 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1841 "UnableToParseExpression","`%s'",symbol);
1842 return(0.0);
1843}
1844
1845static const char *FxOperatorPrecedence(const char *expression,
1846 ExceptionInfo *exception)
1847{
1848 typedef enum
1849 {
1850 UndefinedPrecedence,
1851 NullPrecedence,
1852 BitwiseComplementPrecedence,
1853 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001854 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001855 MultiplyPrecedence,
1856 AdditionPrecedence,
1857 ShiftPrecedence,
1858 RelationalPrecedence,
1859 EquivalencyPrecedence,
1860 BitwiseAndPrecedence,
1861 BitwiseOrPrecedence,
1862 LogicalAndPrecedence,
1863 LogicalOrPrecedence,
1864 TernaryPrecedence,
1865 AssignmentPrecedence,
1866 CommaPrecedence,
1867 SeparatorPrecedence
1868 } FxPrecedence;
1869
1870 FxPrecedence
1871 precedence,
1872 target;
1873
1874 register const char
1875 *subexpression;
1876
1877 register int
1878 c;
1879
cristybb503372010-05-27 20:51:26 +00001880 size_t
cristy3ed852e2009-09-05 21:47:34 +00001881 level;
1882
1883 c=0;
1884 level=0;
1885 subexpression=(const char *) NULL;
1886 target=NullPrecedence;
1887 while (*expression != '\0')
1888 {
1889 precedence=UndefinedPrecedence;
1890 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1891 {
1892 expression++;
1893 continue;
1894 }
cristy488fa882010-03-01 22:34:24 +00001895 switch (*expression)
1896 {
1897 case 'A':
1898 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001899 {
cristyb33454f2011-08-03 02:10:45 +00001900#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001901 if (LocaleNCompare(expression,"acosh",5) == 0)
1902 {
1903 expression+=5;
1904 break;
1905 }
cristyb33454f2011-08-03 02:10:45 +00001906#endif
1907#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001908 if (LocaleNCompare(expression,"asinh",5) == 0)
1909 {
1910 expression+=5;
1911 break;
1912 }
cristyb33454f2011-08-03 02:10:45 +00001913#endif
1914#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001915 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001916 {
1917 expression+=5;
1918 break;
1919 }
cristyb33454f2011-08-03 02:10:45 +00001920#endif
cristy488fa882010-03-01 22:34:24 +00001921 break;
cristy3ed852e2009-09-05 21:47:34 +00001922 }
cristy488fa882010-03-01 22:34:24 +00001923 case 'J':
1924 case 'j':
1925 {
1926 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1927 (LocaleNCompare(expression,"j1",2) == 0))
1928 {
1929 expression+=2;
1930 break;
1931 }
1932 break;
1933 }
cristy2def9322010-06-18 23:59:37 +00001934 case '#':
1935 {
1936 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1937 expression++;
1938 break;
1939 }
cristy488fa882010-03-01 22:34:24 +00001940 default:
1941 break;
1942 }
cristy3ed852e2009-09-05 21:47:34 +00001943 if ((c == (int) '{') || (c == (int) '['))
1944 level++;
1945 else
1946 if ((c == (int) '}') || (c == (int) ']'))
1947 level--;
1948 if (level == 0)
1949 switch ((unsigned char) *expression)
1950 {
1951 case '~':
1952 case '!':
1953 {
1954 precedence=BitwiseComplementPrecedence;
1955 break;
1956 }
1957 case '^':
cristy6621e252010-08-13 00:42:57 +00001958 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001959 {
1960 precedence=ExponentPrecedence;
1961 break;
1962 }
1963 default:
1964 {
1965 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1966 (strchr(")",c) != (char *) NULL))) &&
1967 (((islower((int) ((char) *expression)) != 0) ||
1968 (strchr("(",(int) *expression) != (char *) NULL)) ||
1969 ((isdigit((int) ((char) c)) == 0) &&
1970 (isdigit((int) ((char) *expression)) != 0))) &&
1971 (strchr("xy",(int) *expression) == (char *) NULL))
1972 precedence=MultiplyPrecedence;
1973 break;
1974 }
1975 case '*':
1976 case '/':
1977 case '%':
1978 {
1979 precedence=MultiplyPrecedence;
1980 break;
1981 }
1982 case '+':
1983 case '-':
1984 {
1985 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1986 (isalpha(c) != 0))
1987 precedence=AdditionPrecedence;
1988 break;
1989 }
1990 case LeftShiftOperator:
1991 case RightShiftOperator:
1992 {
1993 precedence=ShiftPrecedence;
1994 break;
1995 }
1996 case '<':
1997 case LessThanEqualOperator:
1998 case GreaterThanEqualOperator:
1999 case '>':
2000 {
2001 precedence=RelationalPrecedence;
2002 break;
2003 }
2004 case EqualOperator:
2005 case NotEqualOperator:
2006 {
2007 precedence=EquivalencyPrecedence;
2008 break;
2009 }
2010 case '&':
2011 {
2012 precedence=BitwiseAndPrecedence;
2013 break;
2014 }
2015 case '|':
2016 {
2017 precedence=BitwiseOrPrecedence;
2018 break;
2019 }
2020 case LogicalAndOperator:
2021 {
2022 precedence=LogicalAndPrecedence;
2023 break;
2024 }
2025 case LogicalOrOperator:
2026 {
2027 precedence=LogicalOrPrecedence;
2028 break;
2029 }
cristy116af162010-08-13 01:25:47 +00002030 case ExponentialNotation:
2031 {
2032 precedence=ExponentialNotationPrecedence;
2033 break;
2034 }
cristy3ed852e2009-09-05 21:47:34 +00002035 case ':':
2036 case '?':
2037 {
2038 precedence=TernaryPrecedence;
2039 break;
2040 }
2041 case '=':
2042 {
2043 precedence=AssignmentPrecedence;
2044 break;
2045 }
2046 case ',':
2047 {
2048 precedence=CommaPrecedence;
2049 break;
2050 }
2051 case ';':
2052 {
2053 precedence=SeparatorPrecedence;
2054 break;
2055 }
2056 }
2057 if ((precedence == BitwiseComplementPrecedence) ||
2058 (precedence == TernaryPrecedence) ||
2059 (precedence == AssignmentPrecedence))
2060 {
2061 if (precedence > target)
2062 {
2063 /*
2064 Right-to-left associativity.
2065 */
2066 target=precedence;
2067 subexpression=expression;
2068 }
2069 }
2070 else
2071 if (precedence >= target)
2072 {
2073 /*
2074 Left-to-right associativity.
2075 */
2076 target=precedence;
2077 subexpression=expression;
2078 }
2079 if (strchr("(",(int) *expression) != (char *) NULL)
2080 expression=FxSubexpression(expression,exception);
2081 c=(int) (*expression++);
2082 }
2083 return(subexpression);
2084}
2085
2086static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002087 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002088 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002089{
2090 char
2091 *q,
2092 subexpression[MaxTextExtent];
2093
2094 MagickRealType
2095 alpha,
2096 gamma;
2097
2098 register const char
2099 *p;
2100
2101 *beta=0.0;
2102 if (exception->severity != UndefinedException)
2103 return(0.0);
2104 while (isspace((int) *expression) != 0)
2105 expression++;
2106 if (*expression == '\0')
2107 {
2108 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2109 "MissingExpression","`%s'",expression);
2110 return(0.0);
2111 }
cristy66322f02010-05-17 11:40:48 +00002112 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002113 p=FxOperatorPrecedence(expression,exception);
2114 if (p != (const char *) NULL)
2115 {
2116 (void) CopyMagickString(subexpression,expression,(size_t)
2117 (p-expression+1));
2118 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2119 exception);
2120 switch ((unsigned char) *p)
2121 {
2122 case '~':
2123 {
2124 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002125 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002126 return(*beta);
2127 }
2128 case '!':
2129 {
2130 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2131 return(*beta == 0.0 ? 1.0 : 0.0);
2132 }
2133 case '^':
2134 {
2135 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2136 channel,x,y,++p,beta,exception));
2137 return(*beta);
2138 }
2139 case '*':
cristy116af162010-08-13 01:25:47 +00002140 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002141 {
2142 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2143 return(alpha*(*beta));
2144 }
2145 case '/':
2146 {
2147 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2148 if (*beta == 0.0)
2149 {
2150 if (exception->severity == UndefinedException)
2151 (void) ThrowMagickException(exception,GetMagickModule(),
2152 OptionError,"DivideByZero","`%s'",expression);
2153 return(0.0);
2154 }
2155 return(alpha/(*beta));
2156 }
2157 case '%':
2158 {
2159 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2160 *beta=fabs(floor(((double) *beta)+0.5));
2161 if (*beta == 0.0)
2162 {
2163 (void) ThrowMagickException(exception,GetMagickModule(),
2164 OptionError,"DivideByZero","`%s'",expression);
2165 return(0.0);
2166 }
2167 return(fmod((double) alpha,(double) *beta));
2168 }
2169 case '+':
2170 {
2171 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2172 return(alpha+(*beta));
2173 }
2174 case '-':
2175 {
2176 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2177 return(alpha-(*beta));
2178 }
2179 case LeftShiftOperator:
2180 {
2181 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002182 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002183 (gamma+0.5));
2184 return(*beta);
2185 }
2186 case RightShiftOperator:
2187 {
2188 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002189 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002190 (gamma+0.5));
2191 return(*beta);
2192 }
2193 case '<':
2194 {
2195 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2196 return(alpha < *beta ? 1.0 : 0.0);
2197 }
2198 case LessThanEqualOperator:
2199 {
2200 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2201 return(alpha <= *beta ? 1.0 : 0.0);
2202 }
2203 case '>':
2204 {
2205 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2206 return(alpha > *beta ? 1.0 : 0.0);
2207 }
2208 case GreaterThanEqualOperator:
2209 {
2210 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2211 return(alpha >= *beta ? 1.0 : 0.0);
2212 }
2213 case EqualOperator:
2214 {
2215 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2216 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2217 }
2218 case NotEqualOperator:
2219 {
2220 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2221 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2222 }
2223 case '&':
2224 {
2225 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002226 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002227 (gamma+0.5));
2228 return(*beta);
2229 }
2230 case '|':
2231 {
2232 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002233 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002234 (gamma+0.5));
2235 return(*beta);
2236 }
2237 case LogicalAndOperator:
2238 {
2239 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2240 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2241 return(*beta);
2242 }
2243 case LogicalOrOperator:
2244 {
2245 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2246 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2247 return(*beta);
2248 }
2249 case '?':
2250 {
2251 MagickRealType
2252 gamma;
2253
2254 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2255 q=subexpression;
2256 p=StringToken(":",&q);
2257 if (q == (char *) NULL)
2258 {
2259 (void) ThrowMagickException(exception,GetMagickModule(),
2260 OptionError,"UnableToParseExpression","`%s'",subexpression);
2261 return(0.0);
2262 }
2263 if (fabs((double) alpha) > MagickEpsilon)
2264 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2265 else
2266 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2267 return(gamma);
2268 }
2269 case '=':
2270 {
2271 char
2272 numeric[MaxTextExtent];
2273
2274 q=subexpression;
2275 while (isalpha((int) ((unsigned char) *q)) != 0)
2276 q++;
2277 if (*q != '\0')
2278 {
2279 (void) ThrowMagickException(exception,GetMagickModule(),
2280 OptionError,"UnableToParseExpression","`%s'",subexpression);
2281 return(0.0);
2282 }
2283 ClearMagickException(exception);
2284 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002285 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002286 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002287 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2288 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2289 subexpression),ConstantString(numeric));
2290 return(*beta);
2291 }
2292 case ',':
2293 {
2294 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2295 return(alpha);
2296 }
2297 case ';':
2298 {
2299 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2300 return(*beta);
2301 }
2302 default:
2303 {
2304 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2305 exception);
2306 return(gamma);
2307 }
2308 }
2309 }
2310 if (strchr("(",(int) *expression) != (char *) NULL)
2311 {
2312 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2313 subexpression[strlen(subexpression)-1]='\0';
2314 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2315 exception);
2316 return(gamma);
2317 }
cristy8b8a3ae2010-10-23 18:49:46 +00002318 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002319 {
2320 case '+':
2321 {
2322 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2323 exception);
2324 return(1.0*gamma);
2325 }
2326 case '-':
2327 {
2328 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2329 exception);
2330 return(-1.0*gamma);
2331 }
2332 case '~':
2333 {
2334 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2335 exception);
cristybb503372010-05-27 20:51:26 +00002336 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002337 }
2338 case 'A':
2339 case 'a':
2340 {
2341 if (LocaleNCompare(expression,"abs",3) == 0)
2342 {
2343 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2344 exception);
2345 return((MagickRealType) fabs((double) alpha));
2346 }
cristyb33454f2011-08-03 02:10:45 +00002347#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002348 if (LocaleNCompare(expression,"acosh",5) == 0)
2349 {
2350 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2351 exception);
2352 return((MagickRealType) acosh((double) alpha));
2353 }
cristyb33454f2011-08-03 02:10:45 +00002354#endif
cristy3ed852e2009-09-05 21:47:34 +00002355 if (LocaleNCompare(expression,"acos",4) == 0)
2356 {
2357 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2358 exception);
2359 return((MagickRealType) acos((double) alpha));
2360 }
cristy43c22f42010-03-30 12:34:07 +00002361#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002362 if (LocaleNCompare(expression,"airy",4) == 0)
2363 {
2364 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2365 exception);
2366 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002367 return(1.0);
2368 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002369 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002370 }
cristy43c22f42010-03-30 12:34:07 +00002371#endif
cristyb33454f2011-08-03 02:10:45 +00002372#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002373 if (LocaleNCompare(expression,"asinh",5) == 0)
2374 {
2375 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2376 exception);
2377 return((MagickRealType) asinh((double) alpha));
2378 }
cristyb33454f2011-08-03 02:10:45 +00002379#endif
cristy3ed852e2009-09-05 21:47:34 +00002380 if (LocaleNCompare(expression,"asin",4) == 0)
2381 {
2382 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2383 exception);
2384 return((MagickRealType) asin((double) alpha));
2385 }
2386 if (LocaleNCompare(expression,"alt",3) == 0)
2387 {
2388 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2389 exception);
cristybb503372010-05-27 20:51:26 +00002390 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002391 }
2392 if (LocaleNCompare(expression,"atan2",5) == 0)
2393 {
2394 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2395 exception);
2396 return((MagickRealType) atan2((double) alpha,(double) *beta));
2397 }
cristyb33454f2011-08-03 02:10:45 +00002398#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002399 if (LocaleNCompare(expression,"atanh",5) == 0)
2400 {
2401 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2402 exception);
2403 return((MagickRealType) atanh((double) alpha));
2404 }
cristyb33454f2011-08-03 02:10:45 +00002405#endif
cristy3ed852e2009-09-05 21:47:34 +00002406 if (LocaleNCompare(expression,"atan",4) == 0)
2407 {
2408 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2409 exception);
2410 return((MagickRealType) atan((double) alpha));
2411 }
2412 if (LocaleCompare(expression,"a") == 0)
2413 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2414 break;
2415 }
2416 case 'B':
2417 case 'b':
2418 {
2419 if (LocaleCompare(expression,"b") == 0)
2420 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2421 break;
2422 }
2423 case 'C':
2424 case 'c':
2425 {
2426 if (LocaleNCompare(expression,"ceil",4) == 0)
2427 {
2428 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2429 exception);
2430 return((MagickRealType) ceil((double) alpha));
2431 }
2432 if (LocaleNCompare(expression,"cosh",4) == 0)
2433 {
2434 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2435 exception);
2436 return((MagickRealType) cosh((double) alpha));
2437 }
2438 if (LocaleNCompare(expression,"cos",3) == 0)
2439 {
2440 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2441 exception);
2442 return((MagickRealType) cos((double) alpha));
2443 }
2444 if (LocaleCompare(expression,"c") == 0)
2445 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2446 break;
2447 }
2448 case 'D':
2449 case 'd':
2450 {
2451 if (LocaleNCompare(expression,"debug",5) == 0)
2452 {
2453 const char
2454 *type;
2455
2456 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2457 exception);
2458 if (fx_info->images->colorspace == CMYKColorspace)
2459 switch (channel)
2460 {
cristy0568ffc2011-07-25 16:54:14 +00002461 case CyanPixelChannel: type="cyan"; break;
2462 case MagentaPixelChannel: type="magenta"; break;
2463 case YellowPixelChannel: type="yellow"; break;
2464 case AlphaPixelChannel: type="opacity"; break;
2465 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002466 default: type="unknown"; break;
2467 }
2468 else
2469 switch (channel)
2470 {
cristy0568ffc2011-07-25 16:54:14 +00002471 case RedPixelChannel: type="red"; break;
2472 case GreenPixelChannel: type="green"; break;
2473 case BluePixelChannel: type="blue"; break;
2474 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002475 default: type="unknown"; break;
2476 }
2477 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2478 if (strlen(subexpression) > 1)
2479 subexpression[strlen(subexpression)-1]='\0';
2480 if (fx_info->file != (FILE *) NULL)
cristy1e604812011-05-19 18:07:50 +00002481 (void) FormatLocaleFile(fx_info->file,
2482 "%s[%.20g,%.20g].%s: %s=%.*g\n",fx_info->images->filename,
2483 (double) x,(double) y,type,subexpression,GetMagickPrecision(),
2484 (double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002485 return(0.0);
2486 }
2487 break;
2488 }
2489 case 'E':
2490 case 'e':
2491 {
2492 if (LocaleCompare(expression,"epsilon") == 0)
2493 return((MagickRealType) MagickEpsilon);
2494 if (LocaleNCompare(expression,"exp",3) == 0)
2495 {
2496 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2497 exception);
2498 return((MagickRealType) exp((double) alpha));
2499 }
2500 if (LocaleCompare(expression,"e") == 0)
2501 return((MagickRealType) 2.7182818284590452354);
2502 break;
2503 }
2504 case 'F':
2505 case 'f':
2506 {
2507 if (LocaleNCompare(expression,"floor",5) == 0)
2508 {
2509 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2510 exception);
2511 return((MagickRealType) floor((double) alpha));
2512 }
2513 break;
2514 }
2515 case 'G':
2516 case 'g':
2517 {
2518 if (LocaleCompare(expression,"g") == 0)
2519 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2520 break;
2521 }
2522 case 'H':
2523 case 'h':
2524 {
2525 if (LocaleCompare(expression,"h") == 0)
2526 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2527 if (LocaleCompare(expression,"hue") == 0)
2528 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2529 if (LocaleNCompare(expression,"hypot",5) == 0)
2530 {
2531 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2532 exception);
2533 return((MagickRealType) hypot((double) alpha,(double) *beta));
2534 }
2535 break;
2536 }
2537 case 'K':
2538 case 'k':
2539 {
2540 if (LocaleCompare(expression,"k") == 0)
2541 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2542 break;
2543 }
2544 case 'I':
2545 case 'i':
2546 {
2547 if (LocaleCompare(expression,"intensity") == 0)
2548 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2549 if (LocaleNCompare(expression,"int",3) == 0)
2550 {
2551 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2552 exception);
cristy16788e42010-08-13 13:44:26 +00002553 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002554 }
2555 if (LocaleCompare(expression,"i") == 0)
2556 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2557 break;
2558 }
2559 case 'J':
2560 case 'j':
2561 {
2562 if (LocaleCompare(expression,"j") == 0)
2563 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002564#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002565 if (LocaleNCompare(expression,"j0",2) == 0)
2566 {
2567 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2568 exception);
2569 return((MagickRealType) j0((double) alpha));
2570 }
cristy161b9262010-03-20 19:34:32 +00002571#endif
2572#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002573 if (LocaleNCompare(expression,"j1",2) == 0)
2574 {
2575 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2576 exception);
2577 return((MagickRealType) j1((double) alpha));
2578 }
cristy161b9262010-03-20 19:34:32 +00002579#endif
cristyaa018fa2010-04-08 23:03:54 +00002580#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002581 if (LocaleNCompare(expression,"jinc",4) == 0)
2582 {
2583 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2584 exception);
cristy0946a822010-03-12 17:14:58 +00002585 if (alpha == 0.0)
2586 return(1.0);
cristy69928f92010-03-12 13:27:09 +00002587 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/
cristyfce2f7b2010-03-12 00:29:49 +00002588 (MagickPI*alpha));
2589 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002590 }
cristyaa018fa2010-04-08 23:03:54 +00002591#endif
cristy3ed852e2009-09-05 21:47:34 +00002592 break;
2593 }
2594 case 'L':
2595 case 'l':
2596 {
2597 if (LocaleNCompare(expression,"ln",2) == 0)
2598 {
2599 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2600 exception);
2601 return((MagickRealType) log((double) alpha));
2602 }
cristyc8ed5322010-08-31 12:07:59 +00002603 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002604 {
cristyc8ed5322010-08-31 12:07:59 +00002605 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002606 exception);
2607 return((MagickRealType) log10((double) alpha))/log10(2.0);
2608 }
2609 if (LocaleNCompare(expression,"log",3) == 0)
2610 {
2611 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2612 exception);
2613 return((MagickRealType) log10((double) alpha));
2614 }
2615 if (LocaleCompare(expression,"lightness") == 0)
2616 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2617 break;
2618 }
2619 case 'M':
2620 case 'm':
2621 {
2622 if (LocaleCompare(expression,"MaxRGB") == 0)
2623 return((MagickRealType) QuantumRange);
2624 if (LocaleNCompare(expression,"maxima",6) == 0)
2625 break;
2626 if (LocaleNCompare(expression,"max",3) == 0)
2627 return(FxMax(fx_info,channel,x,y,expression+3,exception));
2628 if (LocaleNCompare(expression,"minima",6) == 0)
2629 break;
2630 if (LocaleNCompare(expression,"min",3) == 0)
2631 return(FxMin(fx_info,channel,x,y,expression+3,exception));
2632 if (LocaleNCompare(expression,"mod",3) == 0)
2633 {
2634 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2635 exception);
2636 return((MagickRealType) fmod((double) alpha,(double) *beta));
2637 }
2638 if (LocaleCompare(expression,"m") == 0)
2639 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2640 break;
2641 }
2642 case 'N':
2643 case 'n':
2644 {
2645 if (LocaleCompare(expression,"n") == 0)
2646 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2647 break;
2648 }
2649 case 'O':
2650 case 'o':
2651 {
2652 if (LocaleCompare(expression,"Opaque") == 0)
2653 return(1.0);
2654 if (LocaleCompare(expression,"o") == 0)
2655 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2656 break;
2657 }
2658 case 'P':
2659 case 'p':
2660 {
2661 if (LocaleCompare(expression,"pi") == 0)
2662 return((MagickRealType) MagickPI);
2663 if (LocaleNCompare(expression,"pow",3) == 0)
2664 {
2665 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2666 exception);
2667 return((MagickRealType) pow((double) alpha,(double) *beta));
2668 }
2669 if (LocaleCompare(expression,"p") == 0)
2670 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2671 break;
2672 }
2673 case 'Q':
2674 case 'q':
2675 {
2676 if (LocaleCompare(expression,"QuantumRange") == 0)
2677 return((MagickRealType) QuantumRange);
2678 if (LocaleCompare(expression,"QuantumScale") == 0)
2679 return((MagickRealType) QuantumScale);
2680 break;
2681 }
2682 case 'R':
2683 case 'r':
2684 {
2685 if (LocaleNCompare(expression,"rand",4) == 0)
2686 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2687 if (LocaleNCompare(expression,"round",5) == 0)
2688 {
2689 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2690 exception);
cristy16788e42010-08-13 13:44:26 +00002691 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002692 }
2693 if (LocaleCompare(expression,"r") == 0)
2694 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2695 break;
2696 }
2697 case 'S':
2698 case 's':
2699 {
2700 if (LocaleCompare(expression,"saturation") == 0)
2701 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2702 if (LocaleNCompare(expression,"sign",4) == 0)
2703 {
2704 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2705 exception);
2706 return(alpha < 0.0 ? -1.0 : 1.0);
2707 }
cristya6a09e72010-03-02 14:51:02 +00002708 if (LocaleNCompare(expression,"sinc",4) == 0)
2709 {
2710 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2711 exception);
2712 if (alpha == 0)
2713 return(1.0);
2714 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2715 (MagickPI*alpha));
2716 return(gamma);
2717 }
cristy3ed852e2009-09-05 21:47:34 +00002718 if (LocaleNCompare(expression,"sinh",4) == 0)
2719 {
2720 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2721 exception);
2722 return((MagickRealType) sinh((double) alpha));
2723 }
2724 if (LocaleNCompare(expression,"sin",3) == 0)
2725 {
2726 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2727 exception);
2728 return((MagickRealType) sin((double) alpha));
2729 }
2730 if (LocaleNCompare(expression,"sqrt",4) == 0)
2731 {
2732 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2733 exception);
2734 return((MagickRealType) sqrt((double) alpha));
2735 }
2736 if (LocaleCompare(expression,"s") == 0)
2737 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2738 break;
2739 }
2740 case 'T':
2741 case 't':
2742 {
2743 if (LocaleNCompare(expression,"tanh",4) == 0)
2744 {
2745 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2746 exception);
2747 return((MagickRealType) tanh((double) alpha));
2748 }
2749 if (LocaleNCompare(expression,"tan",3) == 0)
2750 {
2751 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2752 exception);
2753 return((MagickRealType) tan((double) alpha));
2754 }
2755 if (LocaleCompare(expression,"Transparent") == 0)
2756 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002757 if (LocaleNCompare(expression,"trunc",5) == 0)
2758 {
2759 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2760 exception);
2761 if (alpha >= 0.0)
2762 return((MagickRealType) floor((double) alpha));
2763 return((MagickRealType) ceil((double) alpha));
2764 }
cristy3ed852e2009-09-05 21:47:34 +00002765 if (LocaleCompare(expression,"t") == 0)
2766 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2767 break;
2768 }
2769 case 'U':
2770 case 'u':
2771 {
2772 if (LocaleCompare(expression,"u") == 0)
2773 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2774 break;
2775 }
2776 case 'V':
2777 case 'v':
2778 {
2779 if (LocaleCompare(expression,"v") == 0)
2780 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2781 break;
2782 }
2783 case 'W':
2784 case 'w':
2785 {
2786 if (LocaleCompare(expression,"w") == 0)
2787 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2788 break;
2789 }
2790 case 'Y':
2791 case 'y':
2792 {
2793 if (LocaleCompare(expression,"y") == 0)
2794 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2795 break;
2796 }
2797 case 'Z':
2798 case 'z':
2799 {
2800 if (LocaleCompare(expression,"z") == 0)
2801 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2802 break;
2803 }
2804 default:
2805 break;
2806 }
2807 q=(char *) expression;
cristyc1acd842011-05-19 23:05:47 +00002808 alpha=InterpretLocaleValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002809 if (q == expression)
2810 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2811 return(alpha);
2812}
2813
2814MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2815 MagickRealType *alpha,ExceptionInfo *exception)
2816{
2817 MagickBooleanType
2818 status;
2819
cristy541ae572011-08-05 19:08:59 +00002820 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2821 exception);
cristy3ed852e2009-09-05 21:47:34 +00002822 return(status);
2823}
2824
2825MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2826 MagickRealType *alpha,ExceptionInfo *exception)
2827{
2828 FILE
2829 *file;
2830
2831 MagickBooleanType
2832 status;
2833
2834 file=fx_info->file;
2835 fx_info->file=(FILE *) NULL;
cristy541ae572011-08-05 19:08:59 +00002836 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2837 exception);
cristy3ed852e2009-09-05 21:47:34 +00002838 fx_info->file=file;
2839 return(status);
2840}
2841
2842MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002843 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002844 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002845{
2846 MagickRealType
2847 beta;
2848
2849 beta=0.0;
2850 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2851 exception);
2852 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2853}
2854
2855/*
2856%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2857% %
2858% %
2859% %
2860% F x I m a g e %
2861% %
2862% %
2863% %
2864%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2865%
2866% FxImage() applies a mathematical expression to the specified image.
2867%
2868% The format of the FxImage method is:
2869%
2870% Image *FxImage(const Image *image,const char *expression,
2871% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002872%
2873% A description of each parameter follows:
2874%
2875% o image: the image.
2876%
cristy3ed852e2009-09-05 21:47:34 +00002877% o expression: A mathematical expression.
2878%
2879% o exception: return any errors or warnings in this structure.
2880%
2881*/
2882
2883static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2884{
cristybb503372010-05-27 20:51:26 +00002885 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002886 i;
2887
2888 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002889 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002890 if (fx_info[i] != (FxInfo *) NULL)
2891 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002892 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002893 return(fx_info);
2894}
2895
2896static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2897 ExceptionInfo *exception)
2898{
2899 char
2900 *fx_expression;
2901
2902 FxInfo
2903 **fx_info;
2904
2905 MagickRealType
2906 alpha;
2907
cristybb503372010-05-27 20:51:26 +00002908 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002909 i;
2910
cristybb503372010-05-27 20:51:26 +00002911 size_t
cristy3ed852e2009-09-05 21:47:34 +00002912 number_threads;
2913
2914 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002915 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002916 if (fx_info == (FxInfo **) NULL)
2917 return((FxInfo **) NULL);
2918 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2919 if (*expression != '@')
2920 fx_expression=ConstantString(expression);
2921 else
2922 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00002923 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002924 {
2925 fx_info[i]=AcquireFxInfo(image,fx_expression);
2926 if (fx_info[i] == (FxInfo *) NULL)
2927 return(DestroyFxThreadSet(fx_info));
2928 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2929 }
2930 fx_expression=DestroyString(fx_expression);
2931 return(fx_info);
2932}
2933
2934MagickExport Image *FxImage(const Image *image,const char *expression,
2935 ExceptionInfo *exception)
2936{
cristy3ed852e2009-09-05 21:47:34 +00002937#define FxImageTag "Fx/Image"
2938
cristyfa112112010-01-04 17:48:07 +00002939 CacheView
cristy79cedc72011-07-25 00:41:15 +00002940 *fx_view,
2941 *image_view;
cristyfa112112010-01-04 17:48:07 +00002942
cristy3ed852e2009-09-05 21:47:34 +00002943 FxInfo
cristyfa112112010-01-04 17:48:07 +00002944 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00002945
2946 Image
2947 *fx_image;
2948
cristy3ed852e2009-09-05 21:47:34 +00002949 MagickBooleanType
2950 status;
2951
cristybb503372010-05-27 20:51:26 +00002952 MagickOffsetType
2953 progress;
2954
cristy3ed852e2009-09-05 21:47:34 +00002955 MagickRealType
2956 alpha;
2957
cristybb503372010-05-27 20:51:26 +00002958 ssize_t
2959 y;
2960
cristy3ed852e2009-09-05 21:47:34 +00002961 assert(image != (Image *) NULL);
2962 assert(image->signature == MagickSignature);
2963 if (image->debug != MagickFalse)
2964 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00002965 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00002966 if (fx_image == (Image *) NULL)
2967 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00002968 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002969 {
cristy3ed852e2009-09-05 21:47:34 +00002970 fx_image=DestroyImage(fx_image);
2971 return((Image *) NULL);
2972 }
2973 fx_info=AcquireFxThreadSet(image,expression,exception);
2974 if (fx_info == (FxInfo **) NULL)
2975 {
2976 fx_image=DestroyImage(fx_image);
2977 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2978 }
2979 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
2980 if (status == MagickFalse)
2981 {
2982 fx_image=DestroyImage(fx_image);
2983 fx_info=DestroyFxThreadSet(fx_info);
2984 return((Image *) NULL);
2985 }
2986 /*
2987 Fx image.
2988 */
2989 status=MagickTrue;
2990 progress=0;
cristy79cedc72011-07-25 00:41:15 +00002991 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00002992 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00002993#if defined(MAGICKCORE_OPENMP_SUPPORT)
2994 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002995#endif
cristybb503372010-05-27 20:51:26 +00002996 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002997 {
cristy5c9e6f22010-09-17 17:31:01 +00002998 const int
2999 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003000
cristy79cedc72011-07-25 00:41:15 +00003001 register const Quantum
3002 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003003
cristy4c08aed2011-07-01 19:47:50 +00003004 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003005 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003006
cristy79cedc72011-07-25 00:41:15 +00003007 register ssize_t
3008 x;
3009
cristy3ed852e2009-09-05 21:47:34 +00003010 if (status == MagickFalse)
3011 continue;
cristy79cedc72011-07-25 00:41:15 +00003012 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00003013 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003014 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003015 {
3016 status=MagickFalse;
3017 continue;
3018 }
cristybb503372010-05-27 20:51:26 +00003019 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003020 {
cristy79cedc72011-07-25 00:41:15 +00003021 register ssize_t
3022 i;
3023
3024 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3025 {
3026 MagickRealType
3027 alpha;
3028
3029 PixelChannel
3030 channel;
3031
3032 PixelTrait
3033 fx_traits,
3034 traits;
3035
3036 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3037 if (traits == UndefinedPixelTrait)
3038 continue;
3039 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3040 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
3041 if (fx_traits == UndefinedPixelTrait)
3042 continue;
3043 if ((fx_traits & CopyPixelTrait) != 0)
3044 {
3045 q[channel]=p[i];
3046 continue;
3047 }
3048 alpha=0.0;
cristy0568ffc2011-07-25 16:54:14 +00003049 (void) FxEvaluateChannelExpression(fx_info[id],(PixelChannel) i,x,y,
3050 &alpha,exception);
cristyb3a73b52011-07-26 01:34:43 +00003051 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003052 }
3053 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003054 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003055 }
3056 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3057 status=MagickFalse;
3058 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3059 {
3060 MagickBooleanType
3061 proceed;
3062
cristyb5d5f722009-11-04 03:03:49 +00003063#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy490408a2011-07-07 14:42:05 +00003064 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003065#endif
3066 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3067 if (proceed == MagickFalse)
3068 status=MagickFalse;
3069 }
3070 }
cristy3ed852e2009-09-05 21:47:34 +00003071 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003072 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003073 fx_info=DestroyFxThreadSet(fx_info);
3074 if (status == MagickFalse)
3075 fx_image=DestroyImage(fx_image);
3076 return(fx_image);
3077}
3078
3079/*
3080%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3081% %
3082% %
3083% %
3084% I m p l o d e I m a g e %
3085% %
3086% %
3087% %
3088%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3089%
3090% ImplodeImage() creates a new image that is a copy of an existing
3091% one with the image pixels "implode" by the specified percentage. It
3092% allocates the memory necessary for the new Image structure and returns a
3093% pointer to the new image.
3094%
3095% The format of the ImplodeImage method is:
3096%
3097% Image *ImplodeImage(const Image *image,const double amount,
3098% ExceptionInfo *exception)
3099%
3100% A description of each parameter follows:
3101%
3102% o implode_image: Method ImplodeImage returns a pointer to the image
3103% after it is implode. A null image is returned if there is a memory
3104% shortage.
3105%
3106% o image: the image.
3107%
3108% o amount: Define the extent of the implosion.
3109%
3110% o exception: return any errors or warnings in this structure.
3111%
3112*/
3113MagickExport Image *ImplodeImage(const Image *image,const double amount,
3114 ExceptionInfo *exception)
3115{
3116#define ImplodeImageTag "Implode/Image"
3117
cristyfa112112010-01-04 17:48:07 +00003118 CacheView
3119 *image_view,
3120 *implode_view;
3121
cristy3ed852e2009-09-05 21:47:34 +00003122 Image
3123 *implode_image;
3124
cristy3ed852e2009-09-05 21:47:34 +00003125 MagickBooleanType
3126 status;
3127
cristybb503372010-05-27 20:51:26 +00003128 MagickOffsetType
3129 progress;
3130
cristy4c08aed2011-07-01 19:47:50 +00003131 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003132 zero;
3133
3134 MagickRealType
3135 radius;
3136
3137 PointInfo
3138 center,
3139 scale;
3140
cristybb503372010-05-27 20:51:26 +00003141 ssize_t
3142 y;
3143
cristy3ed852e2009-09-05 21:47:34 +00003144 /*
3145 Initialize implode image attributes.
3146 */
3147 assert(image != (Image *) NULL);
3148 assert(image->signature == MagickSignature);
3149 if (image->debug != MagickFalse)
3150 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3151 assert(exception != (ExceptionInfo *) NULL);
3152 assert(exception->signature == MagickSignature);
3153 implode_image=CloneImage(image,0,0,MagickTrue,exception);
3154 if (implode_image == (Image *) NULL)
3155 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003156 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003157 {
cristy3ed852e2009-09-05 21:47:34 +00003158 implode_image=DestroyImage(implode_image);
3159 return((Image *) NULL);
3160 }
cristy4c08aed2011-07-01 19:47:50 +00003161 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003162 implode_image->matte=MagickTrue;
3163 /*
3164 Compute scaling factor.
3165 */
3166 scale.x=1.0;
3167 scale.y=1.0;
3168 center.x=0.5*image->columns;
3169 center.y=0.5*image->rows;
3170 radius=center.x;
3171 if (image->columns > image->rows)
3172 scale.y=(double) image->columns/(double) image->rows;
3173 else
3174 if (image->columns < image->rows)
3175 {
3176 scale.x=(double) image->rows/(double) image->columns;
3177 radius=center.y;
3178 }
3179 /*
3180 Implode image.
3181 */
3182 status=MagickTrue;
3183 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003184 GetPixelInfo(implode_image,&zero);
cristy3ed852e2009-09-05 21:47:34 +00003185 image_view=AcquireCacheView(image);
3186 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003187#if defined(MAGICKCORE_OPENMP_SUPPORT)
3188 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003189#endif
cristybb503372010-05-27 20:51:26 +00003190 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003191 {
cristy4c08aed2011-07-01 19:47:50 +00003192 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003193 pixel;
3194
3195 MagickRealType
3196 distance;
3197
3198 PointInfo
3199 delta;
3200
cristybb503372010-05-27 20:51:26 +00003201 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003202 x;
3203
cristy4c08aed2011-07-01 19:47:50 +00003204 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003205 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003206
3207 if (status == MagickFalse)
3208 continue;
3209 q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3210 exception);
cristy4c08aed2011-07-01 19:47:50 +00003211 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003212 {
3213 status=MagickFalse;
3214 continue;
3215 }
cristy3ed852e2009-09-05 21:47:34 +00003216 delta.y=scale.y*(double) (y-center.y);
3217 pixel=zero;
cristybb503372010-05-27 20:51:26 +00003218 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003219 {
3220 /*
3221 Determine if the pixel is within an ellipse.
3222 */
3223 delta.x=scale.x*(double) (x-center.x);
3224 distance=delta.x*delta.x+delta.y*delta.y;
3225 if (distance < (radius*radius))
3226 {
3227 double
3228 factor;
3229
3230 /*
3231 Implode the pixel.
3232 */
3233 factor=1.0;
3234 if (distance > 0.0)
3235 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3236 radius/2)),-amount);
cristy4c08aed2011-07-01 19:47:50 +00003237 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00003238 UndefinedInterpolatePixel,(double) (factor*delta.x/scale.x+
3239 center.x),(double) (factor*delta.y/scale.y+center.y),&pixel,
3240 exception);
cristy4c08aed2011-07-01 19:47:50 +00003241 SetPixelPixelInfo(implode_image,&pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00003242 }
cristyed231572011-07-14 02:18:59 +00003243 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003244 }
3245 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3246 status=MagickFalse;
3247 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3248 {
3249 MagickBooleanType
3250 proceed;
3251
cristyb5d5f722009-11-04 03:03:49 +00003252#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003253 #pragma omp critical (MagickCore_ImplodeImage)
3254#endif
3255 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3256 if (proceed == MagickFalse)
3257 status=MagickFalse;
3258 }
3259 }
3260 implode_view=DestroyCacheView(implode_view);
3261 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003262 if (status == MagickFalse)
3263 implode_image=DestroyImage(implode_image);
3264 return(implode_image);
3265}
3266
3267/*
3268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3269% %
3270% %
3271% %
3272% M o r p h I m a g e s %
3273% %
3274% %
3275% %
3276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3277%
3278% The MorphImages() method requires a minimum of two images. The first
3279% image is transformed into the second by a number of intervening images
3280% as specified by frames.
3281%
3282% The format of the MorphImage method is:
3283%
cristybb503372010-05-27 20:51:26 +00003284% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003285% ExceptionInfo *exception)
3286%
3287% A description of each parameter follows:
3288%
3289% o image: the image.
3290%
3291% o number_frames: Define the number of in-between image to generate.
3292% The more in-between frames, the smoother the morph.
3293%
3294% o exception: return any errors or warnings in this structure.
3295%
3296*/
3297MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003298 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003299{
3300#define MorphImageTag "Morph/Image"
3301
3302 Image
3303 *morph_image,
3304 *morph_images;
3305
cristy9d314ff2011-03-09 01:30:28 +00003306 MagickBooleanType
3307 status;
cristy3ed852e2009-09-05 21:47:34 +00003308
3309 MagickOffsetType
3310 scene;
3311
3312 MagickRealType
3313 alpha,
3314 beta;
3315
3316 register const Image
3317 *next;
3318
cristybb503372010-05-27 20:51:26 +00003319 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003320 i;
3321
cristy9d314ff2011-03-09 01:30:28 +00003322 ssize_t
3323 y;
cristy3ed852e2009-09-05 21:47:34 +00003324
3325 /*
3326 Clone first frame in sequence.
3327 */
3328 assert(image != (Image *) NULL);
3329 assert(image->signature == MagickSignature);
3330 if (image->debug != MagickFalse)
3331 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3332 assert(exception != (ExceptionInfo *) NULL);
3333 assert(exception->signature == MagickSignature);
3334 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3335 if (morph_images == (Image *) NULL)
3336 return((Image *) NULL);
3337 if (GetNextImageInList(image) == (Image *) NULL)
3338 {
3339 /*
3340 Morph single image.
3341 */
cristybb503372010-05-27 20:51:26 +00003342 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003343 {
3344 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3345 if (morph_image == (Image *) NULL)
3346 {
3347 morph_images=DestroyImageList(morph_images);
3348 return((Image *) NULL);
3349 }
3350 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003351 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003352 {
cristy8b27a6d2010-02-14 03:31:15 +00003353 MagickBooleanType
3354 proceed;
3355
cristybb503372010-05-27 20:51:26 +00003356 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3357 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003358 if (proceed == MagickFalse)
3359 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003360 }
3361 }
3362 return(GetFirstImageInList(morph_images));
3363 }
3364 /*
3365 Morph image sequence.
3366 */
3367 status=MagickTrue;
3368 scene=0;
3369 next=image;
3370 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3371 {
cristybb503372010-05-27 20:51:26 +00003372 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003373 {
3374 CacheView
3375 *image_view,
3376 *morph_view;
3377
3378 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3379 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003380 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristybb503372010-05-27 20:51:26 +00003381 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
cristy15b98cd2010-09-12 19:42:50 +00003382 next->rows+beta*GetNextImageInList(next)->rows+0.5),
3383 next->filter,next->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003384 if (morph_image == (Image *) NULL)
3385 {
3386 morph_images=DestroyImageList(morph_images);
3387 return((Image *) NULL);
3388 }
cristy574cc262011-08-05 01:23:58 +00003389 if (SetImageStorageClass(morph_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003390 {
cristy3ed852e2009-09-05 21:47:34 +00003391 morph_image=DestroyImage(morph_image);
3392 return((Image *) NULL);
3393 }
3394 AppendImageToList(&morph_images,morph_image);
3395 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003396 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3397 morph_images->rows,GetNextImageInList(next)->filter,
3398 GetNextImageInList(next)->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003399 if (morph_image == (Image *) NULL)
3400 {
3401 morph_images=DestroyImageList(morph_images);
3402 return((Image *) NULL);
3403 }
3404 image_view=AcquireCacheView(morph_image);
3405 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003406#if defined(MAGICKCORE_OPENMP_SUPPORT)
3407 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003408#endif
cristybb503372010-05-27 20:51:26 +00003409 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003410 {
3411 MagickBooleanType
3412 sync;
3413
cristy4c08aed2011-07-01 19:47:50 +00003414 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003415 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003416
cristybb503372010-05-27 20:51:26 +00003417 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003418 x;
3419
cristy4c08aed2011-07-01 19:47:50 +00003420 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003421 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003422
3423 if (status == MagickFalse)
3424 continue;
3425 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3426 exception);
3427 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3428 exception);
cristy4c08aed2011-07-01 19:47:50 +00003429 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003430 {
3431 status=MagickFalse;
3432 continue;
3433 }
cristybb503372010-05-27 20:51:26 +00003434 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003435 {
cristy4c08aed2011-07-01 19:47:50 +00003436 SetPixelRed(morph_images,ClampToQuantum(alpha*
3437 GetPixelRed(morph_images,q)+beta*GetPixelRed(morph_image,p)),q);
3438 SetPixelGreen(morph_images,ClampToQuantum(alpha*
3439 GetPixelGreen(morph_images,q)+beta*GetPixelGreen(morph_image,p)),q);
3440 SetPixelBlue(morph_images,ClampToQuantum(alpha*
3441 GetPixelBlue(morph_images,q)+beta*GetPixelBlue(morph_image,p)),q);
3442 SetPixelAlpha(morph_images,ClampToQuantum(alpha*
3443 GetPixelAlpha(morph_images,q)+beta*GetPixelAlpha(morph_image,p)),q);
3444 if ((morph_image->colorspace == CMYKColorspace) &&
3445 (morph_images->colorspace == CMYKColorspace))
3446 SetPixelBlack(morph_images,ClampToQuantum(alpha*
3447 GetPixelBlack(morph_images,q)+beta*GetPixelBlack(morph_image,p)),
3448 q);
cristyed231572011-07-14 02:18:59 +00003449 p+=GetPixelChannels(morph_image);
3450 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003451 }
3452 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3453 if (sync == MagickFalse)
3454 status=MagickFalse;
3455 }
3456 morph_view=DestroyCacheView(morph_view);
3457 image_view=DestroyCacheView(image_view);
3458 morph_image=DestroyImage(morph_image);
3459 }
cristybb503372010-05-27 20:51:26 +00003460 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003461 break;
3462 /*
3463 Clone last frame in sequence.
3464 */
3465 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3466 if (morph_image == (Image *) NULL)
3467 {
3468 morph_images=DestroyImageList(morph_images);
3469 return((Image *) NULL);
3470 }
3471 AppendImageToList(&morph_images,morph_image);
3472 morph_images=GetLastImageInList(morph_images);
3473 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3474 {
3475 MagickBooleanType
3476 proceed;
3477
cristyb5d5f722009-11-04 03:03:49 +00003478#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003479 #pragma omp critical (MagickCore_MorphImages)
3480#endif
3481 proceed=SetImageProgress(image,MorphImageTag,scene,
3482 GetImageListLength(image));
3483 if (proceed == MagickFalse)
3484 status=MagickFalse;
3485 }
3486 scene++;
3487 }
3488 if (GetNextImageInList(next) != (Image *) NULL)
3489 {
3490 morph_images=DestroyImageList(morph_images);
3491 return((Image *) NULL);
3492 }
3493 return(GetFirstImageInList(morph_images));
3494}
3495
3496/*
3497%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3498% %
3499% %
3500% %
3501% P l a s m a I m a g e %
3502% %
3503% %
3504% %
3505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3506%
3507% PlasmaImage() initializes an image with plasma fractal values. The image
3508% must be initialized with a base color and the random number generator
3509% seeded before this method is called.
3510%
3511% The format of the PlasmaImage method is:
3512%
3513% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristybb503372010-05-27 20:51:26 +00003514% size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003515%
3516% A description of each parameter follows:
3517%
3518% o image: the image.
3519%
3520% o segment: Define the region to apply plasma fractals values.
3521%
glennrp7dae1ca2010-09-16 12:17:35 +00003522% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003523%
3524% o depth: Limit the plasma recursion depth.
3525%
3526*/
3527
3528static inline Quantum PlasmaPixel(RandomInfo *random_info,
3529 const MagickRealType pixel,const MagickRealType noise)
3530{
3531 Quantum
3532 plasma;
3533
cristyce70c172010-01-07 17:15:30 +00003534 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003535 noise/2.0);
3536 return(plasma);
3537}
3538
3539MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
cristyc5c6f662010-09-22 14:23:02 +00003540 CacheView *image_view,RandomInfo *random_info,const SegmentInfo *segment,
3541 size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003542{
3543 ExceptionInfo
3544 *exception;
3545
cristy3ed852e2009-09-05 21:47:34 +00003546 MagickRealType
3547 plasma;
3548
3549 PixelPacket
3550 u,
3551 v;
3552
cristy9d314ff2011-03-09 01:30:28 +00003553 ssize_t
3554 x,
3555 x_mid,
3556 y,
3557 y_mid;
3558
cristy3ed852e2009-09-05 21:47:34 +00003559 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3560 return(MagickTrue);
3561 if (depth != 0)
3562 {
3563 SegmentInfo
3564 local_info;
3565
3566 /*
3567 Divide the area into quadrants and recurse.
3568 */
3569 depth--;
3570 attenuate++;
cristybb503372010-05-27 20:51:26 +00003571 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3572 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003573 local_info=(*segment);
3574 local_info.x2=(double) x_mid;
3575 local_info.y2=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003576 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3577 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003578 local_info=(*segment);
3579 local_info.y1=(double) y_mid;
3580 local_info.x2=(double) x_mid;
cristyc5c6f662010-09-22 14:23:02 +00003581 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3582 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003583 local_info=(*segment);
3584 local_info.x1=(double) x_mid;
3585 local_info.y2=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003586 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3587 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003588 local_info=(*segment);
3589 local_info.x1=(double) x_mid;
3590 local_info.y1=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003591 return(PlasmaImageProxy(image,image_view,random_info,&local_info,
3592 attenuate,depth));
cristy3ed852e2009-09-05 21:47:34 +00003593 }
cristybb503372010-05-27 20:51:26 +00003594 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3595 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003596 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3597 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3598 return(MagickFalse);
3599 /*
3600 Average pixels and apply plasma.
3601 */
3602 exception=(&image->exception);
3603 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3604 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3605 {
cristy4c08aed2011-07-01 19:47:50 +00003606 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003607 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003608
3609 /*
3610 Left pixel.
3611 */
cristybb503372010-05-27 20:51:26 +00003612 x=(ssize_t) ceil(segment->x1-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003613 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3614 ceil(segment->y1-0.5),&u,exception);
3615 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3616 ceil(segment->y2-0.5),&v,exception);
3617 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003618 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003619 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003620 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3621 (u.red+v.red)/2.0,plasma),q);
3622 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3623 (u.green+v.green)/2.0,plasma),q);
3624 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3625 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003626 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003627 if (segment->x1 != segment->x2)
3628 {
3629 /*
3630 Right pixel.
3631 */
cristybb503372010-05-27 20:51:26 +00003632 x=(ssize_t) ceil(segment->x2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003633 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3634 ceil(segment->y1-0.5),&u,exception);
3635 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3636 ceil(segment->y2-0.5),&v,exception);
3637 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003638 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003639 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003640 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3641 (u.red+v.red)/2.0,plasma),q);
3642 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3643 (u.green+v.green)/2.0,plasma),q);
3644 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3645 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003646 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003647 }
3648 }
3649 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3650 {
3651 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3652 {
cristy4c08aed2011-07-01 19:47:50 +00003653 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003654 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003655
3656 /*
3657 Bottom pixel.
3658 */
cristybb503372010-05-27 20:51:26 +00003659 y=(ssize_t) ceil(segment->y2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003660 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3661 ceil(segment->x1-0.5),y,&u,exception);
3662 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3663 ceil(segment->x2-0.5),y,&v,exception);
3664 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003665 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003666 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003667 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3668 (u.red+v.red)/2.0,plasma),q);
3669 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3670 (u.green+v.green)/2.0,plasma),q);
3671 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3672 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003673 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003674 }
3675 if (segment->y1 != segment->y2)
3676 {
cristy4c08aed2011-07-01 19:47:50 +00003677 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003678 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003679
3680 /*
3681 Top pixel.
3682 */
cristybb503372010-05-27 20:51:26 +00003683 y=(ssize_t) ceil(segment->y1-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003684 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3685 ceil(segment->x1-0.5),y,&u,exception);
3686 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3687 ceil(segment->x2-0.5),y,&v,exception);
3688 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003689 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003690 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003691 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3692 (u.red+v.red)/2.0,plasma),q);
3693 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3694 (u.green+v.green)/2.0,plasma),q);
3695 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3696 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003697 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003698 }
3699 }
3700 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3701 {
cristy4c08aed2011-07-01 19:47:50 +00003702 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003703 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003704
3705 /*
3706 Middle pixel.
3707 */
cristybb503372010-05-27 20:51:26 +00003708 x=(ssize_t) ceil(segment->x1-0.5);
3709 y=(ssize_t) ceil(segment->y1-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003710 (void) GetOneVirtualPixel(image,x,y,&u,exception);
cristybb503372010-05-27 20:51:26 +00003711 x=(ssize_t) ceil(segment->x2-0.5);
3712 y=(ssize_t) ceil(segment->y2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003713 (void) GetOneCacheViewVirtualPixel(image_view,x,y,&v,exception);
3714 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003715 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003716 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00003717 SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3718 (u.red+v.red)/2.0,plasma),q);
3719 SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3720 (u.green+v.green)/2.0,plasma),q);
3721 SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3722 (u.blue+v.blue)/2.0,plasma),q);
cristyc5c6f662010-09-22 14:23:02 +00003723 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003724 }
3725 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3726 return(MagickTrue);
3727 return(MagickFalse);
3728}
3729
3730MagickExport MagickBooleanType PlasmaImage(Image *image,
cristybb503372010-05-27 20:51:26 +00003731 const SegmentInfo *segment,size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003732{
cristyc5c6f662010-09-22 14:23:02 +00003733 CacheView
3734 *image_view;
3735
cristy3ed852e2009-09-05 21:47:34 +00003736 MagickBooleanType
3737 status;
3738
3739 RandomInfo
3740 *random_info;
3741
3742 if (image->debug != MagickFalse)
3743 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3744 assert(image != (Image *) NULL);
3745 assert(image->signature == MagickSignature);
3746 if (image->debug != MagickFalse)
3747 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy574cc262011-08-05 01:23:58 +00003748 if (SetImageStorageClass(image,DirectClass,&image->exception) == MagickFalse)
cristyc5c6f662010-09-22 14:23:02 +00003749 return(MagickFalse);
3750 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003751 random_info=AcquireRandomInfo();
cristyc5c6f662010-09-22 14:23:02 +00003752 status=PlasmaImageProxy(image,image_view,random_info,segment,attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003753 random_info=DestroyRandomInfo(random_info);
cristyc5c6f662010-09-22 14:23:02 +00003754 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003755 return(status);
3756}
3757
3758/*
3759%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3760% %
3761% %
3762% %
3763% P o l a r o i d I m a g e %
3764% %
3765% %
3766% %
3767%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3768%
3769% PolaroidImage() simulates a Polaroid picture.
3770%
3771% The format of the AnnotateImage method is:
3772%
3773% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3774% const double angle,ExceptionInfo exception)
3775%
3776% A description of each parameter follows:
3777%
3778% o image: the image.
3779%
3780% o draw_info: the draw info.
3781%
cristycee97112010-05-28 00:44:52 +00003782% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003783%
3784% o exception: return any errors or warnings in this structure.
3785%
3786*/
3787MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3788 const double angle,ExceptionInfo *exception)
3789{
3790 const char
3791 *value;
3792
cristy3ed852e2009-09-05 21:47:34 +00003793 Image
3794 *bend_image,
3795 *caption_image,
3796 *flop_image,
3797 *picture_image,
3798 *polaroid_image,
3799 *rotate_image,
3800 *trim_image;
3801
cristybb503372010-05-27 20:51:26 +00003802 size_t
cristy3ed852e2009-09-05 21:47:34 +00003803 height;
3804
cristy9d314ff2011-03-09 01:30:28 +00003805 ssize_t
3806 quantum;
3807
cristy3ed852e2009-09-05 21:47:34 +00003808 /*
3809 Simulate a Polaroid picture.
3810 */
3811 assert(image != (Image *) NULL);
3812 assert(image->signature == MagickSignature);
3813 if (image->debug != MagickFalse)
3814 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3815 assert(exception != (ExceptionInfo *) NULL);
3816 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003817 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003818 image->rows)/25.0,10.0);
3819 height=image->rows+2*quantum;
3820 caption_image=(Image *) NULL;
3821 value=GetImageProperty(image,"Caption");
3822 if (value != (const char *) NULL)
3823 {
3824 char
3825 *caption,
3826 geometry[MaxTextExtent];
3827
3828 DrawInfo
3829 *annotate_info;
3830
cristy3ed852e2009-09-05 21:47:34 +00003831 MagickBooleanType
3832 status;
3833
cristy9d314ff2011-03-09 01:30:28 +00003834 ssize_t
3835 count;
3836
cristy3ed852e2009-09-05 21:47:34 +00003837 TypeMetric
3838 metrics;
3839
3840 /*
3841 Generate caption image.
3842 */
3843 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3844 if (caption_image == (Image *) NULL)
3845 return((Image *) NULL);
3846 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
3847 caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
3848 value);
3849 (void) CloneString(&annotate_info->text,caption);
cristy6b1d05e2010-09-22 19:17:27 +00003850 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
3851 &caption);
cristybb503372010-05-27 20:51:26 +00003852 status=SetImageExtent(caption_image,image->columns,(size_t)
cristy63240882011-08-05 19:05:27 +00003853 ((count+1)*(metrics.ascent-metrics.descent)+0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00003854 if (status == MagickFalse)
3855 caption_image=DestroyImage(caption_image);
3856 else
3857 {
3858 caption_image->background_color=image->border_color;
3859 (void) SetImageBackgroundColor(caption_image);
3860 (void) CloneString(&annotate_info->text,caption);
cristyb51dff52011-05-19 16:55:47 +00003861 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00003862 metrics.ascent);
3863 if (annotate_info->gravity == UndefinedGravity)
3864 (void) CloneString(&annotate_info->geometry,AcquireString(
3865 geometry));
3866 (void) AnnotateImage(caption_image,annotate_info);
3867 height+=caption_image->rows;
3868 }
3869 annotate_info=DestroyDrawInfo(annotate_info);
3870 caption=DestroyString(caption);
3871 }
3872 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3873 exception);
3874 if (picture_image == (Image *) NULL)
3875 {
3876 if (caption_image != (Image *) NULL)
3877 caption_image=DestroyImage(caption_image);
3878 return((Image *) NULL);
3879 }
3880 picture_image->background_color=image->border_color;
3881 (void) SetImageBackgroundColor(picture_image);
3882 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
3883 if (caption_image != (Image *) NULL)
3884 {
3885 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
cristybb503372010-05-27 20:51:26 +00003886 quantum,(ssize_t) (image->rows+3*quantum/2));
cristy3ed852e2009-09-05 21:47:34 +00003887 caption_image=DestroyImage(caption_image);
3888 }
3889 (void) QueryColorDatabase("none",&picture_image->background_color,exception);
cristy63240882011-08-05 19:05:27 +00003890 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00003891 rotate_image=RotateImage(picture_image,90.0,exception);
3892 picture_image=DestroyImage(picture_image);
3893 if (rotate_image == (Image *) NULL)
3894 return((Image *) NULL);
3895 picture_image=rotate_image;
3896 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
3897 picture_image->columns,exception);
3898 picture_image=DestroyImage(picture_image);
3899 if (bend_image == (Image *) NULL)
3900 return((Image *) NULL);
3901 InheritException(&bend_image->exception,exception);
3902 picture_image=bend_image;
3903 rotate_image=RotateImage(picture_image,-90.0,exception);
3904 picture_image=DestroyImage(picture_image);
3905 if (rotate_image == (Image *) NULL)
3906 return((Image *) NULL);
3907 picture_image=rotate_image;
3908 picture_image->background_color=image->background_color;
3909 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
3910 exception);
3911 if (polaroid_image == (Image *) NULL)
3912 {
3913 picture_image=DestroyImage(picture_image);
3914 return(picture_image);
3915 }
3916 flop_image=FlopImage(polaroid_image,exception);
3917 polaroid_image=DestroyImage(polaroid_image);
3918 if (flop_image == (Image *) NULL)
3919 {
3920 picture_image=DestroyImage(picture_image);
3921 return(picture_image);
3922 }
3923 polaroid_image=flop_image;
3924 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
cristybb503372010-05-27 20:51:26 +00003925 (ssize_t) (-0.01*picture_image->columns/2.0),0L);
cristy3ed852e2009-09-05 21:47:34 +00003926 picture_image=DestroyImage(picture_image);
3927 (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
3928 rotate_image=RotateImage(polaroid_image,angle,exception);
3929 polaroid_image=DestroyImage(polaroid_image);
3930 if (rotate_image == (Image *) NULL)
3931 return((Image *) NULL);
3932 polaroid_image=rotate_image;
3933 trim_image=TrimImage(polaroid_image,exception);
3934 polaroid_image=DestroyImage(polaroid_image);
3935 if (trim_image == (Image *) NULL)
3936 return((Image *) NULL);
3937 polaroid_image=trim_image;
3938 return(polaroid_image);
3939}
3940
3941/*
3942%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3943% %
3944% %
3945% %
cristy3ed852e2009-09-05 21:47:34 +00003946% S e p i a T o n e I m a g e %
3947% %
3948% %
3949% %
3950%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3951%
3952% MagickSepiaToneImage() applies a special effect to the image, similar to the
3953% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
3954% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
3955% threshold of 80% is a good starting point for a reasonable tone.
3956%
3957% The format of the SepiaToneImage method is:
3958%
3959% Image *SepiaToneImage(const Image *image,const double threshold,
3960% ExceptionInfo *exception)
3961%
3962% A description of each parameter follows:
3963%
3964% o image: the image.
3965%
3966% o threshold: the tone threshold.
3967%
3968% o exception: return any errors or warnings in this structure.
3969%
3970*/
3971MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
3972 ExceptionInfo *exception)
3973{
3974#define SepiaToneImageTag "SepiaTone/Image"
3975
cristyc4c8d132010-01-07 01:58:38 +00003976 CacheView
3977 *image_view,
3978 *sepia_view;
3979
cristy3ed852e2009-09-05 21:47:34 +00003980 Image
3981 *sepia_image;
3982
cristy3ed852e2009-09-05 21:47:34 +00003983 MagickBooleanType
3984 status;
3985
cristybb503372010-05-27 20:51:26 +00003986 MagickOffsetType
3987 progress;
3988
3989 ssize_t
3990 y;
3991
cristy3ed852e2009-09-05 21:47:34 +00003992 /*
3993 Initialize sepia-toned image attributes.
3994 */
3995 assert(image != (const Image *) NULL);
3996 assert(image->signature == MagickSignature);
3997 if (image->debug != MagickFalse)
3998 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3999 assert(exception != (ExceptionInfo *) NULL);
4000 assert(exception->signature == MagickSignature);
4001 sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4002 if (sepia_image == (Image *) NULL)
4003 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004004 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004005 {
cristy3ed852e2009-09-05 21:47:34 +00004006 sepia_image=DestroyImage(sepia_image);
4007 return((Image *) NULL);
4008 }
4009 /*
4010 Tone each row of the image.
4011 */
4012 status=MagickTrue;
4013 progress=0;
4014 image_view=AcquireCacheView(image);
4015 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004016#if defined(MAGICKCORE_OPENMP_SUPPORT)
4017 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004018#endif
cristybb503372010-05-27 20:51:26 +00004019 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004020 {
cristy4c08aed2011-07-01 19:47:50 +00004021 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004022 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004023
cristybb503372010-05-27 20:51:26 +00004024 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004025 x;
4026
cristy4c08aed2011-07-01 19:47:50 +00004027 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004028 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004029
4030 if (status == MagickFalse)
4031 continue;
4032 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4033 q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4034 exception);
cristy4c08aed2011-07-01 19:47:50 +00004035 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004036 {
4037 status=MagickFalse;
4038 continue;
4039 }
cristybb503372010-05-27 20:51:26 +00004040 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004041 {
4042 MagickRealType
4043 intensity,
4044 tone;
4045
cristy4c08aed2011-07-01 19:47:50 +00004046 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004047 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4048 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004049 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004050 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4051 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004052 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004053 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004054 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004055 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004056 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4057 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4058 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4059 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004060 p+=GetPixelChannels(image);
4061 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004062 }
4063 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4064 status=MagickFalse;
4065 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4066 {
4067 MagickBooleanType
4068 proceed;
4069
cristyb5d5f722009-11-04 03:03:49 +00004070#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004071 #pragma omp critical (MagickCore_SepiaToneImage)
4072#endif
4073 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4074 image->rows);
4075 if (proceed == MagickFalse)
4076 status=MagickFalse;
4077 }
4078 }
4079 sepia_view=DestroyCacheView(sepia_view);
4080 image_view=DestroyCacheView(image_view);
cristye23ec9d2011-08-16 18:15:40 +00004081 (void) NormalizeImage(sepia_image,exception);
4082 (void) ContrastImage(sepia_image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004083 if (status == MagickFalse)
4084 sepia_image=DestroyImage(sepia_image);
4085 return(sepia_image);
4086}
4087
4088/*
4089%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4090% %
4091% %
4092% %
4093% S h a d o w I m a g e %
4094% %
4095% %
4096% %
4097%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4098%
4099% ShadowImage() simulates a shadow from the specified image and returns it.
4100%
4101% The format of the ShadowImage method is:
4102%
4103% Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004104% const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004105% ExceptionInfo *exception)
4106%
4107% A description of each parameter follows:
4108%
4109% o image: the image.
4110%
4111% o opacity: percentage transparency.
4112%
4113% o sigma: the standard deviation of the Gaussian, in pixels.
4114%
4115% o x_offset: the shadow x-offset.
4116%
4117% o y_offset: the shadow y-offset.
4118%
4119% o exception: return any errors or warnings in this structure.
4120%
4121*/
4122MagickExport Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004123 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004124 ExceptionInfo *exception)
4125{
4126#define ShadowImageTag "Shadow/Image"
4127
cristyc4c8d132010-01-07 01:58:38 +00004128 CacheView
cristy4c08aed2011-07-01 19:47:50 +00004129 *border_view;
cristyc4c8d132010-01-07 01:58:38 +00004130
cristybd5a96c2011-08-21 00:04:26 +00004131 ChannelType
4132 channel_mask;
4133
cristy3ed852e2009-09-05 21:47:34 +00004134 Image
4135 *border_image,
4136 *clone_image,
4137 *shadow_image;
4138
cristy3ed852e2009-09-05 21:47:34 +00004139 MagickBooleanType
4140 status;
4141
cristybb503372010-05-27 20:51:26 +00004142 MagickOffsetType
4143 progress;
4144
cristy3ed852e2009-09-05 21:47:34 +00004145 RectangleInfo
4146 border_info;
4147
cristybb503372010-05-27 20:51:26 +00004148 ssize_t
4149 y;
4150
cristy3ed852e2009-09-05 21:47:34 +00004151 assert(image != (Image *) NULL);
4152 assert(image->signature == MagickSignature);
4153 if (image->debug != MagickFalse)
4154 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4155 assert(exception != (ExceptionInfo *) NULL);
4156 assert(exception->signature == MagickSignature);
4157 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4158 if (clone_image == (Image *) NULL)
4159 return((Image *) NULL);
4160 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4161 clone_image->compose=OverCompositeOp;
cristybb503372010-05-27 20:51:26 +00004162 border_info.width=(size_t) floor(2.0*sigma+0.5);
4163 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004164 border_info.x=0;
4165 border_info.y=0;
4166 (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4167 border_image=BorderImage(clone_image,&border_info,exception);
4168 clone_image=DestroyImage(clone_image);
4169 if (border_image == (Image *) NULL)
4170 return((Image *) NULL);
4171 if (border_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004172 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004173 /*
4174 Shadow image.
4175 */
4176 status=MagickTrue;
4177 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00004178 border_view=AcquireCacheView(border_image);
cristyb5d5f722009-11-04 03:03:49 +00004179#if defined(MAGICKCORE_OPENMP_SUPPORT)
4180 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004181#endif
cristybb503372010-05-27 20:51:26 +00004182 for (y=0; y < (ssize_t) border_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004183 {
cristybb503372010-05-27 20:51:26 +00004184 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004185 x;
4186
cristy4c08aed2011-07-01 19:47:50 +00004187 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004188 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004189
4190 if (status == MagickFalse)
4191 continue;
cristy4c08aed2011-07-01 19:47:50 +00004192 q=GetCacheViewAuthenticPixels(border_view,0,y,border_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004193 exception);
cristy4c08aed2011-07-01 19:47:50 +00004194 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004195 {
4196 status=MagickFalse;
4197 continue;
4198 }
cristybb503372010-05-27 20:51:26 +00004199 for (x=0; x < (ssize_t) border_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004200 {
cristy4c08aed2011-07-01 19:47:50 +00004201 SetPixelRed(border_image,border_image->background_color.red,q);
4202 SetPixelGreen(border_image,border_image->background_color.green,q);
4203 SetPixelBlue(border_image,border_image->background_color.blue,q);
cristy3ed852e2009-09-05 21:47:34 +00004204 if (border_image->matte == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004205 SetPixelAlpha(border_image,border_image->background_color.alpha,q);
cristy3ed852e2009-09-05 21:47:34 +00004206 else
cristy4c08aed2011-07-01 19:47:50 +00004207 SetPixelAlpha(border_image,ClampToQuantum((MagickRealType)
4208 (GetPixelAlpha(border_image,q)*opacity/100.0)),q);
cristyed231572011-07-14 02:18:59 +00004209 q+=GetPixelChannels(border_image);
cristy3ed852e2009-09-05 21:47:34 +00004210 }
cristy4c08aed2011-07-01 19:47:50 +00004211 if (SyncCacheViewAuthenticPixels(border_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004212 status=MagickFalse;
cristy4c08aed2011-07-01 19:47:50 +00004213 if (border_image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004214 {
4215 MagickBooleanType
4216 proceed;
4217
cristyb5d5f722009-11-04 03:03:49 +00004218#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004219 #pragma omp critical (MagickCore_ShadowImage)
4220#endif
cristy4c08aed2011-07-01 19:47:50 +00004221 proceed=SetImageProgress(border_image,ShadowImageTag,progress++,
cristy3ed852e2009-09-05 21:47:34 +00004222 border_image->rows);
4223 if (proceed == MagickFalse)
4224 status=MagickFalse;
4225 }
4226 }
cristy4c08aed2011-07-01 19:47:50 +00004227 border_view=DestroyCacheView(border_view);
cristybd5a96c2011-08-21 00:04:26 +00004228 channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
cristyf4ad9df2011-07-08 16:49:03 +00004229 shadow_image=BlurImage(border_image,0.0,sigma,exception);
cristybd5a96c2011-08-21 00:04:26 +00004230 (void) SetPixelChannelMap(border_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004231 border_image=DestroyImage(border_image);
4232 if (shadow_image == (Image *) NULL)
4233 return((Image *) NULL);
4234 if (shadow_image->page.width == 0)
4235 shadow_image->page.width=shadow_image->columns;
4236 if (shadow_image->page.height == 0)
4237 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004238 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4239 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4240 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4241 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004242 return(shadow_image);
4243}
4244
4245/*
4246%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4247% %
4248% %
4249% %
4250% S k e t c h I m a g e %
4251% %
4252% %
4253% %
4254%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4255%
4256% SketchImage() simulates a pencil sketch. We convolve the image with a
4257% Gaussian operator of the given radius and standard deviation (sigma). For
4258% reasonable results, radius should be larger than sigma. Use a radius of 0
4259% and SketchImage() selects a suitable radius for you. Angle gives the angle
4260% of the sketch.
4261%
4262% The format of the SketchImage method is:
4263%
4264% Image *SketchImage(const Image *image,const double radius,
4265% const double sigma,const double angle,ExceptionInfo *exception)
4266%
4267% A description of each parameter follows:
4268%
4269% o image: the image.
4270%
cristy574cc262011-08-05 01:23:58 +00004271% o radius: the radius of the Gaussian, in pixels, not counting the
4272% center pixel.
cristy3ed852e2009-09-05 21:47:34 +00004273%
4274% o sigma: the standard deviation of the Gaussian, in pixels.
4275%
cristycee97112010-05-28 00:44:52 +00004276% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00004277%
4278% o exception: return any errors or warnings in this structure.
4279%
4280*/
4281MagickExport Image *SketchImage(const Image *image,const double radius,
4282 const double sigma,const double angle,ExceptionInfo *exception)
4283{
cristyfa112112010-01-04 17:48:07 +00004284 CacheView
4285 *random_view;
4286
cristy3ed852e2009-09-05 21:47:34 +00004287 Image
4288 *blend_image,
4289 *blur_image,
4290 *dodge_image,
4291 *random_image,
4292 *sketch_image;
4293
cristy3ed852e2009-09-05 21:47:34 +00004294 MagickBooleanType
4295 status;
4296
cristy4c08aed2011-07-01 19:47:50 +00004297 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004298 zero;
4299
4300 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004301 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004302
cristy9d314ff2011-03-09 01:30:28 +00004303 ssize_t
4304 y;
4305
cristy3ed852e2009-09-05 21:47:34 +00004306 /*
4307 Sketch image.
4308 */
4309 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4310 MagickTrue,exception);
4311 if (random_image == (Image *) NULL)
4312 return((Image *) NULL);
4313 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00004314 GetPixelInfo(random_image,&zero);
cristy1b784432009-12-19 02:20:40 +00004315 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004316 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004317#if defined(MAGICKCORE_OPENMP_SUPPORT)
4318 #pragma omp parallel for schedule(dynamic,4) shared(status)
4319#endif
cristybb503372010-05-27 20:51:26 +00004320 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004321 {
cristy5c9e6f22010-09-17 17:31:01 +00004322 const int
4323 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004324
cristy4c08aed2011-07-01 19:47:50 +00004325 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004326 pixel;
4327
cristybb503372010-05-27 20:51:26 +00004328 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004329 x;
4330
cristy4c08aed2011-07-01 19:47:50 +00004331 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004332 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004333
cristy1b784432009-12-19 02:20:40 +00004334 if (status == MagickFalse)
4335 continue;
cristy3ed852e2009-09-05 21:47:34 +00004336 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4337 exception);
cristy4c08aed2011-07-01 19:47:50 +00004338 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004339 {
4340 status=MagickFalse;
4341 continue;
4342 }
cristy3ed852e2009-09-05 21:47:34 +00004343 pixel=zero;
cristybb503372010-05-27 20:51:26 +00004344 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004345 {
4346 pixel.red=(MagickRealType) (QuantumRange*
cristy1b784432009-12-19 02:20:40 +00004347 GetPseudoRandomValue(random_info[id]));
cristy3ed852e2009-09-05 21:47:34 +00004348 pixel.green=pixel.red;
4349 pixel.blue=pixel.red;
4350 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004351 pixel.black=pixel.red;
4352 SetPixelPixelInfo(random_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00004353 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004354 }
4355 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4356 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004357 }
4358 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004359 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004360 if (status == MagickFalse)
4361 {
4362 random_image=DestroyImage(random_image);
4363 return(random_image);
4364 }
4365 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4366 random_image=DestroyImage(random_image);
4367 if (blur_image == (Image *) NULL)
4368 return((Image *) NULL);
4369 dodge_image=EdgeImage(blur_image,radius,exception);
4370 blur_image=DestroyImage(blur_image);
4371 if (dodge_image == (Image *) NULL)
4372 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +00004373 (void) NormalizeImage(dodge_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00004374 (void) NegateImage(dodge_image,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00004375 (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4376 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4377 if (sketch_image == (Image *) NULL)
4378 {
4379 dodge_image=DestroyImage(dodge_image);
4380 return((Image *) NULL);
4381 }
4382 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4383 dodge_image=DestroyImage(dodge_image);
4384 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4385 if (blend_image == (Image *) NULL)
4386 {
4387 sketch_image=DestroyImage(sketch_image);
4388 return((Image *) NULL);
4389 }
4390 (void) SetImageArtifact(blend_image,"compose:args","20x80");
4391 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4392 blend_image=DestroyImage(blend_image);
4393 return(sketch_image);
4394}
4395
4396/*
4397%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4398% %
4399% %
4400% %
4401% S o l a r i z e I m a g e %
4402% %
4403% %
4404% %
4405%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4406%
4407% SolarizeImage() applies a special effect to the image, similar to the effect
4408% achieved in a photo darkroom by selectively exposing areas of photo
4409% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4410% measure of the extent of the solarization.
4411%
4412% The format of the SolarizeImage method is:
4413%
4414% MagickBooleanType SolarizeImage(Image *image,const double threshold)
4415%
4416% A description of each parameter follows:
4417%
4418% o image: the image.
4419%
4420% o threshold: Define the extent of the solarization.
4421%
4422*/
4423MagickExport MagickBooleanType SolarizeImage(Image *image,
4424 const double threshold)
4425{
4426#define SolarizeImageTag "Solarize/Image"
4427
cristyc4c8d132010-01-07 01:58:38 +00004428 CacheView
4429 *image_view;
4430
cristy3ed852e2009-09-05 21:47:34 +00004431 ExceptionInfo
4432 *exception;
4433
cristy3ed852e2009-09-05 21:47:34 +00004434 MagickBooleanType
4435 status;
4436
cristybb503372010-05-27 20:51:26 +00004437 MagickOffsetType
4438 progress;
4439
4440 ssize_t
4441 y;
4442
cristy3ed852e2009-09-05 21:47:34 +00004443 assert(image != (Image *) NULL);
4444 assert(image->signature == MagickSignature);
4445 if (image->debug != MagickFalse)
4446 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4447 if (image->storage_class == PseudoClass)
4448 {
cristybb503372010-05-27 20:51:26 +00004449 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004450 i;
4451
4452 /*
4453 Solarize colormap.
4454 */
cristybb503372010-05-27 20:51:26 +00004455 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004456 {
4457 if ((MagickRealType) image->colormap[i].red > threshold)
4458 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4459 if ((MagickRealType) image->colormap[i].green > threshold)
4460 image->colormap[i].green=(Quantum) QuantumRange-
4461 image->colormap[i].green;
4462 if ((MagickRealType) image->colormap[i].blue > threshold)
4463 image->colormap[i].blue=(Quantum) QuantumRange-
4464 image->colormap[i].blue;
4465 }
4466 }
4467 /*
4468 Solarize image.
4469 */
4470 status=MagickTrue;
4471 progress=0;
4472 exception=(&image->exception);
4473 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004474#if defined(MAGICKCORE_OPENMP_SUPPORT)
4475 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004476#endif
cristybb503372010-05-27 20:51:26 +00004477 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004478 {
cristybb503372010-05-27 20:51:26 +00004479 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004480 x;
4481
cristy4c08aed2011-07-01 19:47:50 +00004482 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004483 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004484
4485 if (status == MagickFalse)
4486 continue;
4487 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4488 exception);
cristy4c08aed2011-07-01 19:47:50 +00004489 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004490 {
4491 status=MagickFalse;
4492 continue;
4493 }
cristybb503372010-05-27 20:51:26 +00004494 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004495 {
cristy4c08aed2011-07-01 19:47:50 +00004496 if ((MagickRealType) GetPixelRed(image,q) > threshold)
4497 SetPixelRed(image,QuantumRange-GetPixelRed(image,q),q);
4498 if ((MagickRealType) GetPixelGreen(image,q) > threshold)
4499 SetPixelGreen(image,QuantumRange-GetPixelGreen(image,q),q);
4500 if ((MagickRealType) GetPixelBlue(image,q) > threshold)
4501 SetPixelBlue(image,QuantumRange-GetPixelBlue(image,q),q);
cristyed231572011-07-14 02:18:59 +00004502 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004503 }
4504 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4505 status=MagickFalse;
4506 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4507 {
4508 MagickBooleanType
4509 proceed;
4510
cristyb5d5f722009-11-04 03:03:49 +00004511#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004512 #pragma omp critical (MagickCore_SolarizeImage)
4513#endif
4514 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4515 if (proceed == MagickFalse)
4516 status=MagickFalse;
4517 }
4518 }
4519 image_view=DestroyCacheView(image_view);
4520 return(status);
4521}
4522
4523/*
4524%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4525% %
4526% %
4527% %
4528% S t e g a n o I m a g e %
4529% %
4530% %
4531% %
4532%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4533%
4534% SteganoImage() hides a digital watermark within the image. Recover
4535% the hidden watermark later to prove that the authenticity of an image.
4536% Offset defines the start position within the image to hide the watermark.
4537%
4538% The format of the SteganoImage method is:
4539%
4540% Image *SteganoImage(const Image *image,Image *watermark,
4541% ExceptionInfo *exception)
4542%
4543% A description of each parameter follows:
4544%
4545% o image: the image.
4546%
4547% o watermark: the watermark image.
4548%
4549% o exception: return any errors or warnings in this structure.
4550%
4551*/
4552MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4553 ExceptionInfo *exception)
4554{
cristye1bf8ad2010-09-19 17:07:03 +00004555#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004556#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004557 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004558#define SteganoImageTag "Stegano/Image"
4559
cristyb0d3bb92010-09-22 14:37:58 +00004560 CacheView
4561 *stegano_view,
4562 *watermark_view;
4563
cristy3ed852e2009-09-05 21:47:34 +00004564 Image
4565 *stegano_image;
4566
4567 int
4568 c;
4569
cristy3ed852e2009-09-05 21:47:34 +00004570 MagickBooleanType
4571 status;
4572
4573 PixelPacket
4574 pixel;
4575
cristy4c08aed2011-07-01 19:47:50 +00004576 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004577 *q;
4578
cristye1bf8ad2010-09-19 17:07:03 +00004579 register ssize_t
4580 x;
4581
cristybb503372010-05-27 20:51:26 +00004582 size_t
cristyeaedf062010-05-29 22:36:02 +00004583 depth,
4584 one;
cristy3ed852e2009-09-05 21:47:34 +00004585
cristye1bf8ad2010-09-19 17:07:03 +00004586 ssize_t
4587 i,
4588 j,
4589 k,
4590 y;
4591
cristy3ed852e2009-09-05 21:47:34 +00004592 /*
4593 Initialize steganographic image attributes.
4594 */
4595 assert(image != (const Image *) NULL);
4596 assert(image->signature == MagickSignature);
4597 if (image->debug != MagickFalse)
4598 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4599 assert(watermark != (const Image *) NULL);
4600 assert(watermark->signature == MagickSignature);
4601 assert(exception != (ExceptionInfo *) NULL);
4602 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004603 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004604 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4605 if (stegano_image == (Image *) NULL)
4606 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004607 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004608 {
cristy3ed852e2009-09-05 21:47:34 +00004609 stegano_image=DestroyImage(stegano_image);
4610 return((Image *) NULL);
4611 }
4612 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4613 /*
4614 Hide watermark in low-order bits of image.
4615 */
4616 c=0;
4617 i=0;
4618 j=0;
4619 depth=stegano_image->depth;
4620 k=image->offset;
cristyda16f162011-02-19 23:52:17 +00004621 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004622 watermark_view=AcquireCacheView(watermark);
4623 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004624 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004625 {
cristybb503372010-05-27 20:51:26 +00004626 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004627 {
cristybb503372010-05-27 20:51:26 +00004628 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004629 {
cristyb0d3bb92010-09-22 14:37:58 +00004630 (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,&pixel,exception);
cristybb503372010-05-27 20:51:26 +00004631 if ((k/(ssize_t) stegano_image->columns) >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004632 break;
cristyb0d3bb92010-09-22 14:37:58 +00004633 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4634 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4635 exception);
cristy4c08aed2011-07-01 19:47:50 +00004636 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004637 break;
4638 switch (c)
4639 {
4640 case 0:
4641 {
cristy4c08aed2011-07-01 19:47:50 +00004642 SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
4643 GetPixelPacketIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004644 break;
4645 }
4646 case 1:
4647 {
cristy4c08aed2011-07-01 19:47:50 +00004648 SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
4649 GetPixelPacketIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004650 break;
4651 }
4652 case 2:
4653 {
cristy4c08aed2011-07-01 19:47:50 +00004654 SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
4655 GetPixelPacketIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004656 break;
4657 }
4658 }
cristyb0d3bb92010-09-22 14:37:58 +00004659 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004660 break;
4661 c++;
4662 if (c == 3)
4663 c=0;
4664 k++;
cristybb503372010-05-27 20:51:26 +00004665 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004666 k=0;
4667 if (k == image->offset)
4668 j++;
4669 }
4670 }
cristy8b27a6d2010-02-14 03:31:15 +00004671 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004672 {
cristy8b27a6d2010-02-14 03:31:15 +00004673 MagickBooleanType
4674 proceed;
4675
4676 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4677 (depth-i),depth);
4678 if (proceed == MagickFalse)
4679 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004680 }
4681 }
cristyb0d3bb92010-09-22 14:37:58 +00004682 stegano_view=DestroyCacheView(stegano_view);
4683 watermark_view=DestroyCacheView(watermark_view);
cristy3ed852e2009-09-05 21:47:34 +00004684 if (stegano_image->storage_class == PseudoClass)
4685 (void) SyncImage(stegano_image);
cristyda16f162011-02-19 23:52:17 +00004686 if (status == MagickFalse)
4687 {
4688 stegano_image=DestroyImage(stegano_image);
4689 return((Image *) NULL);
4690 }
cristy3ed852e2009-09-05 21:47:34 +00004691 return(stegano_image);
4692}
4693
4694/*
4695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4696% %
4697% %
4698% %
4699% S t e r e o A n a g l y p h I m a g e %
4700% %
4701% %
4702% %
4703%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4704%
4705% StereoAnaglyphImage() combines two images and produces a single image that
4706% is the composite of a left and right image of a stereo pair. Special
4707% red-green stereo glasses are required to view this effect.
4708%
4709% The format of the StereoAnaglyphImage method is:
4710%
4711% Image *StereoImage(const Image *left_image,const Image *right_image,
4712% ExceptionInfo *exception)
4713% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004714% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004715% ExceptionInfo *exception)
4716%
4717% A description of each parameter follows:
4718%
4719% o left_image: the left image.
4720%
4721% o right_image: the right image.
4722%
4723% o exception: return any errors or warnings in this structure.
4724%
4725% o x_offset: amount, in pixels, by which the left image is offset to the
4726% right of the right image.
4727%
4728% o y_offset: amount, in pixels, by which the left image is offset to the
4729% bottom of the right image.
4730%
4731%
4732*/
4733MagickExport Image *StereoImage(const Image *left_image,
4734 const Image *right_image,ExceptionInfo *exception)
4735{
4736 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4737}
4738
4739MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004740 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004741 ExceptionInfo *exception)
4742{
4743#define StereoImageTag "Stereo/Image"
4744
4745 const Image
4746 *image;
4747
4748 Image
4749 *stereo_image;
4750
cristy3ed852e2009-09-05 21:47:34 +00004751 MagickBooleanType
4752 status;
4753
cristy9d314ff2011-03-09 01:30:28 +00004754 ssize_t
4755 y;
4756
cristy3ed852e2009-09-05 21:47:34 +00004757 assert(left_image != (const Image *) NULL);
4758 assert(left_image->signature == MagickSignature);
4759 if (left_image->debug != MagickFalse)
4760 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4761 left_image->filename);
4762 assert(right_image != (const Image *) NULL);
4763 assert(right_image->signature == MagickSignature);
4764 assert(exception != (ExceptionInfo *) NULL);
4765 assert(exception->signature == MagickSignature);
4766 assert(right_image != (const Image *) NULL);
4767 image=left_image;
4768 if ((left_image->columns != right_image->columns) ||
4769 (left_image->rows != right_image->rows))
4770 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4771 /*
4772 Initialize stereo image attributes.
4773 */
4774 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4775 MagickTrue,exception);
4776 if (stereo_image == (Image *) NULL)
4777 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004778 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004779 {
cristy3ed852e2009-09-05 21:47:34 +00004780 stereo_image=DestroyImage(stereo_image);
4781 return((Image *) NULL);
4782 }
4783 /*
4784 Copy left image to red channel and right image to blue channel.
4785 */
cristyda16f162011-02-19 23:52:17 +00004786 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004787 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004788 {
cristy4c08aed2011-07-01 19:47:50 +00004789 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004790 *restrict p,
4791 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004792
cristybb503372010-05-27 20:51:26 +00004793 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004794 x;
4795
cristy4c08aed2011-07-01 19:47:50 +00004796 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004797 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004798
4799 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4800 exception);
4801 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4802 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004803 if ((p == (const Quantum *) NULL) ||
4804 (q == (const Quantum *) NULL) || (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004805 break;
cristybb503372010-05-27 20:51:26 +00004806 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004807 {
cristy4c08aed2011-07-01 19:47:50 +00004808 SetPixelRed(image,GetPixelRed(left_image,p),r);
4809 SetPixelGreen(image,GetPixelGreen(left_image,q),r);
4810 SetPixelBlue(image,GetPixelBlue(left_image,q),r);
4811 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4812 GetPixelAlpha(left_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00004813 p+=GetPixelChannels(left_image);
cristy3ed852e2009-09-05 21:47:34 +00004814 q++;
4815 r++;
4816 }
4817 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4818 break;
cristy8b27a6d2010-02-14 03:31:15 +00004819 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004820 {
cristy8b27a6d2010-02-14 03:31:15 +00004821 MagickBooleanType
4822 proceed;
4823
cristybb503372010-05-27 20:51:26 +00004824 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4825 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004826 if (proceed == MagickFalse)
4827 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004828 }
4829 }
cristyda16f162011-02-19 23:52:17 +00004830 if (status == MagickFalse)
4831 {
4832 stereo_image=DestroyImage(stereo_image);
4833 return((Image *) NULL);
4834 }
cristy3ed852e2009-09-05 21:47:34 +00004835 return(stereo_image);
4836}
4837
4838/*
4839%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4840% %
4841% %
4842% %
4843% S w i r l I m a g e %
4844% %
4845% %
4846% %
4847%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4848%
4849% SwirlImage() swirls the pixels about the center of the image, where
4850% degrees indicates the sweep of the arc through which each pixel is moved.
4851% You get a more dramatic effect as the degrees move from 1 to 360.
4852%
4853% The format of the SwirlImage method is:
4854%
4855% Image *SwirlImage(const Image *image,double degrees,
4856% ExceptionInfo *exception)
4857%
4858% A description of each parameter follows:
4859%
4860% o image: the image.
4861%
4862% o degrees: Define the tightness of the swirling effect.
4863%
4864% o exception: return any errors or warnings in this structure.
4865%
4866*/
4867MagickExport Image *SwirlImage(const Image *image,double degrees,
4868 ExceptionInfo *exception)
4869{
4870#define SwirlImageTag "Swirl/Image"
4871
cristyfa112112010-01-04 17:48:07 +00004872 CacheView
4873 *image_view,
4874 *swirl_view;
4875
cristy3ed852e2009-09-05 21:47:34 +00004876 Image
4877 *swirl_image;
4878
cristy3ed852e2009-09-05 21:47:34 +00004879 MagickBooleanType
4880 status;
4881
cristybb503372010-05-27 20:51:26 +00004882 MagickOffsetType
4883 progress;
4884
cristy4c08aed2011-07-01 19:47:50 +00004885 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004886 zero;
4887
4888 MagickRealType
4889 radius;
4890
4891 PointInfo
4892 center,
4893 scale;
4894
cristybb503372010-05-27 20:51:26 +00004895 ssize_t
4896 y;
4897
cristy3ed852e2009-09-05 21:47:34 +00004898 /*
4899 Initialize swirl image attributes.
4900 */
4901 assert(image != (const Image *) NULL);
4902 assert(image->signature == MagickSignature);
4903 if (image->debug != MagickFalse)
4904 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4905 assert(exception != (ExceptionInfo *) NULL);
4906 assert(exception->signature == MagickSignature);
4907 swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4908 if (swirl_image == (Image *) NULL)
4909 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004910 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004911 {
cristy3ed852e2009-09-05 21:47:34 +00004912 swirl_image=DestroyImage(swirl_image);
4913 return((Image *) NULL);
4914 }
cristy4c08aed2011-07-01 19:47:50 +00004915 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004916 swirl_image->matte=MagickTrue;
4917 /*
4918 Compute scaling factor.
4919 */
4920 center.x=(double) image->columns/2.0;
4921 center.y=(double) image->rows/2.0;
4922 radius=MagickMax(center.x,center.y);
4923 scale.x=1.0;
4924 scale.y=1.0;
4925 if (image->columns > image->rows)
4926 scale.y=(double) image->columns/(double) image->rows;
4927 else
4928 if (image->columns < image->rows)
4929 scale.x=(double) image->rows/(double) image->columns;
4930 degrees=(double) DegreesToRadians(degrees);
4931 /*
4932 Swirl image.
4933 */
4934 status=MagickTrue;
4935 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00004936 GetPixelInfo(swirl_image,&zero);
cristy3ed852e2009-09-05 21:47:34 +00004937 image_view=AcquireCacheView(image);
4938 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00004939#if defined(MAGICKCORE_OPENMP_SUPPORT)
4940 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004941#endif
cristybb503372010-05-27 20:51:26 +00004942 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004943 {
cristy4c08aed2011-07-01 19:47:50 +00004944 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004945 pixel;
4946
4947 MagickRealType
4948 distance;
4949
4950 PointInfo
4951 delta;
4952
cristybb503372010-05-27 20:51:26 +00004953 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004954 x;
4955
cristy4c08aed2011-07-01 19:47:50 +00004956 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004957 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004958
4959 if (status == MagickFalse)
4960 continue;
4961 q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
4962 exception);
cristy4c08aed2011-07-01 19:47:50 +00004963 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004964 {
4965 status=MagickFalse;
4966 continue;
4967 }
cristy3ed852e2009-09-05 21:47:34 +00004968 delta.y=scale.y*(double) (y-center.y);
4969 pixel=zero;
cristybb503372010-05-27 20:51:26 +00004970 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004971 {
4972 /*
4973 Determine if the pixel is within an ellipse.
4974 */
4975 delta.x=scale.x*(double) (x-center.x);
4976 distance=delta.x*delta.x+delta.y*delta.y;
4977 if (distance < (radius*radius))
4978 {
4979 MagickRealType
4980 cosine,
4981 factor,
4982 sine;
4983
4984 /*
4985 Swirl the pixel.
4986 */
4987 factor=1.0-sqrt((double) distance)/radius;
4988 sine=sin((double) (degrees*factor*factor));
4989 cosine=cos((double) (degrees*factor*factor));
cristy4c08aed2011-07-01 19:47:50 +00004990 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00004991 UndefinedInterpolatePixel,(double) ((cosine*delta.x-sine*delta.y)/
4992 scale.x+center.x),(double) ((sine*delta.x+cosine*delta.y)/scale.y+
cristyd76c51e2011-03-26 00:21:26 +00004993 center.y),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00004994 SetPixelPixelInfo(swirl_image,&pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00004995 }
cristyed231572011-07-14 02:18:59 +00004996 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00004997 }
4998 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
4999 status=MagickFalse;
5000 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5001 {
5002 MagickBooleanType
5003 proceed;
5004
cristyb5d5f722009-11-04 03:03:49 +00005005#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005006 #pragma omp critical (MagickCore_SwirlImage)
5007#endif
5008 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5009 if (proceed == MagickFalse)
5010 status=MagickFalse;
5011 }
5012 }
5013 swirl_view=DestroyCacheView(swirl_view);
5014 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005015 if (status == MagickFalse)
5016 swirl_image=DestroyImage(swirl_image);
5017 return(swirl_image);
5018}
5019
5020/*
5021%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5022% %
5023% %
5024% %
5025% T i n t I m a g e %
5026% %
5027% %
5028% %
5029%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5030%
5031% TintImage() applies a color vector to each pixel in the image. The length
5032% of the vector is 0 for black and white and at its maximum for the midtones.
5033% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5034%
5035% The format of the TintImage method is:
5036%
5037% Image *TintImage(const Image *image,const char *opacity,
5038% const PixelPacket tint,ExceptionInfo *exception)
5039%
5040% A description of each parameter follows:
5041%
5042% o image: the image.
5043%
5044% o opacity: A color value used for tinting.
5045%
5046% o tint: A color value used for tinting.
5047%
5048% o exception: return any errors or warnings in this structure.
5049%
5050*/
5051MagickExport Image *TintImage(const Image *image,const char *opacity,
5052 const PixelPacket tint,ExceptionInfo *exception)
5053{
5054#define TintImageTag "Tint/Image"
5055
cristyc4c8d132010-01-07 01:58:38 +00005056 CacheView
5057 *image_view,
5058 *tint_view;
5059
cristy3ed852e2009-09-05 21:47:34 +00005060 GeometryInfo
5061 geometry_info;
5062
5063 Image
5064 *tint_image;
5065
cristy3ed852e2009-09-05 21:47:34 +00005066 MagickBooleanType
5067 status;
5068
cristybb503372010-05-27 20:51:26 +00005069 MagickOffsetType
5070 progress;
cristy3ed852e2009-09-05 21:47:34 +00005071
cristy4c08aed2011-07-01 19:47:50 +00005072 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005073 color_vector,
5074 pixel;
5075
cristybb503372010-05-27 20:51:26 +00005076 MagickStatusType
5077 flags;
5078
5079 ssize_t
5080 y;
5081
cristy3ed852e2009-09-05 21:47:34 +00005082 /*
5083 Allocate tint image.
5084 */
5085 assert(image != (const Image *) NULL);
5086 assert(image->signature == MagickSignature);
5087 if (image->debug != MagickFalse)
5088 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5089 assert(exception != (ExceptionInfo *) NULL);
5090 assert(exception->signature == MagickSignature);
5091 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5092 if (tint_image == (Image *) NULL)
5093 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005094 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005095 {
cristy3ed852e2009-09-05 21:47:34 +00005096 tint_image=DestroyImage(tint_image);
5097 return((Image *) NULL);
5098 }
5099 if (opacity == (const char *) NULL)
5100 return(tint_image);
5101 /*
5102 Determine RGB values of the color.
5103 */
5104 flags=ParseGeometry(opacity,&geometry_info);
5105 pixel.red=geometry_info.rho;
5106 if ((flags & SigmaValue) != 0)
5107 pixel.green=geometry_info.sigma;
5108 else
5109 pixel.green=pixel.red;
5110 if ((flags & XiValue) != 0)
5111 pixel.blue=geometry_info.xi;
5112 else
5113 pixel.blue=pixel.red;
5114 if ((flags & PsiValue) != 0)
cristy4c08aed2011-07-01 19:47:50 +00005115 pixel.alpha=geometry_info.psi;
cristy3ed852e2009-09-05 21:47:34 +00005116 else
cristy4c08aed2011-07-01 19:47:50 +00005117 pixel.alpha=(MagickRealType) OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005118 color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
cristy4c08aed2011-07-01 19:47:50 +00005119 GetPixelPacketIntensity(&tint));
cristy3ed852e2009-09-05 21:47:34 +00005120 color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
cristy4c08aed2011-07-01 19:47:50 +00005121 GetPixelPacketIntensity(&tint));
cristy3ed852e2009-09-05 21:47:34 +00005122 color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
cristy4c08aed2011-07-01 19:47:50 +00005123 GetPixelPacketIntensity(&tint));
cristy3ed852e2009-09-05 21:47:34 +00005124 /*
5125 Tint image.
5126 */
5127 status=MagickTrue;
5128 progress=0;
5129 image_view=AcquireCacheView(image);
5130 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005131#if defined(MAGICKCORE_OPENMP_SUPPORT)
5132 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005133#endif
cristybb503372010-05-27 20:51:26 +00005134 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005135 {
cristy4c08aed2011-07-01 19:47:50 +00005136 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005137 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005138
cristy4c08aed2011-07-01 19:47:50 +00005139 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005140 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005141
cristy6b91acb2011-04-19 12:23:54 +00005142 register ssize_t
5143 x;
5144
cristy3ed852e2009-09-05 21:47:34 +00005145 if (status == MagickFalse)
5146 continue;
5147 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5148 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5149 exception);
cristy4c08aed2011-07-01 19:47:50 +00005150 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005151 {
5152 status=MagickFalse;
5153 continue;
5154 }
cristybb503372010-05-27 20:51:26 +00005155 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005156 {
cristy4c08aed2011-07-01 19:47:50 +00005157 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005158 pixel;
5159
5160 MagickRealType
5161 weight;
5162
cristy4c08aed2011-07-01 19:47:50 +00005163 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5164 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5165 (1.0-(4.0*(weight*weight)));
5166 SetPixelRed(tint_image,ClampToQuantum(pixel.red),q);
5167 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5168 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5169 (1.0-(4.0*(weight*weight)));
5170 SetPixelGreen(tint_image,ClampToQuantum(pixel.green),q);
5171 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5172 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5173 (1.0-(4.0*(weight*weight)));
5174 SetPixelBlue(tint_image,ClampToQuantum(pixel.blue),q);
5175 SetPixelAlpha(tint_image,GetPixelAlpha(image,p),q);
cristyed231572011-07-14 02:18:59 +00005176 p+=GetPixelChannels(image);
5177 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005178 }
5179 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5180 status=MagickFalse;
5181 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5182 {
5183 MagickBooleanType
5184 proceed;
5185
cristyb5d5f722009-11-04 03:03:49 +00005186#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005187 #pragma omp critical (MagickCore_TintImage)
5188#endif
5189 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5190 if (proceed == MagickFalse)
5191 status=MagickFalse;
5192 }
5193 }
5194 tint_view=DestroyCacheView(tint_view);
5195 image_view=DestroyCacheView(image_view);
5196 if (status == MagickFalse)
5197 tint_image=DestroyImage(tint_image);
5198 return(tint_image);
5199}
5200
5201/*
5202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5203% %
5204% %
5205% %
5206% V i g n e t t e I m a g e %
5207% %
5208% %
5209% %
5210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5211%
5212% VignetteImage() softens the edges of the image in vignette style.
5213%
5214% The format of the VignetteImage method is:
5215%
5216% Image *VignetteImage(const Image *image,const double radius,
cristyb3a73b52011-07-26 01:34:43 +00005217% const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005218%
5219% A description of each parameter follows:
5220%
5221% o image: the image.
5222%
5223% o radius: the radius of the pixel neighborhood.
5224%
5225% o sigma: the standard deviation of the Gaussian, in pixels.
5226%
5227% o x, y: Define the x and y ellipse offset.
5228%
5229% o exception: return any errors or warnings in this structure.
5230%
5231*/
5232MagickExport Image *VignetteImage(const Image *image,const double radius,
cristybb503372010-05-27 20:51:26 +00005233 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005234{
5235 char
5236 ellipse[MaxTextExtent];
5237
5238 DrawInfo
5239 *draw_info;
5240
5241 Image
5242 *canvas_image,
5243 *blur_image,
5244 *oval_image,
5245 *vignette_image;
5246
5247 assert(image != (Image *) NULL);
5248 assert(image->signature == MagickSignature);
5249 if (image->debug != MagickFalse)
5250 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5251 assert(exception != (ExceptionInfo *) NULL);
5252 assert(exception->signature == MagickSignature);
5253 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5254 if (canvas_image == (Image *) NULL)
5255 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005256 if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005257 {
cristy3ed852e2009-09-05 21:47:34 +00005258 canvas_image=DestroyImage(canvas_image);
5259 return((Image *) NULL);
5260 }
5261 canvas_image->matte=MagickTrue;
cristyb3a73b52011-07-26 01:34:43 +00005262 oval_image=CloneImage(canvas_image,canvas_image->columns,
5263 canvas_image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005264 if (oval_image == (Image *) NULL)
5265 {
5266 canvas_image=DestroyImage(canvas_image);
5267 return((Image *) NULL);
5268 }
5269 (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5270 (void) SetImageBackgroundColor(oval_image);
5271 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5272 (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5273 (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
cristyb51dff52011-05-19 16:55:47 +00005274 (void) FormatLocaleString(ellipse,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00005275 "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
cristy8cd5b312010-01-07 01:10:24 +00005276 image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005277 draw_info->primitive=AcquireString(ellipse);
5278 (void) DrawImage(oval_image,draw_info);
5279 draw_info=DestroyDrawInfo(draw_info);
5280 blur_image=BlurImage(oval_image,radius,sigma,exception);
5281 oval_image=DestroyImage(oval_image);
5282 if (blur_image == (Image *) NULL)
5283 {
5284 canvas_image=DestroyImage(canvas_image);
5285 return((Image *) NULL);
5286 }
5287 blur_image->matte=MagickFalse;
5288 (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5289 blur_image=DestroyImage(blur_image);
5290 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5291 canvas_image=DestroyImage(canvas_image);
5292 return(vignette_image);
5293}
5294
5295/*
5296%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5297% %
5298% %
5299% %
5300% W a v e I m a g e %
5301% %
5302% %
5303% %
5304%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5305%
5306% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005307% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005308% by the given parameters.
5309%
5310% The format of the WaveImage method is:
5311%
5312% Image *WaveImage(const Image *image,const double amplitude,
5313% const double wave_length,ExceptionInfo *exception)
5314%
5315% A description of each parameter follows:
5316%
5317% o image: the image.
5318%
5319% o amplitude, wave_length: Define the amplitude and wave length of the
5320% sine wave.
5321%
5322% o exception: return any errors or warnings in this structure.
5323%
5324*/
5325MagickExport Image *WaveImage(const Image *image,const double amplitude,
5326 const double wave_length,ExceptionInfo *exception)
5327{
5328#define WaveImageTag "Wave/Image"
5329
cristyfa112112010-01-04 17:48:07 +00005330 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005331 *image_view,
cristyfa112112010-01-04 17:48:07 +00005332 *wave_view;
5333
cristy3ed852e2009-09-05 21:47:34 +00005334 Image
5335 *wave_image;
5336
cristy3ed852e2009-09-05 21:47:34 +00005337 MagickBooleanType
5338 status;
5339
cristybb503372010-05-27 20:51:26 +00005340 MagickOffsetType
5341 progress;
5342
cristy4c08aed2011-07-01 19:47:50 +00005343 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005344 zero;
5345
5346 MagickRealType
5347 *sine_map;
5348
cristybb503372010-05-27 20:51:26 +00005349 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005350 i;
5351
cristybb503372010-05-27 20:51:26 +00005352 ssize_t
5353 y;
5354
cristy3ed852e2009-09-05 21:47:34 +00005355 /*
5356 Initialize wave image attributes.
5357 */
5358 assert(image != (Image *) NULL);
5359 assert(image->signature == MagickSignature);
5360 if (image->debug != MagickFalse)
5361 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5362 assert(exception != (ExceptionInfo *) NULL);
5363 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005364 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005365 fabs(amplitude)),MagickTrue,exception);
5366 if (wave_image == (Image *) NULL)
5367 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005368 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005369 {
cristy3ed852e2009-09-05 21:47:34 +00005370 wave_image=DestroyImage(wave_image);
5371 return((Image *) NULL);
5372 }
cristy4c08aed2011-07-01 19:47:50 +00005373 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005374 wave_image->matte=MagickTrue;
5375 /*
5376 Allocate sine map.
5377 */
5378 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5379 sizeof(*sine_map));
5380 if (sine_map == (MagickRealType *) NULL)
5381 {
5382 wave_image=DestroyImage(wave_image);
5383 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5384 }
cristybb503372010-05-27 20:51:26 +00005385 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005386 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5387 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005388 /*
5389 Wave image.
5390 */
5391 status=MagickTrue;
5392 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00005393 GetPixelInfo(wave_image,&zero);
cristyd76c51e2011-03-26 00:21:26 +00005394 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005395 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005396 (void) SetCacheViewVirtualPixelMethod(image_view,
5397 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005398#if defined(MAGICKCORE_OPENMP_SUPPORT)
5399 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005400#endif
cristybb503372010-05-27 20:51:26 +00005401 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005402 {
cristy4c08aed2011-07-01 19:47:50 +00005403 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005404 pixel;
5405
cristy4c08aed2011-07-01 19:47:50 +00005406 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005407 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005408
cristye97bb922011-04-03 01:36:52 +00005409 register ssize_t
5410 x;
5411
cristy3ed852e2009-09-05 21:47:34 +00005412 if (status == MagickFalse)
5413 continue;
5414 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5415 exception);
cristy4c08aed2011-07-01 19:47:50 +00005416 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005417 {
5418 status=MagickFalse;
5419 continue;
5420 }
cristy3ed852e2009-09-05 21:47:34 +00005421 pixel=zero;
cristybb503372010-05-27 20:51:26 +00005422 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005423 {
cristy4c08aed2011-07-01 19:47:50 +00005424 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00005425 UndefinedInterpolatePixel,(double) x,(double) (y-sine_map[x]),&pixel,
5426 exception);
cristy4c08aed2011-07-01 19:47:50 +00005427 SetPixelPixelInfo(wave_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005428 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005429 }
5430 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5431 status=MagickFalse;
5432 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5433 {
5434 MagickBooleanType
5435 proceed;
5436
cristyb5d5f722009-11-04 03:03:49 +00005437#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005438 #pragma omp critical (MagickCore_WaveImage)
5439#endif
5440 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5441 if (proceed == MagickFalse)
5442 status=MagickFalse;
5443 }
5444 }
5445 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005446 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005447 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5448 if (status == MagickFalse)
5449 wave_image=DestroyImage(wave_image);
5450 return(wave_image);
5451}