blob: d9aec0c222570ef129e3640d9b7e67b19a3dcfe9 [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% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 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"
cristyc53413d2011-11-17 13:04:26 +000053#include "MagickCore/distort.h"
cristy4c08aed2011-07-01 19:47:50 +000054#include "MagickCore/draw.h"
55#include "MagickCore/effect.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/fx.h"
60#include "MagickCore/fx-private.h"
61#include "MagickCore/gem.h"
cristy8ea81222011-09-04 10:33:32 +000062#include "MagickCore/gem-private.h"
cristy4c08aed2011-07-01 19:47:50 +000063#include "MagickCore/geometry.h"
64#include "MagickCore/layer.h"
65#include "MagickCore/list.h"
66#include "MagickCore/log.h"
67#include "MagickCore/image.h"
68#include "MagickCore/image-private.h"
69#include "MagickCore/magick.h"
70#include "MagickCore/memory_.h"
71#include "MagickCore/monitor.h"
72#include "MagickCore/monitor-private.h"
73#include "MagickCore/option.h"
74#include "MagickCore/pixel.h"
75#include "MagickCore/pixel-accessor.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum.h"
78#include "MagickCore/quantum-private.h"
79#include "MagickCore/random_.h"
80#include "MagickCore/random-private.h"
81#include "MagickCore/resample.h"
82#include "MagickCore/resample-private.h"
83#include "MagickCore/resize.h"
cristy4c08aed2011-07-01 19:47:50 +000084#include "MagickCore/splay-tree.h"
85#include "MagickCore/statistic.h"
86#include "MagickCore/string_.h"
87#include "MagickCore/string-private.h"
88#include "MagickCore/thread-private.h"
89#include "MagickCore/transform.h"
90#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000091
92/*
93 Define declarations.
94*/
95#define LeftShiftOperator 0xf5
96#define RightShiftOperator 0xf6
97#define LessThanEqualOperator 0xf7
98#define GreaterThanEqualOperator 0xf8
99#define EqualOperator 0xf9
100#define NotEqualOperator 0xfa
101#define LogicalAndOperator 0xfb
102#define LogicalOrOperator 0xfc
cristy116af162010-08-13 01:25:47 +0000103#define ExponentialNotation 0xfd
cristy3ed852e2009-09-05 21:47:34 +0000104
105struct _FxInfo
106{
107 const Image
108 *images;
109
cristy3ed852e2009-09-05 21:47:34 +0000110 char
111 *expression;
112
113 FILE
114 *file;
115
116 SplayTreeInfo
117 *colors,
118 *symbols;
119
cristyd76c51e2011-03-26 00:21:26 +0000120 CacheView
121 **view;
cristy3ed852e2009-09-05 21:47:34 +0000122
123 RandomInfo
124 *random_info;
125
126 ExceptionInfo
127 *exception;
128};
129
130/*
131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
132% %
133% %
134% %
135+ A c q u i r e F x I n f o %
136% %
137% %
138% %
139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140%
141% AcquireFxInfo() allocates the FxInfo structure.
142%
143% The format of the AcquireFxInfo method is:
144%
145% FxInfo *AcquireFxInfo(Image *image,const char *expression)
cristy0a9b3722010-10-23 18:45:49 +0000146%
cristy3ed852e2009-09-05 21:47:34 +0000147% A description of each parameter follows:
148%
149% o image: the image.
150%
151% o expression: the expression.
152%
153*/
cristy7832dc22011-09-05 01:21:53 +0000154MagickPrivate FxInfo *AcquireFxInfo(const Image *image,const char *expression)
cristy3ed852e2009-09-05 21:47:34 +0000155{
156 char
157 fx_op[2];
158
cristycb180922011-03-11 14:41:24 +0000159 const Image
160 *next;
161
cristy3ed852e2009-09-05 21:47:34 +0000162 FxInfo
163 *fx_info;
164
cristybb503372010-05-27 20:51:26 +0000165 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000166 i;
167
cristy73bd4a52010-10-05 11:24:23 +0000168 fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +0000169 if (fx_info == (FxInfo *) NULL)
170 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
171 (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
172 fx_info->exception=AcquireExceptionInfo();
anthony7d86e172011-03-23 12:37:06 +0000173 fx_info->images=image;
cristy3ed852e2009-09-05 21:47:34 +0000174 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
175 RelinquishMagickMemory);
176 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
177 RelinquishMagickMemory);
cristyd76c51e2011-03-26 00:21:26 +0000178 fx_info->view=(CacheView **) AcquireQuantumMemory(GetImageListLength(
179 fx_info->images),sizeof(*fx_info->view));
180 if (fx_info->view == (CacheView **) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000181 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristya2262262011-03-11 02:50:37 +0000182 i=0;
cristy0ea377f2011-03-24 00:54:19 +0000183 next=GetFirstImageInList(fx_info->images);
184 for ( ; next != (Image *) NULL; next=next->next)
cristy3ed852e2009-09-05 21:47:34 +0000185 {
cristyd76c51e2011-03-26 00:21:26 +0000186 fx_info->view[i]=AcquireCacheView(next);
cristya2262262011-03-11 02:50:37 +0000187 i++;
cristy3ed852e2009-09-05 21:47:34 +0000188 }
189 fx_info->random_info=AcquireRandomInfo();
190 fx_info->expression=ConstantString(expression);
191 fx_info->file=stderr;
192 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
cristy37af0912011-05-23 16:09:42 +0000193 /*
194 Force right-to-left associativity for unary negation.
195 */
196 (void) SubstituteString(&fx_info->expression,"-","-1.0*");
cristy8b8a3ae2010-10-23 18:49:46 +0000197 /*
cristy3ed852e2009-09-05 21:47:34 +0000198 Convert complex to simple operators.
199 */
200 fx_op[1]='\0';
201 *fx_op=(char) LeftShiftOperator;
202 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
203 *fx_op=(char) RightShiftOperator;
204 (void) SubstituteString(&fx_info->expression,">>",fx_op);
205 *fx_op=(char) LessThanEqualOperator;
206 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
207 *fx_op=(char) GreaterThanEqualOperator;
208 (void) SubstituteString(&fx_info->expression,">=",fx_op);
209 *fx_op=(char) EqualOperator;
210 (void) SubstituteString(&fx_info->expression,"==",fx_op);
211 *fx_op=(char) NotEqualOperator;
212 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
213 *fx_op=(char) LogicalAndOperator;
214 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
215 *fx_op=(char) LogicalOrOperator;
216 (void) SubstituteString(&fx_info->expression,"||",fx_op);
cristy116af162010-08-13 01:25:47 +0000217 *fx_op=(char) ExponentialNotation;
218 (void) SubstituteString(&fx_info->expression,"**",fx_op);
cristy3ed852e2009-09-05 21:47:34 +0000219 return(fx_info);
220}
221
222/*
223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
224% %
225% %
226% %
227% A d d N o i s e I m a g e %
228% %
229% %
230% %
231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
232%
233% AddNoiseImage() adds random noise to the image.
234%
235% The format of the AddNoiseImage method is:
236%
237% Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
cristy9ed1f812011-10-08 02:00:08 +0000238% const double attenuate,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000239%
240% A description of each parameter follows:
241%
242% o image: the image.
243%
244% o channel: the channel type.
245%
246% o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
247% Impulse, Laplacian, or Poisson.
248%
cristy9ed1f812011-10-08 02:00:08 +0000249% o attenuate: attenuate the random distribution.
250%
cristy3ed852e2009-09-05 21:47:34 +0000251% o exception: return any errors or warnings in this structure.
252%
253*/
cristy9ed1f812011-10-08 02:00:08 +0000254MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
255 const double attenuate,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000256{
257#define AddNoiseImageTag "AddNoise/Image"
258
cristyfa112112010-01-04 17:48:07 +0000259 CacheView
260 *image_view,
261 *noise_view;
262
cristy3ed852e2009-09-05 21:47:34 +0000263 Image
264 *noise_image;
265
cristy3ed852e2009-09-05 21:47:34 +0000266 MagickBooleanType
267 status;
268
cristybb503372010-05-27 20:51:26 +0000269 MagickOffsetType
270 progress;
271
cristy3ed852e2009-09-05 21:47:34 +0000272 RandomInfo
cristyfa112112010-01-04 17:48:07 +0000273 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +0000274
cristybb503372010-05-27 20:51:26 +0000275 ssize_t
276 y;
277
cristy3ed852e2009-09-05 21:47:34 +0000278 /*
279 Initialize noise image attributes.
280 */
281 assert(image != (const Image *) NULL);
282 assert(image->signature == MagickSignature);
283 if (image->debug != MagickFalse)
284 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
285 assert(exception != (ExceptionInfo *) NULL);
286 assert(exception->signature == MagickSignature);
cristyb2145892011-10-10 00:55:32 +0000287 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000288 if (noise_image == (Image *) NULL)
289 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000290 if (SetImageStorageClass(noise_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000291 {
cristy3ed852e2009-09-05 21:47:34 +0000292 noise_image=DestroyImage(noise_image);
293 return((Image *) NULL);
294 }
295 /*
296 Add noise in each row.
297 */
cristy3ed852e2009-09-05 21:47:34 +0000298 status=MagickTrue;
299 progress=0;
300 random_info=AcquireRandomInfoThreadSet();
301 image_view=AcquireCacheView(image);
302 noise_view=AcquireCacheView(noise_image);
cristy319a1e72010-02-21 15:13:11 +0000303#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000304 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000305#endif
cristybb503372010-05-27 20:51:26 +0000306 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000307 {
cristy5c9e6f22010-09-17 17:31:01 +0000308 const int
309 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000310
cristy3ed852e2009-09-05 21:47:34 +0000311 MagickBooleanType
312 sync;
313
cristy4c08aed2011-07-01 19:47:50 +0000314 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000315 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000316
cristybb503372010-05-27 20:51:26 +0000317 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000318 x;
319
cristy4c08aed2011-07-01 19:47:50 +0000320 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000321 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000322
323 if (status == MagickFalse)
324 continue;
325 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +0000326 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +0000327 exception);
cristy4c08aed2011-07-01 19:47:50 +0000328 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000329 {
330 status=MagickFalse;
331 continue;
332 }
cristybb503372010-05-27 20:51:26 +0000333 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000334 {
cristy850b3072011-10-08 01:38:05 +0000335 register ssize_t
336 i;
337
338 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
339 {
340 PixelChannel
341 channel;
342
343 PixelTrait
344 noise_traits,
345 traits;
346
cristye2a912b2011-12-05 20:02:07 +0000347 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000348 traits=GetPixelChannelMapTraits(image,channel);
cristy850b3072011-10-08 01:38:05 +0000349 noise_traits=GetPixelChannelMapTraits(noise_image,channel);
350 if ((traits == UndefinedPixelTrait) ||
351 (noise_traits == UndefinedPixelTrait))
352 continue;
cristyec9e3a62012-02-01 02:09:32 +0000353 if (((noise_traits & CopyPixelTrait) != 0) ||
354 (GetPixelMask(image,p) != 0))
cristyb2145892011-10-10 00:55:32 +0000355 {
356 SetPixelChannel(noise_image,channel,p[i],q);
357 continue;
358 }
cristy850b3072011-10-08 01:38:05 +0000359 SetPixelChannel(noise_image,channel,ClampToQuantum(
360 GenerateDifferentialNoise(random_info[id],p[i],noise_type,attenuate)),
361 q);
362 }
cristyed231572011-07-14 02:18:59 +0000363 p+=GetPixelChannels(image);
364 q+=GetPixelChannels(noise_image);
cristy3ed852e2009-09-05 21:47:34 +0000365 }
366 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
367 if (sync == MagickFalse)
368 status=MagickFalse;
369 if (image->progress_monitor != (MagickProgressMonitor) NULL)
370 {
371 MagickBooleanType
372 proceed;
373
cristyb5d5f722009-11-04 03:03:49 +0000374#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy319a1e72010-02-21 15:13:11 +0000375 #pragma omp critical (MagickCore_AddNoiseImage)
cristy3ed852e2009-09-05 21:47:34 +0000376#endif
377 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
378 image->rows);
379 if (proceed == MagickFalse)
380 status=MagickFalse;
381 }
382 }
383 noise_view=DestroyCacheView(noise_view);
384 image_view=DestroyCacheView(image_view);
385 random_info=DestroyRandomInfoThreadSet(random_info);
386 if (status == MagickFalse)
387 noise_image=DestroyImage(noise_image);
388 return(noise_image);
389}
390
391/*
392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
393% %
394% %
395% %
396% B l u e S h i f t I m a g e %
397% %
398% %
399% %
400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
401%
402% BlueShiftImage() mutes the colors of the image to simulate a scene at
403% nighttime in the moonlight.
404%
405% The format of the BlueShiftImage method is:
406%
407% Image *BlueShiftImage(const Image *image,const double factor,
408% ExceptionInfo *exception)
409%
410% A description of each parameter follows:
411%
412% o image: the image.
413%
414% o factor: the shift factor.
415%
416% o exception: return any errors or warnings in this structure.
417%
418*/
419MagickExport Image *BlueShiftImage(const Image *image,const double factor,
420 ExceptionInfo *exception)
421{
422#define BlueShiftImageTag "BlueShift/Image"
423
cristyc4c8d132010-01-07 01:58:38 +0000424 CacheView
425 *image_view,
426 *shift_view;
427
cristy3ed852e2009-09-05 21:47:34 +0000428 Image
429 *shift_image;
430
cristy3ed852e2009-09-05 21:47:34 +0000431 MagickBooleanType
432 status;
433
cristybb503372010-05-27 20:51:26 +0000434 MagickOffsetType
435 progress;
436
437 ssize_t
438 y;
439
cristy3ed852e2009-09-05 21:47:34 +0000440 /*
441 Allocate blue shift image.
442 */
443 assert(image != (const Image *) NULL);
444 assert(image->signature == MagickSignature);
445 if (image->debug != MagickFalse)
446 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
447 assert(exception != (ExceptionInfo *) NULL);
448 assert(exception->signature == MagickSignature);
cristya6d7a9b2012-01-18 20:04:48 +0000449 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000450 if (shift_image == (Image *) NULL)
451 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000452 if (SetImageStorageClass(shift_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000453 {
cristy3ed852e2009-09-05 21:47:34 +0000454 shift_image=DestroyImage(shift_image);
455 return((Image *) NULL);
456 }
457 /*
458 Blue-shift DirectClass image.
459 */
460 status=MagickTrue;
461 progress=0;
462 image_view=AcquireCacheView(image);
463 shift_view=AcquireCacheView(shift_image);
cristy319a1e72010-02-21 15:13:11 +0000464#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000465 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000466#endif
cristybb503372010-05-27 20:51:26 +0000467 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000468 {
469 MagickBooleanType
470 sync;
471
cristy4c08aed2011-07-01 19:47:50 +0000472 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000473 pixel;
474
475 Quantum
476 quantum;
477
cristy4c08aed2011-07-01 19:47:50 +0000478 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000479 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000480
cristybb503372010-05-27 20:51:26 +0000481 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000482 x;
483
cristy4c08aed2011-07-01 19:47:50 +0000484 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000485 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000486
487 if (status == MagickFalse)
488 continue;
489 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
490 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
491 exception);
cristy4c08aed2011-07-01 19:47:50 +0000492 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000493 {
494 status=MagickFalse;
495 continue;
496 }
cristybb503372010-05-27 20:51:26 +0000497 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000498 {
cristy4c08aed2011-07-01 19:47:50 +0000499 quantum=GetPixelRed(image,p);
500 if (GetPixelGreen(image,p) < quantum)
501 quantum=GetPixelGreen(image,p);
502 if (GetPixelBlue(image,p) < quantum)
503 quantum=GetPixelBlue(image,p);
504 pixel.red=0.5*(GetPixelRed(image,p)+factor*quantum);
505 pixel.green=0.5*(GetPixelGreen(image,p)+factor*quantum);
506 pixel.blue=0.5*(GetPixelBlue(image,p)+factor*quantum);
507 quantum=GetPixelRed(image,p);
508 if (GetPixelGreen(image,p) > quantum)
509 quantum=GetPixelGreen(image,p);
510 if (GetPixelBlue(image,p) > quantum)
511 quantum=GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000512 pixel.red=0.5*(pixel.red+factor*quantum);
513 pixel.green=0.5*(pixel.green+factor*quantum);
514 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristy4c08aed2011-07-01 19:47:50 +0000515 SetPixelRed(shift_image,ClampToQuantum(pixel.red),q);
516 SetPixelGreen(shift_image,ClampToQuantum(pixel.green),q);
517 SetPixelBlue(shift_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000518 p+=GetPixelChannels(image);
519 q+=GetPixelChannels(shift_image);
cristy3ed852e2009-09-05 21:47:34 +0000520 }
521 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
522 if (sync == MagickFalse)
523 status=MagickFalse;
524 if (image->progress_monitor != (MagickProgressMonitor) NULL)
525 {
526 MagickBooleanType
527 proceed;
528
cristy319a1e72010-02-21 15:13:11 +0000529#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000530 #pragma omp critical (MagickCore_BlueShiftImage)
cristy3ed852e2009-09-05 21:47:34 +0000531#endif
532 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
533 image->rows);
534 if (proceed == MagickFalse)
535 status=MagickFalse;
536 }
537 }
538 image_view=DestroyCacheView(image_view);
539 shift_view=DestroyCacheView(shift_view);
540 if (status == MagickFalse)
541 shift_image=DestroyImage(shift_image);
542 return(shift_image);
543}
544
545/*
546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547% %
548% %
549% %
550% C h a r c o a l I m a g e %
551% %
552% %
553% %
554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555%
556% CharcoalImage() creates a new image that is a copy of an existing one with
557% the edge highlighted. It allocates the memory necessary for the new Image
558% structure and returns a pointer to the new image.
559%
560% The format of the CharcoalImage method is:
561%
562% Image *CharcoalImage(const Image *image,const double radius,
cristy05c0c9a2011-09-05 23:16:13 +0000563% const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000564%
565% A description of each parameter follows:
566%
567% o image: the image.
568%
569% o radius: the radius of the pixel neighborhood.
570%
571% o sigma: the standard deviation of the Gaussian, in pixels.
572%
cristy05c0c9a2011-09-05 23:16:13 +0000573% o bias: the bias.
574%
cristy3ed852e2009-09-05 21:47:34 +0000575% o exception: return any errors or warnings in this structure.
576%
577*/
578MagickExport Image *CharcoalImage(const Image *image,const double radius,
cristy05c0c9a2011-09-05 23:16:13 +0000579 const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000580{
581 Image
582 *charcoal_image,
583 *clone_image,
584 *edge_image;
585
586 assert(image != (Image *) NULL);
587 assert(image->signature == MagickSignature);
588 if (image->debug != MagickFalse)
589 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
590 assert(exception != (ExceptionInfo *) NULL);
591 assert(exception->signature == MagickSignature);
592 clone_image=CloneImage(image,0,0,MagickTrue,exception);
593 if (clone_image == (Image *) NULL)
594 return((Image *) NULL);
cristy018f07f2011-09-04 21:15:19 +0000595 (void) SetImageType(clone_image,GrayscaleType,exception);
cristy8ae632d2011-09-05 17:29:53 +0000596 edge_image=EdgeImage(clone_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000597 clone_image=DestroyImage(clone_image);
598 if (edge_image == (Image *) NULL)
599 return((Image *) NULL);
cristy05c0c9a2011-09-05 23:16:13 +0000600 charcoal_image=BlurImage(edge_image,radius,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +0000601 edge_image=DestroyImage(edge_image);
602 if (charcoal_image == (Image *) NULL)
603 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +0000604 (void) NormalizeImage(charcoal_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +0000605 (void) NegateImage(charcoal_image,MagickFalse,exception);
cristy018f07f2011-09-04 21:15:19 +0000606 (void) SetImageType(charcoal_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000607 return(charcoal_image);
608}
609
610/*
611%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
612% %
613% %
614% %
615% C o l o r i z e I m a g e %
616% %
617% %
618% %
619%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
620%
621% ColorizeImage() blends the fill color with each pixel in the image.
622% A percentage blend is specified with opacity. Control the application
623% of different color components by specifying a different percentage for
624% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
625%
626% The format of the ColorizeImage method is:
627%
cristyc7e6ff62011-10-03 13:46:11 +0000628% Image *ColorizeImage(const Image *image,const char *blend,
629% const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000630%
631% A description of each parameter follows:
632%
633% o image: the image.
634%
cristyc7e6ff62011-10-03 13:46:11 +0000635% o blend: A character string indicating the level of blending as a
cristy3ed852e2009-09-05 21:47:34 +0000636% percentage.
637%
638% o colorize: A color value.
639%
640% o exception: return any errors or warnings in this structure.
641%
642*/
cristyc7e6ff62011-10-03 13:46:11 +0000643MagickExport Image *ColorizeImage(const Image *image,const char *blend,
644 const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000645{
646#define ColorizeImageTag "Colorize/Image"
647
cristyc4c8d132010-01-07 01:58:38 +0000648 CacheView
649 *colorize_view,
650 *image_view;
651
cristy3ed852e2009-09-05 21:47:34 +0000652 GeometryInfo
653 geometry_info;
654
655 Image
656 *colorize_image;
657
cristy3ed852e2009-09-05 21:47:34 +0000658 MagickBooleanType
659 status;
660
cristybb503372010-05-27 20:51:26 +0000661 MagickOffsetType
662 progress;
663
cristy3ed852e2009-09-05 21:47:34 +0000664 MagickStatusType
665 flags;
666
cristyc7e6ff62011-10-03 13:46:11 +0000667 PixelInfo
668 pixel;
669
cristybb503372010-05-27 20:51:26 +0000670 ssize_t
671 y;
672
cristy3ed852e2009-09-05 21:47:34 +0000673 /*
674 Allocate colorized image.
675 */
676 assert(image != (const Image *) NULL);
677 assert(image->signature == MagickSignature);
678 if (image->debug != MagickFalse)
679 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
680 assert(exception != (ExceptionInfo *) NULL);
681 assert(exception->signature == MagickSignature);
682 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
683 exception);
684 if (colorize_image == (Image *) NULL)
685 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000686 if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000687 {
cristy3ed852e2009-09-05 21:47:34 +0000688 colorize_image=DestroyImage(colorize_image);
689 return((Image *) NULL);
690 }
cristy5b67d4e2012-02-07 19:43:53 +0000691 if ((colorize->matte != MagickFalse) &&
692 (colorize_image->matte == MagickFalse))
693 (void) SetImageAlpha(colorize_image,OpaqueAlpha,exception);
cristyc7e6ff62011-10-03 13:46:11 +0000694 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000695 return(colorize_image);
696 /*
anthonyfd706f92012-01-19 04:22:02 +0000697 Determine RGB values of the fill color for pixel
cristy3ed852e2009-09-05 21:47:34 +0000698 */
cristyc7e6ff62011-10-03 13:46:11 +0000699 GetPixelInfo(image,&pixel);
cristyb817c3f2011-10-03 14:00:35 +0000700 flags=ParseGeometry(blend,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000701 pixel.red=geometry_info.rho;
702 pixel.green=geometry_info.rho;
703 pixel.blue=geometry_info.rho;
cristyc7e6ff62011-10-03 13:46:11 +0000704 pixel.alpha=100.0;
cristy3ed852e2009-09-05 21:47:34 +0000705 if ((flags & SigmaValue) != 0)
706 pixel.green=geometry_info.sigma;
707 if ((flags & XiValue) != 0)
708 pixel.blue=geometry_info.xi;
709 if ((flags & PsiValue) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000710 pixel.alpha=geometry_info.psi;
cristyc7e6ff62011-10-03 13:46:11 +0000711 if (pixel.colorspace == CMYKColorspace)
712 {
713 pixel.black=geometry_info.rho;
714 if ((flags & PsiValue) != 0)
715 pixel.black=geometry_info.psi;
716 if ((flags & ChiValue) != 0)
717 pixel.alpha=geometry_info.chi;
718 }
cristy3ed852e2009-09-05 21:47:34 +0000719 /*
720 Colorize DirectClass image.
721 */
722 status=MagickTrue;
723 progress=0;
724 image_view=AcquireCacheView(image);
725 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000726#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000727 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000728#endif
cristybb503372010-05-27 20:51:26 +0000729 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000730 {
731 MagickBooleanType
732 sync;
733
cristy4c08aed2011-07-01 19:47:50 +0000734 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000735 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000736
cristybb503372010-05-27 20:51:26 +0000737 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000738 x;
739
cristy4c08aed2011-07-01 19:47:50 +0000740 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000741 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000742
743 if (status == MagickFalse)
744 continue;
745 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
746 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
747 exception);
cristy4c08aed2011-07-01 19:47:50 +0000748 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000749 {
750 status=MagickFalse;
751 continue;
752 }
cristybb503372010-05-27 20:51:26 +0000753 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000754 {
cristyc7e6ff62011-10-03 13:46:11 +0000755 register ssize_t
756 i;
757
758 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
759 {
760 PixelChannel
761 channel;
762
763 PixelTrait
764 colorize_traits,
765 traits;
766
cristye2a912b2011-12-05 20:02:07 +0000767 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000768 traits=GetPixelChannelMapTraits(image,channel);
cristyc7e6ff62011-10-03 13:46:11 +0000769 colorize_traits=GetPixelChannelMapTraits(colorize_image,channel);
770 if ((traits == UndefinedPixelTrait) ||
771 (colorize_traits == UndefinedPixelTrait))
772 continue;
cristyd09f8802012-02-04 16:44:10 +0000773 if (((colorize_traits & CopyPixelTrait) != 0) ||
774 (GetPixelMask(image,p) != 0))
cristyc7e6ff62011-10-03 13:46:11 +0000775 {
776 SetPixelChannel(colorize_image,channel,p[i],q);
777 continue;
778 }
779 switch (channel)
780 {
781 case RedPixelChannel:
782 {
783 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
784 (100.0-pixel.red)+colorize->red*pixel.red)/100.0),q);
785 break;
786 }
787 case GreenPixelChannel:
788 {
789 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
790 (100.0-pixel.green)+colorize->green*pixel.green)/100.0),q);
791 break;
792 }
793 case BluePixelChannel:
794 {
795 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
796 (100.0-pixel.blue)+colorize->blue*pixel.blue)/100.0),q);
797 break;
798 }
799 case BlackPixelChannel:
800 {
801 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
802 (100.0-pixel.black)+colorize->black*pixel.black)/100.0),q);
803 break;
804 }
805 case AlphaPixelChannel:
806 {
807 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
808 (100.0-pixel.alpha)+colorize->alpha*pixel.alpha)/100.0),q);
809 break;
810 }
811 default:
812 {
813 SetPixelChannel(colorize_image,channel,p[i],q);
814 break;
815 }
816 }
817 }
cristyed231572011-07-14 02:18:59 +0000818 p+=GetPixelChannels(image);
819 q+=GetPixelChannels(colorize_image);
cristy3ed852e2009-09-05 21:47:34 +0000820 }
821 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
822 if (sync == MagickFalse)
823 status=MagickFalse;
824 if (image->progress_monitor != (MagickProgressMonitor) NULL)
825 {
826 MagickBooleanType
827 proceed;
828
cristy319a1e72010-02-21 15:13:11 +0000829#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000830 #pragma omp critical (MagickCore_ColorizeImage)
cristy3ed852e2009-09-05 21:47:34 +0000831#endif
832 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
833 if (proceed == MagickFalse)
834 status=MagickFalse;
835 }
836 }
837 image_view=DestroyCacheView(image_view);
838 colorize_view=DestroyCacheView(colorize_view);
839 if (status == MagickFalse)
840 colorize_image=DestroyImage(colorize_image);
841 return(colorize_image);
842}
843
844/*
845%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
846% %
847% %
848% %
cristye6365592010-04-02 17:31:23 +0000849% C o l o r M a t r i x I m a g e %
850% %
851% %
852% %
853%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
854%
855% ColorMatrixImage() applies color transformation to an image. This method
856% permits saturation changes, hue rotation, luminance to alpha, and various
857% other effects. Although variable-sized transformation matrices can be used,
858% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
859% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
860% except offsets are in column 6 rather than 5 (in support of CMYKA images)
861% and offsets are normalized (divide Flash offset by 255).
862%
863% The format of the ColorMatrixImage method is:
864%
865% Image *ColorMatrixImage(const Image *image,
866% const KernelInfo *color_matrix,ExceptionInfo *exception)
867%
868% A description of each parameter follows:
869%
870% o image: the image.
871%
872% o color_matrix: the color matrix.
873%
874% o exception: return any errors or warnings in this structure.
875%
876*/
anthonyfd706f92012-01-19 04:22:02 +0000877/* FUTURE: modify to make use of a MagickMatrix Mutliply function
878 That should be provided in "matrix.c"
879 (ASIDE: actually distorts should do this too but currently doesn't)
880*/
881
cristye6365592010-04-02 17:31:23 +0000882MagickExport Image *ColorMatrixImage(const Image *image,
883 const KernelInfo *color_matrix,ExceptionInfo *exception)
884{
885#define ColorMatrixImageTag "ColorMatrix/Image"
886
887 CacheView
888 *color_view,
889 *image_view;
890
891 double
892 ColorMatrix[6][6] =
893 {
894 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
895 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
896 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
897 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
898 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
899 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
900 };
901
902 Image
903 *color_image;
904
cristye6365592010-04-02 17:31:23 +0000905 MagickBooleanType
906 status;
907
cristybb503372010-05-27 20:51:26 +0000908 MagickOffsetType
909 progress;
910
911 register ssize_t
cristye6365592010-04-02 17:31:23 +0000912 i;
913
cristybb503372010-05-27 20:51:26 +0000914 ssize_t
915 u,
916 v,
917 y;
918
cristye6365592010-04-02 17:31:23 +0000919 /*
anthonyfd706f92012-01-19 04:22:02 +0000920 Map given color_matrix, into a 6x6 matrix RGBKA and a constant
cristye6365592010-04-02 17:31:23 +0000921 */
922 assert(image != (Image *) NULL);
923 assert(image->signature == MagickSignature);
924 if (image->debug != MagickFalse)
925 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
926 assert(exception != (ExceptionInfo *) NULL);
927 assert(exception->signature == MagickSignature);
928 i=0;
cristybb503372010-05-27 20:51:26 +0000929 for (v=0; v < (ssize_t) color_matrix->height; v++)
930 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000931 {
932 if ((v < 6) && (u < 6))
933 ColorMatrix[v][u]=color_matrix->values[i];
934 i++;
935 }
936 /*
937 Initialize color image.
938 */
cristy12550e62010-06-07 12:46:40 +0000939 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000940 if (color_image == (Image *) NULL)
941 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000942 if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
cristye6365592010-04-02 17:31:23 +0000943 {
cristye6365592010-04-02 17:31:23 +0000944 color_image=DestroyImage(color_image);
945 return((Image *) NULL);
946 }
947 if (image->debug != MagickFalse)
948 {
949 char
950 format[MaxTextExtent],
951 *message;
952
953 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
954 " ColorMatrix image with color matrix:");
955 message=AcquireString("");
956 for (v=0; v < 6; v++)
957 {
958 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000959 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000960 (void) ConcatenateString(&message,format);
961 for (u=0; u < 6; u++)
962 {
cristyb51dff52011-05-19 16:55:47 +0000963 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
cristye6365592010-04-02 17:31:23 +0000964 ColorMatrix[v][u]);
965 (void) ConcatenateString(&message,format);
966 }
967 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
968 }
969 message=DestroyString(message);
970 }
971 /*
anthonyfd706f92012-01-19 04:22:02 +0000972 Apply the ColorMatrix to image.
cristye6365592010-04-02 17:31:23 +0000973 */
974 status=MagickTrue;
975 progress=0;
976 image_view=AcquireCacheView(image);
977 color_view=AcquireCacheView(color_image);
978#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000979 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye6365592010-04-02 17:31:23 +0000980#endif
cristybb503372010-05-27 20:51:26 +0000981 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000982 {
cristyfcc25d92012-02-19 23:06:48 +0000983 PixelInfo
cristye6365592010-04-02 17:31:23 +0000984 pixel;
985
cristy4c08aed2011-07-01 19:47:50 +0000986 register const Quantum
cristye6365592010-04-02 17:31:23 +0000987 *restrict p;
988
cristy4c08aed2011-07-01 19:47:50 +0000989 register Quantum
990 *restrict q;
991
cristybb503372010-05-27 20:51:26 +0000992 register ssize_t
cristye6365592010-04-02 17:31:23 +0000993 x;
994
cristye6365592010-04-02 17:31:23 +0000995 if (status == MagickFalse)
996 continue;
997 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
998 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
999 exception);
cristy4c08aed2011-07-01 19:47:50 +00001000 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristye6365592010-04-02 17:31:23 +00001001 {
1002 status=MagickFalse;
1003 continue;
1004 }
cristyfcc25d92012-02-19 23:06:48 +00001005 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001006 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +00001007 {
cristybb503372010-05-27 20:51:26 +00001008 register ssize_t
cristye6365592010-04-02 17:31:23 +00001009 v;
1010
cristybb503372010-05-27 20:51:26 +00001011 size_t
cristye6365592010-04-02 17:31:23 +00001012 height;
1013
cristyfcc25d92012-02-19 23:06:48 +00001014 GetPixelInfoPixel(image,p,&pixel);
cristye6365592010-04-02 17:31:23 +00001015 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +00001016 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +00001017 {
cristyfcc25d92012-02-19 23:06:48 +00001018 MagickRealType
1019 sum;
1020
1021 sum=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
cristy4c08aed2011-07-01 19:47:50 +00001022 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +00001023 if (image->colorspace == CMYKColorspace)
cristyfcc25d92012-02-19 23:06:48 +00001024 sum+=ColorMatrix[v][3]*GetPixelBlack(image,p);
cristy4c08aed2011-07-01 19:47:50 +00001025 if (image->matte != MagickFalse)
cristyfcc25d92012-02-19 23:06:48 +00001026 sum+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
1027 sum+=QuantumRange*ColorMatrix[v][5];
cristye6365592010-04-02 17:31:23 +00001028 switch (v)
1029 {
cristyfcc25d92012-02-19 23:06:48 +00001030 case 0: pixel.red=sum; break;
1031 case 1: pixel.green=sum; break;
1032 case 2: pixel.blue=sum; break;
1033 case 3: pixel.black=sum; break;
1034 case 4: pixel.alpha=sum; break;
1035 default: break;
cristye6365592010-04-02 17:31:23 +00001036 }
1037 }
cristyfcc25d92012-02-19 23:06:48 +00001038 SetPixelInfoPixel(color_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001039 p+=GetPixelChannels(image);
1040 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +00001041 }
1042 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1043 status=MagickFalse;
1044 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1045 {
1046 MagickBooleanType
1047 proceed;
1048
1049#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00001050 #pragma omp critical (MagickCore_ColorMatrixImage)
cristye6365592010-04-02 17:31:23 +00001051#endif
1052 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1053 image->rows);
1054 if (proceed == MagickFalse)
1055 status=MagickFalse;
1056 }
1057 }
1058 color_view=DestroyCacheView(color_view);
1059 image_view=DestroyCacheView(image_view);
1060 if (status == MagickFalse)
1061 color_image=DestroyImage(color_image);
1062 return(color_image);
1063}
1064
1065/*
1066%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1067% %
1068% %
1069% %
cristy3ed852e2009-09-05 21:47:34 +00001070+ D e s t r o y F x I n f o %
1071% %
1072% %
1073% %
1074%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1075%
1076% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1077%
1078% The format of the DestroyFxInfo method is:
1079%
1080% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1081%
1082% A description of each parameter follows:
1083%
1084% o fx_info: the fx info.
1085%
1086*/
cristy7832dc22011-09-05 01:21:53 +00001087MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
cristy3ed852e2009-09-05 21:47:34 +00001088{
cristybb503372010-05-27 20:51:26 +00001089 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001090 i;
1091
1092 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1093 fx_info->expression=DestroyString(fx_info->expression);
1094 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1095 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001096 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001097 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1098 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001099 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1100 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1101 return(fx_info);
1102}
1103
1104/*
1105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1106% %
1107% %
1108% %
cristy3ed852e2009-09-05 21:47:34 +00001109+ 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 %
1110% %
1111% %
1112% %
1113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1114%
1115% FxEvaluateChannelExpression() evaluates an expression and returns the
1116% results.
1117%
1118% The format of the FxEvaluateExpression method is:
1119%
1120% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001121% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001122% MagickRealType *alpha,Exceptioninfo *exception)
1123% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1124% MagickRealType *alpha,Exceptioninfo *exception)
1125%
1126% A description of each parameter follows:
1127%
1128% o fx_info: the fx info.
1129%
1130% o channel: the channel.
1131%
1132% o x,y: the pixel position.
1133%
1134% o alpha: the result.
1135%
1136% o exception: return any errors or warnings in this structure.
1137%
1138*/
1139
cristy351842f2010-03-07 15:27:38 +00001140static inline double MagickMax(const double x,const double y)
1141{
1142 if (x > y)
1143 return(x);
1144 return(y);
1145}
1146
1147static inline double MagickMin(const double x,const double y)
1148{
1149 if (x < y)
1150 return(x);
1151 return(y);
1152}
1153
cristy3ed852e2009-09-05 21:47:34 +00001154static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001155 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001156{
1157 char
1158 key[MaxTextExtent],
1159 statistic[MaxTextExtent];
1160
1161 const char
1162 *value;
1163
1164 register const char
1165 *p;
1166
1167 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1168 if (*p == '.')
1169 switch (*++p) /* e.g. depth.r */
1170 {
cristy541ae572011-08-05 19:08:59 +00001171 case 'r': channel=RedPixelChannel; break;
1172 case 'g': channel=GreenPixelChannel; break;
1173 case 'b': channel=BluePixelChannel; break;
1174 case 'c': channel=CyanPixelChannel; break;
1175 case 'm': channel=MagentaPixelChannel; break;
1176 case 'y': channel=YellowPixelChannel; break;
1177 case 'k': channel=BlackPixelChannel; break;
cristy3ed852e2009-09-05 21:47:34 +00001178 default: break;
1179 }
cristyb51dff52011-05-19 16:55:47 +00001180 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001181 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001182 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1183 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001184 return(QuantumScale*StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001185 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1186 if (LocaleNCompare(symbol,"depth",5) == 0)
1187 {
cristybb503372010-05-27 20:51:26 +00001188 size_t
cristy3ed852e2009-09-05 21:47:34 +00001189 depth;
1190
cristyfefab1b2011-07-05 00:33:22 +00001191 depth=GetImageDepth(image,exception);
1192 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001193 }
1194 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1195 {
1196 double
1197 kurtosis,
1198 skewness;
1199
cristyd42d9952011-07-08 14:21:50 +00001200 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001201 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001202 }
1203 if (LocaleNCompare(symbol,"maxima",6) == 0)
1204 {
1205 double
1206 maxima,
1207 minima;
1208
cristyd42d9952011-07-08 14:21:50 +00001209 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001210 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001211 }
1212 if (LocaleNCompare(symbol,"mean",4) == 0)
1213 {
1214 double
1215 mean,
1216 standard_deviation;
1217
cristyd42d9952011-07-08 14:21:50 +00001218 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001219 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001220 }
1221 if (LocaleNCompare(symbol,"minima",6) == 0)
1222 {
1223 double
1224 maxima,
1225 minima;
1226
cristyd42d9952011-07-08 14:21:50 +00001227 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001228 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001229 }
1230 if (LocaleNCompare(symbol,"skewness",8) == 0)
1231 {
1232 double
1233 kurtosis,
1234 skewness;
1235
cristyd42d9952011-07-08 14:21:50 +00001236 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001237 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001238 }
1239 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1240 {
1241 double
1242 mean,
1243 standard_deviation;
1244
cristyd42d9952011-07-08 14:21:50 +00001245 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001246 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001247 standard_deviation);
1248 }
1249 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1250 ConstantString(statistic));
cristydbdd0e32011-11-04 23:29:40 +00001251 return(QuantumScale*StringToDouble(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001252}
1253
1254static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001255 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001256 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001257
cristyb0aad4c2011-11-02 19:30:35 +00001258static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
1259{
1260 if (beta != 0)
1261 return(FxGCD(beta,alpha % beta));
1262 return(alpha);
1263}
1264
cristy3ed852e2009-09-05 21:47:34 +00001265static inline const char *FxSubexpression(const char *expression,
1266 ExceptionInfo *exception)
1267{
1268 const char
1269 *subexpression;
1270
cristybb503372010-05-27 20:51:26 +00001271 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001272 level;
1273
1274 level=0;
1275 subexpression=expression;
1276 while ((*subexpression != '\0') &&
1277 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1278 {
1279 if (strchr("(",(int) *subexpression) != (char *) NULL)
1280 level++;
1281 else
1282 if (strchr(")",(int) *subexpression) != (char *) NULL)
1283 level--;
1284 subexpression++;
1285 }
1286 if (*subexpression == '\0')
1287 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1288 "UnbalancedParenthesis","`%s'",expression);
1289 return(subexpression);
1290}
1291
cristy0568ffc2011-07-25 16:54:14 +00001292static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001293 const ssize_t x,const ssize_t y,const char *expression,
1294 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001295{
1296 char
1297 *q,
1298 subexpression[MaxTextExtent],
1299 symbol[MaxTextExtent];
1300
1301 const char
1302 *p,
1303 *value;
1304
1305 Image
1306 *image;
1307
cristy4c08aed2011-07-01 19:47:50 +00001308 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001309 pixel;
1310
1311 MagickRealType
1312 alpha,
1313 beta;
1314
1315 PointInfo
1316 point;
1317
cristybb503372010-05-27 20:51:26 +00001318 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001319 i;
1320
1321 size_t
cristy1707c6c2012-01-18 23:30:54 +00001322 length,
cristy3ed852e2009-09-05 21:47:34 +00001323 level;
1324
1325 p=expression;
1326 i=GetImageIndexInList(fx_info->images);
1327 level=0;
1328 point.x=(double) x;
1329 point.y=(double) y;
1330 if (isalpha((int) *(p+1)) == 0)
1331 {
1332 if (strchr("suv",(int) *p) != (char *) NULL)
1333 {
1334 switch (*p)
1335 {
1336 case 's':
1337 default:
1338 {
1339 i=GetImageIndexInList(fx_info->images);
1340 break;
1341 }
1342 case 'u': i=0; break;
1343 case 'v': i=1; break;
1344 }
1345 p++;
1346 if (*p == '[')
1347 {
1348 level++;
1349 q=subexpression;
1350 for (p++; *p != '\0'; )
1351 {
1352 if (*p == '[')
1353 level++;
1354 else
1355 if (*p == ']')
1356 {
1357 level--;
1358 if (level == 0)
1359 break;
1360 }
1361 *q++=(*p++);
1362 }
1363 *q='\0';
1364 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1365 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001366 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001367 p++;
1368 }
1369 if (*p == '.')
1370 p++;
1371 }
1372 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1373 {
1374 p++;
1375 if (*p == '{')
1376 {
1377 level++;
1378 q=subexpression;
1379 for (p++; *p != '\0'; )
1380 {
1381 if (*p == '{')
1382 level++;
1383 else
1384 if (*p == '}')
1385 {
1386 level--;
1387 if (level == 0)
1388 break;
1389 }
1390 *q++=(*p++);
1391 }
1392 *q='\0';
1393 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1394 &beta,exception);
1395 point.x=alpha;
1396 point.y=beta;
1397 p++;
1398 }
1399 else
1400 if (*p == '[')
1401 {
1402 level++;
1403 q=subexpression;
1404 for (p++; *p != '\0'; )
1405 {
1406 if (*p == '[')
1407 level++;
1408 else
1409 if (*p == ']')
1410 {
1411 level--;
1412 if (level == 0)
1413 break;
1414 }
1415 *q++=(*p++);
1416 }
1417 *q='\0';
1418 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1419 &beta,exception);
1420 point.x+=alpha;
1421 point.y+=beta;
1422 p++;
1423 }
1424 if (*p == '.')
1425 p++;
1426 }
1427 }
1428 length=GetImageListLength(fx_info->images);
1429 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001430 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001431 i%=length;
1432 image=GetImageFromList(fx_info->images,i);
1433 if (image == (Image *) NULL)
1434 {
1435 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1436 "NoSuchImage","`%s'",expression);
1437 return(0.0);
1438 }
cristy4c08aed2011-07-01 19:47:50 +00001439 GetPixelInfo(image,&pixel);
1440 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001441 point.x,point.y,&pixel,exception);
cristy1707c6c2012-01-18 23:30:54 +00001442 if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1443 (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001444 (LocaleCompare(p,"saturation") != 0) &&
1445 (LocaleCompare(p,"lightness") != 0))
1446 {
1447 char
1448 name[MaxTextExtent];
1449
1450 (void) CopyMagickString(name,p,MaxTextExtent);
1451 for (q=name+(strlen(name)-1); q > name; q--)
1452 {
1453 if (*q == ')')
1454 break;
1455 if (*q == '.')
1456 {
1457 *q='\0';
1458 break;
1459 }
1460 }
1461 if ((strlen(name) > 2) &&
1462 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1463 {
cristy4c08aed2011-07-01 19:47:50 +00001464 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001465 *color;
1466
cristy4c08aed2011-07-01 19:47:50 +00001467 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1468 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001469 {
1470 pixel=(*color);
1471 p+=strlen(name);
1472 }
1473 else
cristy1707c6c2012-01-18 23:30:54 +00001474 {
1475 MagickBooleanType
1476 status;
1477
1478 status=QueryColorCompliance(name,AllCompliance,&pixel,
1479 fx_info->exception);
1480 if (status != MagickFalse)
1481 {
1482 (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1483 name),ClonePixelInfo(&pixel));
1484 p+=strlen(name);
1485 }
1486 }
cristy3ed852e2009-09-05 21:47:34 +00001487 }
1488 }
1489 (void) CopyMagickString(symbol,p,MaxTextExtent);
1490 StripString(symbol);
1491 if (*symbol == '\0')
1492 {
1493 switch (channel)
1494 {
cristy0568ffc2011-07-25 16:54:14 +00001495 case RedPixelChannel: return(QuantumScale*pixel.red);
1496 case GreenPixelChannel: return(QuantumScale*pixel.green);
1497 case BluePixelChannel: return(QuantumScale*pixel.blue);
1498 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001499 {
1500 if (image->colorspace != CMYKColorspace)
1501 {
1502 (void) ThrowMagickException(exception,GetMagickModule(),
cristy1a020e42011-12-06 18:13:23 +00001503 ImageError,"ColorSeparatedImageRequired","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00001504 image->filename);
1505 return(0.0);
1506 }
cristy4c08aed2011-07-01 19:47:50 +00001507 return(QuantumScale*pixel.black);
1508 }
cristy0568ffc2011-07-25 16:54:14 +00001509 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001510 {
1511 MagickRealType
1512 alpha;
1513
1514 if (pixel.matte == MagickFalse)
1515 return(1.0);
1516 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1517 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001518 }
cristya382aca2011-12-06 18:22:48 +00001519 case IndexPixelChannel:
1520 return(0.0);
cristyb3a73b52011-07-26 01:34:43 +00001521 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001522 {
cristy4c08aed2011-07-01 19:47:50 +00001523 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001524 }
cristy3ed852e2009-09-05 21:47:34 +00001525 default:
1526 break;
1527 }
1528 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1529 "UnableToParseExpression","`%s'",p);
1530 return(0.0);
1531 }
1532 switch (*symbol)
1533 {
1534 case 'A':
1535 case 'a':
1536 {
1537 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001538 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001539 break;
1540 }
1541 case 'B':
1542 case 'b':
1543 {
1544 if (LocaleCompare(symbol,"b") == 0)
1545 return(QuantumScale*pixel.blue);
1546 break;
1547 }
1548 case 'C':
1549 case 'c':
1550 {
1551 if (LocaleNCompare(symbol,"channel",7) == 0)
1552 {
1553 GeometryInfo
1554 channel_info;
1555
1556 MagickStatusType
1557 flags;
1558
1559 flags=ParseGeometry(symbol+7,&channel_info);
1560 if (image->colorspace == CMYKColorspace)
1561 switch (channel)
1562 {
cristy0568ffc2011-07-25 16:54:14 +00001563 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001564 {
1565 if ((flags & RhoValue) == 0)
1566 return(0.0);
1567 return(channel_info.rho);
1568 }
cristy0568ffc2011-07-25 16:54:14 +00001569 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001570 {
1571 if ((flags & SigmaValue) == 0)
1572 return(0.0);
1573 return(channel_info.sigma);
1574 }
cristy0568ffc2011-07-25 16:54:14 +00001575 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001576 {
1577 if ((flags & XiValue) == 0)
1578 return(0.0);
1579 return(channel_info.xi);
1580 }
cristy0568ffc2011-07-25 16:54:14 +00001581 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001582 {
1583 if ((flags & PsiValue) == 0)
1584 return(0.0);
1585 return(channel_info.psi);
1586 }
cristy0568ffc2011-07-25 16:54:14 +00001587 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001588 {
1589 if ((flags & ChiValue) == 0)
1590 return(0.0);
1591 return(channel_info.chi);
1592 }
1593 default:
1594 return(0.0);
1595 }
1596 switch (channel)
1597 {
cristy0568ffc2011-07-25 16:54:14 +00001598 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001599 {
1600 if ((flags & RhoValue) == 0)
1601 return(0.0);
1602 return(channel_info.rho);
1603 }
cristy0568ffc2011-07-25 16:54:14 +00001604 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001605 {
1606 if ((flags & SigmaValue) == 0)
1607 return(0.0);
1608 return(channel_info.sigma);
1609 }
cristy0568ffc2011-07-25 16:54:14 +00001610 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001611 {
1612 if ((flags & XiValue) == 0)
1613 return(0.0);
1614 return(channel_info.xi);
1615 }
cristy0568ffc2011-07-25 16:54:14 +00001616 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001617 {
1618 if ((flags & ChiValue) == 0)
1619 return(0.0);
1620 return(channel_info.chi);
1621 }
cristy0568ffc2011-07-25 16:54:14 +00001622 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001623 {
1624 if ((flags & PsiValue) == 0)
1625 return(0.0);
1626 return(channel_info.psi);
1627 }
cristy3ed852e2009-09-05 21:47:34 +00001628 default:
1629 return(0.0);
1630 }
1631 return(0.0);
1632 }
1633 if (LocaleCompare(symbol,"c") == 0)
1634 return(QuantumScale*pixel.red);
1635 break;
1636 }
1637 case 'D':
1638 case 'd':
1639 {
1640 if (LocaleNCompare(symbol,"depth",5) == 0)
1641 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1642 break;
1643 }
1644 case 'G':
1645 case 'g':
1646 {
1647 if (LocaleCompare(symbol,"g") == 0)
1648 return(QuantumScale*pixel.green);
1649 break;
1650 }
1651 case 'K':
1652 case 'k':
1653 {
1654 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1655 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1656 if (LocaleCompare(symbol,"k") == 0)
1657 {
1658 if (image->colorspace != CMYKColorspace)
1659 {
1660 (void) ThrowMagickException(exception,GetMagickModule(),
1661 OptionError,"ColorSeparatedImageRequired","`%s'",
1662 image->filename);
1663 return(0.0);
1664 }
cristy4c08aed2011-07-01 19:47:50 +00001665 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001666 }
1667 break;
1668 }
1669 case 'H':
1670 case 'h':
1671 {
1672 if (LocaleCompare(symbol,"h") == 0)
1673 return((MagickRealType) image->rows);
1674 if (LocaleCompare(symbol,"hue") == 0)
1675 {
1676 double
1677 hue,
1678 lightness,
1679 saturation;
1680
cristyda1f9c12011-10-02 21:39:49 +00001681 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1682 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001683 return(hue);
1684 }
1685 break;
1686 }
1687 case 'I':
1688 case 'i':
1689 {
1690 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1691 (LocaleCompare(symbol,"image.minima") == 0) ||
1692 (LocaleCompare(symbol,"image.maxima") == 0) ||
1693 (LocaleCompare(symbol,"image.mean") == 0) ||
1694 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1695 (LocaleCompare(symbol,"image.skewness") == 0) ||
1696 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1697 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1698 if (LocaleCompare(symbol,"image.resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001699 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001700 if (LocaleCompare(symbol,"image.resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001701 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001702 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001703 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001704 if (LocaleCompare(symbol,"i") == 0)
1705 return((MagickRealType) x);
1706 break;
1707 }
1708 case 'J':
1709 case 'j':
1710 {
1711 if (LocaleCompare(symbol,"j") == 0)
1712 return((MagickRealType) y);
1713 break;
1714 }
1715 case 'L':
1716 case 'l':
1717 {
1718 if (LocaleCompare(symbol,"lightness") == 0)
1719 {
1720 double
1721 hue,
1722 lightness,
1723 saturation;
1724
cristyda1f9c12011-10-02 21:39:49 +00001725 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1726 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001727 return(lightness);
1728 }
1729 if (LocaleCompare(symbol,"luminance") == 0)
1730 {
1731 double
1732 luminence;
1733
1734 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1735 return(QuantumScale*luminence);
1736 }
1737 break;
1738 }
1739 case 'M':
1740 case 'm':
1741 {
1742 if (LocaleNCompare(symbol,"maxima",6) == 0)
1743 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1744 if (LocaleNCompare(symbol,"mean",4) == 0)
1745 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1746 if (LocaleNCompare(symbol,"minima",6) == 0)
1747 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1748 if (LocaleCompare(symbol,"m") == 0)
1749 return(QuantumScale*pixel.blue);
1750 break;
1751 }
1752 case 'N':
1753 case 'n':
1754 {
1755 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001756 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001757 break;
1758 }
1759 case 'O':
1760 case 'o':
1761 {
1762 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001763 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001764 break;
1765 }
1766 case 'P':
1767 case 'p':
1768 {
1769 if (LocaleCompare(symbol,"page.height") == 0)
1770 return((MagickRealType) image->page.height);
1771 if (LocaleCompare(symbol,"page.width") == 0)
1772 return((MagickRealType) image->page.width);
1773 if (LocaleCompare(symbol,"page.x") == 0)
1774 return((MagickRealType) image->page.x);
1775 if (LocaleCompare(symbol,"page.y") == 0)
1776 return((MagickRealType) image->page.y);
1777 break;
1778 }
1779 case 'R':
1780 case 'r':
1781 {
1782 if (LocaleCompare(symbol,"resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001783 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001784 if (LocaleCompare(symbol,"resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001785 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001786 if (LocaleCompare(symbol,"r") == 0)
1787 return(QuantumScale*pixel.red);
1788 break;
1789 }
1790 case 'S':
1791 case 's':
1792 {
1793 if (LocaleCompare(symbol,"saturation") == 0)
1794 {
1795 double
1796 hue,
1797 lightness,
1798 saturation;
1799
cristyda1f9c12011-10-02 21:39:49 +00001800 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1801 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001802 return(saturation);
1803 }
1804 if (LocaleNCompare(symbol,"skewness",8) == 0)
1805 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1806 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1807 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1808 break;
1809 }
1810 case 'T':
1811 case 't':
1812 {
1813 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001814 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001815 break;
1816 }
1817 case 'W':
1818 case 'w':
1819 {
1820 if (LocaleCompare(symbol,"w") == 0)
1821 return((MagickRealType) image->columns);
1822 break;
1823 }
1824 case 'Y':
1825 case 'y':
1826 {
1827 if (LocaleCompare(symbol,"y") == 0)
1828 return(QuantumScale*pixel.green);
1829 break;
1830 }
1831 case 'Z':
1832 case 'z':
1833 {
1834 if (LocaleCompare(symbol,"z") == 0)
1835 {
1836 MagickRealType
1837 depth;
1838
cristyfefab1b2011-07-05 00:33:22 +00001839 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001840 return(depth);
1841 }
1842 break;
1843 }
1844 default:
1845 break;
1846 }
1847 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1848 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001849 return((MagickRealType) StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001850 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1851 "UnableToParseExpression","`%s'",symbol);
1852 return(0.0);
1853}
1854
1855static const char *FxOperatorPrecedence(const char *expression,
1856 ExceptionInfo *exception)
1857{
1858 typedef enum
1859 {
1860 UndefinedPrecedence,
1861 NullPrecedence,
1862 BitwiseComplementPrecedence,
1863 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001864 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001865 MultiplyPrecedence,
1866 AdditionPrecedence,
1867 ShiftPrecedence,
1868 RelationalPrecedence,
1869 EquivalencyPrecedence,
1870 BitwiseAndPrecedence,
1871 BitwiseOrPrecedence,
1872 LogicalAndPrecedence,
1873 LogicalOrPrecedence,
1874 TernaryPrecedence,
1875 AssignmentPrecedence,
1876 CommaPrecedence,
1877 SeparatorPrecedence
1878 } FxPrecedence;
1879
1880 FxPrecedence
1881 precedence,
1882 target;
1883
1884 register const char
1885 *subexpression;
1886
1887 register int
1888 c;
1889
cristybb503372010-05-27 20:51:26 +00001890 size_t
cristy3ed852e2009-09-05 21:47:34 +00001891 level;
1892
1893 c=0;
1894 level=0;
1895 subexpression=(const char *) NULL;
1896 target=NullPrecedence;
1897 while (*expression != '\0')
1898 {
1899 precedence=UndefinedPrecedence;
1900 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1901 {
1902 expression++;
1903 continue;
1904 }
cristy488fa882010-03-01 22:34:24 +00001905 switch (*expression)
1906 {
1907 case 'A':
1908 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001909 {
cristyb33454f2011-08-03 02:10:45 +00001910#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001911 if (LocaleNCompare(expression,"acosh",5) == 0)
1912 {
1913 expression+=5;
1914 break;
1915 }
cristyb33454f2011-08-03 02:10:45 +00001916#endif
1917#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001918 if (LocaleNCompare(expression,"asinh",5) == 0)
1919 {
1920 expression+=5;
1921 break;
1922 }
cristyb33454f2011-08-03 02:10:45 +00001923#endif
1924#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001925 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001926 {
1927 expression+=5;
1928 break;
1929 }
cristyb33454f2011-08-03 02:10:45 +00001930#endif
cristy488fa882010-03-01 22:34:24 +00001931 break;
cristy3ed852e2009-09-05 21:47:34 +00001932 }
cristy62d455f2011-11-03 11:42:28 +00001933 case 'E':
1934 case 'e':
1935 {
1936 if ((LocaleNCompare(expression,"E+",2) == 0) ||
1937 (LocaleNCompare(expression,"E-",2) == 0))
1938 {
1939 expression+=2; /* scientific notation */
1940 break;
1941 }
1942 }
cristy488fa882010-03-01 22:34:24 +00001943 case 'J':
1944 case 'j':
1945 {
1946 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1947 (LocaleNCompare(expression,"j1",2) == 0))
1948 {
1949 expression+=2;
1950 break;
1951 }
1952 break;
1953 }
cristy2def9322010-06-18 23:59:37 +00001954 case '#':
1955 {
1956 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1957 expression++;
1958 break;
1959 }
cristy488fa882010-03-01 22:34:24 +00001960 default:
1961 break;
1962 }
cristy3ed852e2009-09-05 21:47:34 +00001963 if ((c == (int) '{') || (c == (int) '['))
1964 level++;
1965 else
1966 if ((c == (int) '}') || (c == (int) ']'))
1967 level--;
1968 if (level == 0)
1969 switch ((unsigned char) *expression)
1970 {
1971 case '~':
1972 case '!':
1973 {
1974 precedence=BitwiseComplementPrecedence;
1975 break;
1976 }
1977 case '^':
cristy6621e252010-08-13 00:42:57 +00001978 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001979 {
1980 precedence=ExponentPrecedence;
1981 break;
1982 }
1983 default:
1984 {
1985 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1986 (strchr(")",c) != (char *) NULL))) &&
1987 (((islower((int) ((char) *expression)) != 0) ||
1988 (strchr("(",(int) *expression) != (char *) NULL)) ||
1989 ((isdigit((int) ((char) c)) == 0) &&
1990 (isdigit((int) ((char) *expression)) != 0))) &&
1991 (strchr("xy",(int) *expression) == (char *) NULL))
1992 precedence=MultiplyPrecedence;
1993 break;
1994 }
1995 case '*':
1996 case '/':
1997 case '%':
1998 {
1999 precedence=MultiplyPrecedence;
2000 break;
2001 }
2002 case '+':
2003 case '-':
2004 {
2005 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
2006 (isalpha(c) != 0))
2007 precedence=AdditionPrecedence;
2008 break;
2009 }
2010 case LeftShiftOperator:
2011 case RightShiftOperator:
2012 {
2013 precedence=ShiftPrecedence;
2014 break;
2015 }
2016 case '<':
2017 case LessThanEqualOperator:
2018 case GreaterThanEqualOperator:
2019 case '>':
2020 {
2021 precedence=RelationalPrecedence;
2022 break;
2023 }
2024 case EqualOperator:
2025 case NotEqualOperator:
2026 {
2027 precedence=EquivalencyPrecedence;
2028 break;
2029 }
2030 case '&':
2031 {
2032 precedence=BitwiseAndPrecedence;
2033 break;
2034 }
2035 case '|':
2036 {
2037 precedence=BitwiseOrPrecedence;
2038 break;
2039 }
2040 case LogicalAndOperator:
2041 {
2042 precedence=LogicalAndPrecedence;
2043 break;
2044 }
2045 case LogicalOrOperator:
2046 {
2047 precedence=LogicalOrPrecedence;
2048 break;
2049 }
cristy116af162010-08-13 01:25:47 +00002050 case ExponentialNotation:
2051 {
2052 precedence=ExponentialNotationPrecedence;
2053 break;
2054 }
cristy3ed852e2009-09-05 21:47:34 +00002055 case ':':
2056 case '?':
2057 {
2058 precedence=TernaryPrecedence;
2059 break;
2060 }
2061 case '=':
2062 {
2063 precedence=AssignmentPrecedence;
2064 break;
2065 }
2066 case ',':
2067 {
2068 precedence=CommaPrecedence;
2069 break;
2070 }
2071 case ';':
2072 {
2073 precedence=SeparatorPrecedence;
2074 break;
2075 }
2076 }
2077 if ((precedence == BitwiseComplementPrecedence) ||
2078 (precedence == TernaryPrecedence) ||
2079 (precedence == AssignmentPrecedence))
2080 {
2081 if (precedence > target)
2082 {
2083 /*
2084 Right-to-left associativity.
2085 */
2086 target=precedence;
2087 subexpression=expression;
2088 }
2089 }
2090 else
2091 if (precedence >= target)
2092 {
2093 /*
2094 Left-to-right associativity.
2095 */
2096 target=precedence;
2097 subexpression=expression;
2098 }
2099 if (strchr("(",(int) *expression) != (char *) NULL)
2100 expression=FxSubexpression(expression,exception);
2101 c=(int) (*expression++);
2102 }
2103 return(subexpression);
2104}
2105
2106static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002107 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002108 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002109{
2110 char
2111 *q,
2112 subexpression[MaxTextExtent];
2113
2114 MagickRealType
2115 alpha,
2116 gamma;
2117
2118 register const char
2119 *p;
2120
2121 *beta=0.0;
2122 if (exception->severity != UndefinedException)
2123 return(0.0);
2124 while (isspace((int) *expression) != 0)
2125 expression++;
2126 if (*expression == '\0')
2127 {
2128 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2129 "MissingExpression","`%s'",expression);
2130 return(0.0);
2131 }
cristy66322f02010-05-17 11:40:48 +00002132 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002133 p=FxOperatorPrecedence(expression,exception);
2134 if (p != (const char *) NULL)
2135 {
2136 (void) CopyMagickString(subexpression,expression,(size_t)
2137 (p-expression+1));
2138 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2139 exception);
2140 switch ((unsigned char) *p)
2141 {
2142 case '~':
2143 {
2144 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002145 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002146 return(*beta);
2147 }
2148 case '!':
2149 {
2150 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2151 return(*beta == 0.0 ? 1.0 : 0.0);
2152 }
2153 case '^':
2154 {
2155 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2156 channel,x,y,++p,beta,exception));
2157 return(*beta);
2158 }
2159 case '*':
cristy116af162010-08-13 01:25:47 +00002160 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002161 {
2162 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2163 return(alpha*(*beta));
2164 }
2165 case '/':
2166 {
2167 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2168 if (*beta == 0.0)
2169 {
2170 if (exception->severity == UndefinedException)
2171 (void) ThrowMagickException(exception,GetMagickModule(),
2172 OptionError,"DivideByZero","`%s'",expression);
2173 return(0.0);
2174 }
2175 return(alpha/(*beta));
2176 }
2177 case '%':
2178 {
2179 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2180 *beta=fabs(floor(((double) *beta)+0.5));
2181 if (*beta == 0.0)
2182 {
2183 (void) ThrowMagickException(exception,GetMagickModule(),
2184 OptionError,"DivideByZero","`%s'",expression);
2185 return(0.0);
2186 }
2187 return(fmod((double) alpha,(double) *beta));
2188 }
2189 case '+':
2190 {
2191 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2192 return(alpha+(*beta));
2193 }
2194 case '-':
2195 {
2196 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2197 return(alpha-(*beta));
2198 }
2199 case LeftShiftOperator:
2200 {
2201 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002202 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002203 return(*beta);
2204 }
2205 case RightShiftOperator:
2206 {
2207 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002208 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002209 return(*beta);
2210 }
2211 case '<':
2212 {
2213 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2214 return(alpha < *beta ? 1.0 : 0.0);
2215 }
2216 case LessThanEqualOperator:
2217 {
2218 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2219 return(alpha <= *beta ? 1.0 : 0.0);
2220 }
2221 case '>':
2222 {
2223 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2224 return(alpha > *beta ? 1.0 : 0.0);
2225 }
2226 case GreaterThanEqualOperator:
2227 {
2228 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2229 return(alpha >= *beta ? 1.0 : 0.0);
2230 }
2231 case EqualOperator:
2232 {
2233 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2234 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2235 }
2236 case NotEqualOperator:
2237 {
2238 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2239 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2240 }
2241 case '&':
2242 {
2243 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002244 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002245 return(*beta);
2246 }
2247 case '|':
2248 {
2249 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002250 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002251 return(*beta);
2252 }
2253 case LogicalAndOperator:
2254 {
2255 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2256 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2257 return(*beta);
2258 }
2259 case LogicalOrOperator:
2260 {
2261 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2262 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2263 return(*beta);
2264 }
2265 case '?':
2266 {
2267 MagickRealType
2268 gamma;
2269
2270 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2271 q=subexpression;
2272 p=StringToken(":",&q);
2273 if (q == (char *) NULL)
2274 {
2275 (void) ThrowMagickException(exception,GetMagickModule(),
2276 OptionError,"UnableToParseExpression","`%s'",subexpression);
2277 return(0.0);
2278 }
2279 if (fabs((double) alpha) > MagickEpsilon)
2280 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2281 else
2282 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2283 return(gamma);
2284 }
2285 case '=':
2286 {
2287 char
2288 numeric[MaxTextExtent];
2289
2290 q=subexpression;
2291 while (isalpha((int) ((unsigned char) *q)) != 0)
2292 q++;
2293 if (*q != '\0')
2294 {
2295 (void) ThrowMagickException(exception,GetMagickModule(),
2296 OptionError,"UnableToParseExpression","`%s'",subexpression);
2297 return(0.0);
2298 }
2299 ClearMagickException(exception);
2300 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002301 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002302 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002303 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2304 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2305 subexpression),ConstantString(numeric));
2306 return(*beta);
2307 }
2308 case ',':
2309 {
2310 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2311 return(alpha);
2312 }
2313 case ';':
2314 {
2315 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2316 return(*beta);
2317 }
2318 default:
2319 {
2320 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2321 exception);
2322 return(gamma);
2323 }
2324 }
2325 }
2326 if (strchr("(",(int) *expression) != (char *) NULL)
2327 {
2328 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2329 subexpression[strlen(subexpression)-1]='\0';
2330 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2331 exception);
2332 return(gamma);
2333 }
cristy8b8a3ae2010-10-23 18:49:46 +00002334 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002335 {
2336 case '+':
2337 {
2338 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2339 exception);
2340 return(1.0*gamma);
2341 }
2342 case '-':
2343 {
2344 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2345 exception);
2346 return(-1.0*gamma);
2347 }
2348 case '~':
2349 {
2350 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2351 exception);
cristybb503372010-05-27 20:51:26 +00002352 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002353 }
2354 case 'A':
2355 case 'a':
2356 {
2357 if (LocaleNCompare(expression,"abs",3) == 0)
2358 {
2359 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2360 exception);
2361 return((MagickRealType) fabs((double) alpha));
2362 }
cristyb33454f2011-08-03 02:10:45 +00002363#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002364 if (LocaleNCompare(expression,"acosh",5) == 0)
2365 {
2366 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2367 exception);
2368 return((MagickRealType) acosh((double) alpha));
2369 }
cristyb33454f2011-08-03 02:10:45 +00002370#endif
cristy3ed852e2009-09-05 21:47:34 +00002371 if (LocaleNCompare(expression,"acos",4) == 0)
2372 {
2373 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2374 exception);
2375 return((MagickRealType) acos((double) alpha));
2376 }
cristy43c22f42010-03-30 12:34:07 +00002377#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002378 if (LocaleNCompare(expression,"airy",4) == 0)
2379 {
2380 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2381 exception);
2382 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002383 return(1.0);
2384 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002385 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002386 }
cristy43c22f42010-03-30 12:34:07 +00002387#endif
cristyb33454f2011-08-03 02:10:45 +00002388#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002389 if (LocaleNCompare(expression,"asinh",5) == 0)
2390 {
2391 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2392 exception);
2393 return((MagickRealType) asinh((double) alpha));
2394 }
cristyb33454f2011-08-03 02:10:45 +00002395#endif
cristy3ed852e2009-09-05 21:47:34 +00002396 if (LocaleNCompare(expression,"asin",4) == 0)
2397 {
2398 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2399 exception);
2400 return((MagickRealType) asin((double) alpha));
2401 }
2402 if (LocaleNCompare(expression,"alt",3) == 0)
2403 {
2404 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2405 exception);
cristybb503372010-05-27 20:51:26 +00002406 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002407 }
2408 if (LocaleNCompare(expression,"atan2",5) == 0)
2409 {
2410 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2411 exception);
2412 return((MagickRealType) atan2((double) alpha,(double) *beta));
2413 }
cristyb33454f2011-08-03 02:10:45 +00002414#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002415 if (LocaleNCompare(expression,"atanh",5) == 0)
2416 {
2417 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2418 exception);
2419 return((MagickRealType) atanh((double) alpha));
2420 }
cristyb33454f2011-08-03 02:10:45 +00002421#endif
cristy3ed852e2009-09-05 21:47:34 +00002422 if (LocaleNCompare(expression,"atan",4) == 0)
2423 {
2424 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2425 exception);
2426 return((MagickRealType) atan((double) alpha));
2427 }
2428 if (LocaleCompare(expression,"a") == 0)
2429 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2430 break;
2431 }
2432 case 'B':
2433 case 'b':
2434 {
2435 if (LocaleCompare(expression,"b") == 0)
2436 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2437 break;
2438 }
2439 case 'C':
2440 case 'c':
2441 {
2442 if (LocaleNCompare(expression,"ceil",4) == 0)
2443 {
2444 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2445 exception);
2446 return((MagickRealType) ceil((double) alpha));
2447 }
2448 if (LocaleNCompare(expression,"cosh",4) == 0)
2449 {
2450 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2451 exception);
2452 return((MagickRealType) cosh((double) alpha));
2453 }
2454 if (LocaleNCompare(expression,"cos",3) == 0)
2455 {
2456 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2457 exception);
2458 return((MagickRealType) cos((double) alpha));
2459 }
2460 if (LocaleCompare(expression,"c") == 0)
2461 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2462 break;
2463 }
2464 case 'D':
2465 case 'd':
2466 {
2467 if (LocaleNCompare(expression,"debug",5) == 0)
2468 {
2469 const char
2470 *type;
2471
2472 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2473 exception);
2474 if (fx_info->images->colorspace == CMYKColorspace)
2475 switch (channel)
2476 {
cristy0568ffc2011-07-25 16:54:14 +00002477 case CyanPixelChannel: type="cyan"; break;
2478 case MagentaPixelChannel: type="magenta"; break;
2479 case YellowPixelChannel: type="yellow"; break;
2480 case AlphaPixelChannel: type="opacity"; break;
2481 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002482 default: type="unknown"; break;
2483 }
2484 else
2485 switch (channel)
2486 {
cristy0568ffc2011-07-25 16:54:14 +00002487 case RedPixelChannel: type="red"; break;
2488 case GreenPixelChannel: type="green"; break;
2489 case BluePixelChannel: type="blue"; break;
2490 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002491 default: type="unknown"; break;
2492 }
2493 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2494 if (strlen(subexpression) > 1)
2495 subexpression[strlen(subexpression)-1]='\0';
2496 if (fx_info->file != (FILE *) NULL)
cristy1707c6c2012-01-18 23:30:54 +00002497 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2498 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2499 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002500 return(0.0);
2501 }
cristy5597a8d2011-11-04 00:25:32 +00002502 if (LocaleNCompare(expression,"drc",3) == 0)
2503 {
2504 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2505 exception);
2506 return((MagickRealType) (alpha/(*beta*(alpha-1.0)+1.0)));
2507 }
cristy3ed852e2009-09-05 21:47:34 +00002508 break;
2509 }
2510 case 'E':
2511 case 'e':
2512 {
2513 if (LocaleCompare(expression,"epsilon") == 0)
2514 return((MagickRealType) MagickEpsilon);
2515 if (LocaleNCompare(expression,"exp",3) == 0)
2516 {
2517 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2518 exception);
2519 return((MagickRealType) exp((double) alpha));
2520 }
2521 if (LocaleCompare(expression,"e") == 0)
2522 return((MagickRealType) 2.7182818284590452354);
2523 break;
2524 }
2525 case 'F':
2526 case 'f':
2527 {
2528 if (LocaleNCompare(expression,"floor",5) == 0)
2529 {
2530 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2531 exception);
2532 return((MagickRealType) floor((double) alpha));
2533 }
2534 break;
2535 }
2536 case 'G':
2537 case 'g':
2538 {
cristy9eeedea2011-11-02 19:04:05 +00002539 if (LocaleNCompare(expression,"gauss",5) == 0)
2540 {
2541 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2542 exception);
2543 gamma=exp((double) (-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2544 return((MagickRealType) gamma);
2545 }
cristyb0aad4c2011-11-02 19:30:35 +00002546 if (LocaleNCompare(expression,"gcd",3) == 0)
2547 {
2548 MagickOffsetType
2549 gcd;
2550
2551 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2552 exception);
cristy1707c6c2012-01-18 23:30:54 +00002553 gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2554 0.5));
cristyb0aad4c2011-11-02 19:30:35 +00002555 return((MagickRealType) gcd);
2556 }
cristy3ed852e2009-09-05 21:47:34 +00002557 if (LocaleCompare(expression,"g") == 0)
2558 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2559 break;
2560 }
2561 case 'H':
2562 case 'h':
2563 {
2564 if (LocaleCompare(expression,"h") == 0)
2565 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2566 if (LocaleCompare(expression,"hue") == 0)
2567 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2568 if (LocaleNCompare(expression,"hypot",5) == 0)
2569 {
2570 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2571 exception);
2572 return((MagickRealType) hypot((double) alpha,(double) *beta));
2573 }
2574 break;
2575 }
2576 case 'K':
2577 case 'k':
2578 {
2579 if (LocaleCompare(expression,"k") == 0)
2580 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2581 break;
2582 }
2583 case 'I':
2584 case 'i':
2585 {
2586 if (LocaleCompare(expression,"intensity") == 0)
2587 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2588 if (LocaleNCompare(expression,"int",3) == 0)
2589 {
2590 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2591 exception);
cristy16788e42010-08-13 13:44:26 +00002592 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002593 }
cristy82b20722011-11-05 21:52:36 +00002594#if defined(MAGICKCORE_HAVE_ISNAN)
cristy639399c2011-11-02 19:16:15 +00002595 if (LocaleNCompare(expression,"isnan",5) == 0)
2596 {
2597 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2598 exception);
cristy17a10202011-11-02 19:17:04 +00002599 return((MagickRealType) !!isnan((double) alpha));
cristy639399c2011-11-02 19:16:15 +00002600 }
cristy82b20722011-11-05 21:52:36 +00002601#endif
cristy3ed852e2009-09-05 21:47:34 +00002602 if (LocaleCompare(expression,"i") == 0)
2603 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2604 break;
2605 }
2606 case 'J':
2607 case 'j':
2608 {
2609 if (LocaleCompare(expression,"j") == 0)
2610 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002611#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002612 if (LocaleNCompare(expression,"j0",2) == 0)
2613 {
2614 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2615 exception);
2616 return((MagickRealType) j0((double) alpha));
2617 }
cristy161b9262010-03-20 19:34:32 +00002618#endif
2619#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002620 if (LocaleNCompare(expression,"j1",2) == 0)
2621 {
2622 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2623 exception);
2624 return((MagickRealType) j1((double) alpha));
2625 }
cristy161b9262010-03-20 19:34:32 +00002626#endif
cristyaa018fa2010-04-08 23:03:54 +00002627#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002628 if (LocaleNCompare(expression,"jinc",4) == 0)
2629 {
2630 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2631 exception);
cristy0946a822010-03-12 17:14:58 +00002632 if (alpha == 0.0)
2633 return(1.0);
cristy1707c6c2012-01-18 23:30:54 +00002634 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/(MagickPI*
2635 alpha));
cristyfce2f7b2010-03-12 00:29:49 +00002636 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002637 }
cristyaa018fa2010-04-08 23:03:54 +00002638#endif
cristy3ed852e2009-09-05 21:47:34 +00002639 break;
2640 }
2641 case 'L':
2642 case 'l':
2643 {
2644 if (LocaleNCompare(expression,"ln",2) == 0)
2645 {
2646 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2647 exception);
2648 return((MagickRealType) log((double) alpha));
2649 }
cristyc8ed5322010-08-31 12:07:59 +00002650 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002651 {
cristyc8ed5322010-08-31 12:07:59 +00002652 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002653 exception);
2654 return((MagickRealType) log10((double) alpha))/log10(2.0);
2655 }
2656 if (LocaleNCompare(expression,"log",3) == 0)
2657 {
2658 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2659 exception);
2660 return((MagickRealType) log10((double) alpha));
2661 }
2662 if (LocaleCompare(expression,"lightness") == 0)
2663 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2664 break;
2665 }
2666 case 'M':
2667 case 'm':
2668 {
2669 if (LocaleCompare(expression,"MaxRGB") == 0)
2670 return((MagickRealType) QuantumRange);
2671 if (LocaleNCompare(expression,"maxima",6) == 0)
2672 break;
2673 if (LocaleNCompare(expression,"max",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002674 {
2675 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2676 exception);
2677 return(alpha > *beta ? alpha : *beta);
2678 }
cristy3ed852e2009-09-05 21:47:34 +00002679 if (LocaleNCompare(expression,"minima",6) == 0)
2680 break;
2681 if (LocaleNCompare(expression,"min",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002682 {
2683 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2684 exception);
2685 return(alpha < *beta ? alpha : *beta);
2686 }
cristy3ed852e2009-09-05 21:47:34 +00002687 if (LocaleNCompare(expression,"mod",3) == 0)
2688 {
2689 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2690 exception);
cristy984049c2011-11-03 18:34:58 +00002691 gamma=alpha-floor((double) (alpha/(*beta)))*(*beta);
2692 return(gamma);
cristy3ed852e2009-09-05 21:47:34 +00002693 }
2694 if (LocaleCompare(expression,"m") == 0)
2695 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2696 break;
2697 }
2698 case 'N':
2699 case 'n':
2700 {
cristyad3502e2011-11-02 19:10:45 +00002701 if (LocaleNCompare(expression,"not",3) == 0)
2702 {
2703 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2704 exception);
2705 return((MagickRealType) (alpha < MagickEpsilon));
2706 }
cristy3ed852e2009-09-05 21:47:34 +00002707 if (LocaleCompare(expression,"n") == 0)
2708 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2709 break;
2710 }
2711 case 'O':
2712 case 'o':
2713 {
2714 if (LocaleCompare(expression,"Opaque") == 0)
2715 return(1.0);
2716 if (LocaleCompare(expression,"o") == 0)
2717 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2718 break;
2719 }
2720 case 'P':
2721 case 'p':
2722 {
cristy670aa3c2011-11-03 00:54:00 +00002723 if (LocaleCompare(expression,"phi") == 0)
2724 return((MagickRealType) MagickPHI);
cristy3ed852e2009-09-05 21:47:34 +00002725 if (LocaleCompare(expression,"pi") == 0)
2726 return((MagickRealType) MagickPI);
2727 if (LocaleNCompare(expression,"pow",3) == 0)
2728 {
2729 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2730 exception);
2731 return((MagickRealType) pow((double) alpha,(double) *beta));
2732 }
2733 if (LocaleCompare(expression,"p") == 0)
2734 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2735 break;
2736 }
2737 case 'Q':
2738 case 'q':
2739 {
2740 if (LocaleCompare(expression,"QuantumRange") == 0)
2741 return((MagickRealType) QuantumRange);
2742 if (LocaleCompare(expression,"QuantumScale") == 0)
2743 return((MagickRealType) QuantumScale);
2744 break;
2745 }
2746 case 'R':
2747 case 'r':
2748 {
2749 if (LocaleNCompare(expression,"rand",4) == 0)
2750 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2751 if (LocaleNCompare(expression,"round",5) == 0)
2752 {
2753 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2754 exception);
cristy16788e42010-08-13 13:44:26 +00002755 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002756 }
2757 if (LocaleCompare(expression,"r") == 0)
2758 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2759 break;
2760 }
2761 case 'S':
2762 case 's':
2763 {
2764 if (LocaleCompare(expression,"saturation") == 0)
2765 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2766 if (LocaleNCompare(expression,"sign",4) == 0)
2767 {
2768 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2769 exception);
2770 return(alpha < 0.0 ? -1.0 : 1.0);
2771 }
cristya6a09e72010-03-02 14:51:02 +00002772 if (LocaleNCompare(expression,"sinc",4) == 0)
2773 {
2774 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2775 exception);
2776 if (alpha == 0)
2777 return(1.0);
2778 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2779 (MagickPI*alpha));
2780 return(gamma);
2781 }
cristy3ed852e2009-09-05 21:47:34 +00002782 if (LocaleNCompare(expression,"sinh",4) == 0)
2783 {
2784 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2785 exception);
2786 return((MagickRealType) sinh((double) alpha));
2787 }
2788 if (LocaleNCompare(expression,"sin",3) == 0)
2789 {
2790 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2791 exception);
2792 return((MagickRealType) sin((double) alpha));
2793 }
2794 if (LocaleNCompare(expression,"sqrt",4) == 0)
2795 {
2796 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2797 exception);
2798 return((MagickRealType) sqrt((double) alpha));
2799 }
cristy9eeedea2011-11-02 19:04:05 +00002800 if (LocaleNCompare(expression,"squish",6) == 0)
2801 {
2802 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2803 exception);
2804 return((MagickRealType) (1.0/(1.0+exp((double) (4.0*alpha)))));
2805 }
cristy3ed852e2009-09-05 21:47:34 +00002806 if (LocaleCompare(expression,"s") == 0)
2807 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2808 break;
2809 }
2810 case 'T':
2811 case 't':
2812 {
2813 if (LocaleNCompare(expression,"tanh",4) == 0)
2814 {
2815 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2816 exception);
2817 return((MagickRealType) tanh((double) alpha));
2818 }
2819 if (LocaleNCompare(expression,"tan",3) == 0)
2820 {
2821 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2822 exception);
2823 return((MagickRealType) tan((double) alpha));
2824 }
2825 if (LocaleCompare(expression,"Transparent") == 0)
2826 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002827 if (LocaleNCompare(expression,"trunc",5) == 0)
2828 {
2829 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2830 exception);
2831 if (alpha >= 0.0)
2832 return((MagickRealType) floor((double) alpha));
2833 return((MagickRealType) ceil((double) alpha));
2834 }
cristy3ed852e2009-09-05 21:47:34 +00002835 if (LocaleCompare(expression,"t") == 0)
2836 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2837 break;
2838 }
2839 case 'U':
2840 case 'u':
2841 {
2842 if (LocaleCompare(expression,"u") == 0)
2843 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2844 break;
2845 }
2846 case 'V':
2847 case 'v':
2848 {
2849 if (LocaleCompare(expression,"v") == 0)
2850 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2851 break;
2852 }
2853 case 'W':
2854 case 'w':
2855 {
cristy9eeedea2011-11-02 19:04:05 +00002856 if (LocaleNCompare(expression,"while",5) == 0)
2857 {
2858 do
2859 {
2860 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2861 exception);
2862 } while (fabs((double) alpha) >= MagickEpsilon);
2863 return((MagickRealType) *beta);
2864 }
cristy3ed852e2009-09-05 21:47:34 +00002865 if (LocaleCompare(expression,"w") == 0)
2866 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2867 break;
2868 }
2869 case 'Y':
2870 case 'y':
2871 {
2872 if (LocaleCompare(expression,"y") == 0)
2873 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2874 break;
2875 }
2876 case 'Z':
2877 case 'z':
2878 {
2879 if (LocaleCompare(expression,"z") == 0)
2880 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2881 break;
2882 }
2883 default:
2884 break;
2885 }
2886 q=(char *) expression;
cristydbdd0e32011-11-04 23:29:40 +00002887 alpha=InterpretSiPrefixValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002888 if (q == expression)
2889 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2890 return(alpha);
2891}
2892
cristy7832dc22011-09-05 01:21:53 +00002893MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
cristy3ed852e2009-09-05 21:47:34 +00002894 MagickRealType *alpha,ExceptionInfo *exception)
2895{
2896 MagickBooleanType
2897 status;
2898
cristy541ae572011-08-05 19:08:59 +00002899 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2900 exception);
cristy3ed852e2009-09-05 21:47:34 +00002901 return(status);
2902}
2903
2904MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2905 MagickRealType *alpha,ExceptionInfo *exception)
2906{
2907 FILE
2908 *file;
2909
2910 MagickBooleanType
2911 status;
2912
2913 file=fx_info->file;
2914 fx_info->file=(FILE *) NULL;
cristy541ae572011-08-05 19:08:59 +00002915 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2916 exception);
cristy3ed852e2009-09-05 21:47:34 +00002917 fx_info->file=file;
2918 return(status);
2919}
2920
cristy7832dc22011-09-05 01:21:53 +00002921MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002922 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002923 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002924{
2925 MagickRealType
2926 beta;
2927
2928 beta=0.0;
2929 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2930 exception);
2931 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2932}
2933
2934/*
2935%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2936% %
2937% %
2938% %
2939% F x I m a g e %
2940% %
2941% %
2942% %
2943%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2944%
2945% FxImage() applies a mathematical expression to the specified image.
2946%
2947% The format of the FxImage method is:
2948%
2949% Image *FxImage(const Image *image,const char *expression,
2950% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002951%
2952% A description of each parameter follows:
2953%
2954% o image: the image.
2955%
cristy3ed852e2009-09-05 21:47:34 +00002956% o expression: A mathematical expression.
2957%
2958% o exception: return any errors or warnings in this structure.
2959%
2960*/
2961
2962static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2963{
cristybb503372010-05-27 20:51:26 +00002964 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002965 i;
2966
2967 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002968 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002969 if (fx_info[i] != (FxInfo *) NULL)
2970 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002971 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002972 return(fx_info);
2973}
2974
2975static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2976 ExceptionInfo *exception)
2977{
2978 char
2979 *fx_expression;
2980
2981 FxInfo
2982 **fx_info;
2983
2984 MagickRealType
2985 alpha;
2986
cristybb503372010-05-27 20:51:26 +00002987 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002988 i;
2989
cristybb503372010-05-27 20:51:26 +00002990 size_t
cristy3ed852e2009-09-05 21:47:34 +00002991 number_threads;
2992
2993 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002994 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002995 if (fx_info == (FxInfo **) NULL)
2996 return((FxInfo **) NULL);
2997 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2998 if (*expression != '@')
2999 fx_expression=ConstantString(expression);
3000 else
3001 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00003002 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00003003 {
3004 fx_info[i]=AcquireFxInfo(image,fx_expression);
3005 if (fx_info[i] == (FxInfo *) NULL)
3006 return(DestroyFxThreadSet(fx_info));
3007 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
3008 }
3009 fx_expression=DestroyString(fx_expression);
3010 return(fx_info);
3011}
3012
3013MagickExport Image *FxImage(const Image *image,const char *expression,
3014 ExceptionInfo *exception)
3015{
cristy3ed852e2009-09-05 21:47:34 +00003016#define FxImageTag "Fx/Image"
3017
cristyfa112112010-01-04 17:48:07 +00003018 CacheView
cristy79cedc72011-07-25 00:41:15 +00003019 *fx_view,
3020 *image_view;
cristyfa112112010-01-04 17:48:07 +00003021
cristy3ed852e2009-09-05 21:47:34 +00003022 FxInfo
cristyfa112112010-01-04 17:48:07 +00003023 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00003024
3025 Image
3026 *fx_image;
3027
cristy3ed852e2009-09-05 21:47:34 +00003028 MagickBooleanType
3029 status;
3030
cristybb503372010-05-27 20:51:26 +00003031 MagickOffsetType
3032 progress;
3033
cristy3ed852e2009-09-05 21:47:34 +00003034 MagickRealType
3035 alpha;
3036
cristybb503372010-05-27 20:51:26 +00003037 ssize_t
3038 y;
3039
cristy3ed852e2009-09-05 21:47:34 +00003040 assert(image != (Image *) NULL);
3041 assert(image->signature == MagickSignature);
3042 if (image->debug != MagickFalse)
3043 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00003044 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003045 if (fx_image == (Image *) NULL)
3046 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003047 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003048 {
cristy3ed852e2009-09-05 21:47:34 +00003049 fx_image=DestroyImage(fx_image);
3050 return((Image *) NULL);
3051 }
3052 fx_info=AcquireFxThreadSet(image,expression,exception);
3053 if (fx_info == (FxInfo **) NULL)
3054 {
3055 fx_image=DestroyImage(fx_image);
3056 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3057 }
3058 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3059 if (status == MagickFalse)
3060 {
3061 fx_image=DestroyImage(fx_image);
3062 fx_info=DestroyFxThreadSet(fx_info);
3063 return((Image *) NULL);
3064 }
3065 /*
3066 Fx image.
3067 */
3068 status=MagickTrue;
3069 progress=0;
cristy79cedc72011-07-25 00:41:15 +00003070 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003071 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003072#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003073 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003074#endif
cristybb503372010-05-27 20:51:26 +00003075 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003076 {
cristy5c9e6f22010-09-17 17:31:01 +00003077 const int
3078 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003079
cristy79cedc72011-07-25 00:41:15 +00003080 register const Quantum
3081 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003082
cristy4c08aed2011-07-01 19:47:50 +00003083 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003084 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003085
cristy79cedc72011-07-25 00:41:15 +00003086 register ssize_t
3087 x;
3088
cristy3ed852e2009-09-05 21:47:34 +00003089 if (status == MagickFalse)
3090 continue;
cristy79cedc72011-07-25 00:41:15 +00003091 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003092 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003093 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003094 {
3095 status=MagickFalse;
3096 continue;
3097 }
cristybb503372010-05-27 20:51:26 +00003098 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003099 {
cristy79cedc72011-07-25 00:41:15 +00003100 register ssize_t
3101 i;
3102
3103 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3104 {
3105 MagickRealType
3106 alpha;
3107
3108 PixelChannel
3109 channel;
3110
3111 PixelTrait
3112 fx_traits,
3113 traits;
3114
cristye2a912b2011-12-05 20:02:07 +00003115 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003116 traits=GetPixelChannelMapTraits(image,channel);
cristy79cedc72011-07-25 00:41:15 +00003117 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
cristy010d7d12011-08-31 01:02:48 +00003118 if ((traits == UndefinedPixelTrait) ||
3119 (fx_traits == UndefinedPixelTrait))
cristy79cedc72011-07-25 00:41:15 +00003120 continue;
cristyd09f8802012-02-04 16:44:10 +00003121 if (((fx_traits & CopyPixelTrait) != 0) ||
3122 (GetPixelMask(image,p) != 0))
cristy79cedc72011-07-25 00:41:15 +00003123 {
cristy0beccfa2011-09-25 20:47:53 +00003124 SetPixelChannel(fx_image,channel,p[i],q);
cristy79cedc72011-07-25 00:41:15 +00003125 continue;
3126 }
3127 alpha=0.0;
cristya382aca2011-12-06 18:22:48 +00003128 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3129 exception);
cristyb3a73b52011-07-26 01:34:43 +00003130 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003131 }
3132 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003133 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003134 }
3135 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3136 status=MagickFalse;
3137 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3138 {
3139 MagickBooleanType
3140 proceed;
3141
cristyb5d5f722009-11-04 03:03:49 +00003142#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003143 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003144#endif
3145 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3146 if (proceed == MagickFalse)
3147 status=MagickFalse;
3148 }
3149 }
cristy3ed852e2009-09-05 21:47:34 +00003150 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003151 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003152 fx_info=DestroyFxThreadSet(fx_info);
3153 if (status == MagickFalse)
3154 fx_image=DestroyImage(fx_image);
3155 return(fx_image);
3156}
3157
3158/*
3159%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3160% %
3161% %
3162% %
3163% I m p l o d e I m a g e %
3164% %
3165% %
3166% %
3167%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3168%
3169% ImplodeImage() creates a new image that is a copy of an existing
3170% one with the image pixels "implode" by the specified percentage. It
3171% allocates the memory necessary for the new Image structure and returns a
3172% pointer to the new image.
3173%
3174% The format of the ImplodeImage method is:
3175%
3176% Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003177% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003178%
3179% A description of each parameter follows:
3180%
3181% o implode_image: Method ImplodeImage returns a pointer to the image
3182% after it is implode. A null image is returned if there is a memory
3183% shortage.
3184%
3185% o image: the image.
3186%
3187% o amount: Define the extent of the implosion.
3188%
cristy76f512e2011-09-12 01:26:56 +00003189% o method: the pixel interpolation method.
3190%
cristy3ed852e2009-09-05 21:47:34 +00003191% o exception: return any errors or warnings in this structure.
3192%
3193*/
3194MagickExport Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003195 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003196{
3197#define ImplodeImageTag "Implode/Image"
3198
cristyfa112112010-01-04 17:48:07 +00003199 CacheView
3200 *image_view,
3201 *implode_view;
3202
cristy3ed852e2009-09-05 21:47:34 +00003203 Image
3204 *implode_image;
3205
cristy3ed852e2009-09-05 21:47:34 +00003206 MagickBooleanType
3207 status;
3208
cristybb503372010-05-27 20:51:26 +00003209 MagickOffsetType
3210 progress;
3211
cristy3ed852e2009-09-05 21:47:34 +00003212 MagickRealType
3213 radius;
3214
3215 PointInfo
3216 center,
3217 scale;
3218
cristybb503372010-05-27 20:51:26 +00003219 ssize_t
3220 y;
3221
cristy3ed852e2009-09-05 21:47:34 +00003222 /*
3223 Initialize implode image attributes.
3224 */
3225 assert(image != (Image *) NULL);
3226 assert(image->signature == MagickSignature);
3227 if (image->debug != MagickFalse)
3228 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3229 assert(exception != (ExceptionInfo *) NULL);
3230 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00003231 implode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3232 exception);
cristy3ed852e2009-09-05 21:47:34 +00003233 if (implode_image == (Image *) NULL)
3234 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003235 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003236 {
cristy3ed852e2009-09-05 21:47:34 +00003237 implode_image=DestroyImage(implode_image);
3238 return((Image *) NULL);
3239 }
cristy4c08aed2011-07-01 19:47:50 +00003240 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003241 implode_image->matte=MagickTrue;
3242 /*
3243 Compute scaling factor.
3244 */
3245 scale.x=1.0;
3246 scale.y=1.0;
3247 center.x=0.5*image->columns;
3248 center.y=0.5*image->rows;
3249 radius=center.x;
3250 if (image->columns > image->rows)
3251 scale.y=(double) image->columns/(double) image->rows;
3252 else
3253 if (image->columns < image->rows)
3254 {
3255 scale.x=(double) image->rows/(double) image->columns;
3256 radius=center.y;
3257 }
3258 /*
3259 Implode image.
3260 */
3261 status=MagickTrue;
3262 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003263 image_view=AcquireCacheView(image);
3264 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003265#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003266 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003267#endif
cristybb503372010-05-27 20:51:26 +00003268 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003269 {
cristy3ed852e2009-09-05 21:47:34 +00003270 MagickRealType
3271 distance;
3272
3273 PointInfo
3274 delta;
3275
cristy6d188022011-09-12 13:23:33 +00003276 register const Quantum
3277 *restrict p;
3278
cristybb503372010-05-27 20:51:26 +00003279 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003280 x;
3281
cristy4c08aed2011-07-01 19:47:50 +00003282 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003283 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003284
3285 if (status == MagickFalse)
3286 continue;
cristy6d188022011-09-12 13:23:33 +00003287 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003288 q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003289 exception);
cristy6d188022011-09-12 13:23:33 +00003290 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003291 {
3292 status=MagickFalse;
3293 continue;
3294 }
cristy3ed852e2009-09-05 21:47:34 +00003295 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00003296 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003297 {
cristy6d188022011-09-12 13:23:33 +00003298 register ssize_t
3299 i;
3300
cristy3ed852e2009-09-05 21:47:34 +00003301 /*
3302 Determine if the pixel is within an ellipse.
3303 */
cristy10a6c612012-01-29 21:41:05 +00003304 if (GetPixelMask(image,p) != 0)
3305 {
3306 p+=GetPixelChannels(image);
3307 q+=GetPixelChannels(implode_image);
3308 continue;
3309 }
cristy3ed852e2009-09-05 21:47:34 +00003310 delta.x=scale.x*(double) (x-center.x);
3311 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00003312 if (distance >= (radius*radius))
cristya6d7a9b2012-01-18 20:04:48 +00003313 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy6d188022011-09-12 13:23:33 +00003314 {
cristya6d7a9b2012-01-18 20:04:48 +00003315 PixelChannel
3316 channel;
3317
cristy1707c6c2012-01-18 23:30:54 +00003318 PixelTrait
3319 implode_traits,
3320 traits;
3321
cristya6d7a9b2012-01-18 20:04:48 +00003322 channel=GetPixelChannelMapChannel(image,i);
cristy1707c6c2012-01-18 23:30:54 +00003323 traits=GetPixelChannelMapTraits(image,channel);
3324 implode_traits=GetPixelChannelMapTraits(implode_image,channel);
3325 if ((traits == UndefinedPixelTrait) ||
3326 (implode_traits == UndefinedPixelTrait))
3327 continue;
cristya6d7a9b2012-01-18 20:04:48 +00003328 SetPixelChannel(implode_image,channel,p[i],q);
cristy6d188022011-09-12 13:23:33 +00003329 }
3330 else
cristy3ed852e2009-09-05 21:47:34 +00003331 {
3332 double
3333 factor;
3334
3335 /*
3336 Implode the pixel.
3337 */
3338 factor=1.0;
3339 if (distance > 0.0)
cristy1707c6c2012-01-18 23:30:54 +00003340 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/radius/
3341 2)),-amount);
cristy76f512e2011-09-12 01:26:56 +00003342 status=InterpolatePixelChannels(image,image_view,implode_image,method,
3343 (double) (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3344 scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00003345 }
cristy6d188022011-09-12 13:23:33 +00003346 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003347 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003348 }
3349 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3350 status=MagickFalse;
3351 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3352 {
3353 MagickBooleanType
3354 proceed;
3355
cristyb5d5f722009-11-04 03:03:49 +00003356#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003357 #pragma omp critical (MagickCore_ImplodeImage)
cristy3ed852e2009-09-05 21:47:34 +00003358#endif
3359 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3360 if (proceed == MagickFalse)
3361 status=MagickFalse;
3362 }
3363 }
3364 implode_view=DestroyCacheView(implode_view);
3365 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003366 if (status == MagickFalse)
3367 implode_image=DestroyImage(implode_image);
3368 return(implode_image);
3369}
3370
3371/*
3372%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3373% %
3374% %
3375% %
3376% M o r p h I m a g e s %
3377% %
3378% %
3379% %
3380%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3381%
3382% The MorphImages() method requires a minimum of two images. The first
3383% image is transformed into the second by a number of intervening images
3384% as specified by frames.
3385%
3386% The format of the MorphImage method is:
3387%
cristybb503372010-05-27 20:51:26 +00003388% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003389% ExceptionInfo *exception)
3390%
3391% A description of each parameter follows:
3392%
3393% o image: the image.
3394%
3395% o number_frames: Define the number of in-between image to generate.
3396% The more in-between frames, the smoother the morph.
3397%
3398% o exception: return any errors or warnings in this structure.
3399%
3400*/
3401MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003402 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003403{
3404#define MorphImageTag "Morph/Image"
3405
3406 Image
3407 *morph_image,
3408 *morph_images;
3409
cristy9d314ff2011-03-09 01:30:28 +00003410 MagickBooleanType
3411 status;
cristy3ed852e2009-09-05 21:47:34 +00003412
3413 MagickOffsetType
3414 scene;
3415
3416 MagickRealType
3417 alpha,
3418 beta;
3419
3420 register const Image
3421 *next;
3422
cristybb503372010-05-27 20:51:26 +00003423 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003424 i;
3425
cristy9d314ff2011-03-09 01:30:28 +00003426 ssize_t
3427 y;
cristy3ed852e2009-09-05 21:47:34 +00003428
3429 /*
3430 Clone first frame in sequence.
3431 */
3432 assert(image != (Image *) NULL);
3433 assert(image->signature == MagickSignature);
3434 if (image->debug != MagickFalse)
3435 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3436 assert(exception != (ExceptionInfo *) NULL);
3437 assert(exception->signature == MagickSignature);
3438 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3439 if (morph_images == (Image *) NULL)
3440 return((Image *) NULL);
3441 if (GetNextImageInList(image) == (Image *) NULL)
3442 {
3443 /*
3444 Morph single image.
3445 */
cristybb503372010-05-27 20:51:26 +00003446 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003447 {
3448 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3449 if (morph_image == (Image *) NULL)
3450 {
3451 morph_images=DestroyImageList(morph_images);
3452 return((Image *) NULL);
3453 }
3454 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003455 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003456 {
cristy8b27a6d2010-02-14 03:31:15 +00003457 MagickBooleanType
3458 proceed;
3459
cristybb503372010-05-27 20:51:26 +00003460 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3461 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003462 if (proceed == MagickFalse)
3463 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003464 }
3465 }
3466 return(GetFirstImageInList(morph_images));
3467 }
3468 /*
3469 Morph image sequence.
3470 */
3471 status=MagickTrue;
3472 scene=0;
3473 next=image;
3474 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3475 {
cristybb503372010-05-27 20:51:26 +00003476 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003477 {
3478 CacheView
3479 *image_view,
3480 *morph_view;
3481
3482 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3483 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003484 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristybb503372010-05-27 20:51:26 +00003485 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
cristy15b98cd2010-09-12 19:42:50 +00003486 next->rows+beta*GetNextImageInList(next)->rows+0.5),
3487 next->filter,next->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003488 if (morph_image == (Image *) NULL)
3489 {
3490 morph_images=DestroyImageList(morph_images);
3491 return((Image *) NULL);
3492 }
cristy1707c6c2012-01-18 23:30:54 +00003493 status=SetImageStorageClass(morph_image,DirectClass,exception);
3494 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003495 {
cristy3ed852e2009-09-05 21:47:34 +00003496 morph_image=DestroyImage(morph_image);
3497 return((Image *) NULL);
3498 }
3499 AppendImageToList(&morph_images,morph_image);
3500 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003501 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3502 morph_images->rows,GetNextImageInList(next)->filter,
3503 GetNextImageInList(next)->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003504 if (morph_image == (Image *) NULL)
3505 {
3506 morph_images=DestroyImageList(morph_images);
3507 return((Image *) NULL);
3508 }
3509 image_view=AcquireCacheView(morph_image);
3510 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003511#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003512 #pragma omp parallel for schedule(static,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003513#endif
cristybb503372010-05-27 20:51:26 +00003514 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003515 {
3516 MagickBooleanType
3517 sync;
3518
cristy4c08aed2011-07-01 19:47:50 +00003519 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003520 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003521
cristybb503372010-05-27 20:51:26 +00003522 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003523 x;
3524
cristy4c08aed2011-07-01 19:47:50 +00003525 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003526 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003527
3528 if (status == MagickFalse)
3529 continue;
3530 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3531 exception);
3532 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3533 exception);
cristy4c08aed2011-07-01 19:47:50 +00003534 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003535 {
3536 status=MagickFalse;
3537 continue;
3538 }
cristybb503372010-05-27 20:51:26 +00003539 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003540 {
cristy1707c6c2012-01-18 23:30:54 +00003541 register ssize_t
3542 i;
3543
cristy10a6c612012-01-29 21:41:05 +00003544 for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
cristy1707c6c2012-01-18 23:30:54 +00003545 {
3546 PixelChannel
3547 channel;
3548
3549 PixelTrait
3550 morph_traits,
3551 traits;
3552
3553 channel=GetPixelChannelMapChannel(image,i);
3554 traits=GetPixelChannelMapTraits(image,channel);
3555 morph_traits=GetPixelChannelMapTraits(morph_image,channel);
3556 if ((traits == UndefinedPixelTrait) ||
3557 (morph_traits == UndefinedPixelTrait))
3558 continue;
cristyd09f8802012-02-04 16:44:10 +00003559 if (((morph_traits & CopyPixelTrait) != 0) ||
3560 (GetPixelMask(image,p) != 0))
cristy1707c6c2012-01-18 23:30:54 +00003561 {
3562 SetPixelChannel(morph_image,channel,p[i],q);
3563 continue;
3564 }
3565 SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3566 GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3567 }
cristyed231572011-07-14 02:18:59 +00003568 p+=GetPixelChannels(morph_image);
3569 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003570 }
3571 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3572 if (sync == MagickFalse)
3573 status=MagickFalse;
3574 }
3575 morph_view=DestroyCacheView(morph_view);
3576 image_view=DestroyCacheView(image_view);
3577 morph_image=DestroyImage(morph_image);
3578 }
cristybb503372010-05-27 20:51:26 +00003579 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003580 break;
3581 /*
3582 Clone last frame in sequence.
3583 */
3584 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3585 if (morph_image == (Image *) NULL)
3586 {
3587 morph_images=DestroyImageList(morph_images);
3588 return((Image *) NULL);
3589 }
3590 AppendImageToList(&morph_images,morph_image);
3591 morph_images=GetLastImageInList(morph_images);
3592 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3593 {
3594 MagickBooleanType
3595 proceed;
3596
cristyb5d5f722009-11-04 03:03:49 +00003597#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003598 #pragma omp critical (MagickCore_MorphImages)
cristy3ed852e2009-09-05 21:47:34 +00003599#endif
3600 proceed=SetImageProgress(image,MorphImageTag,scene,
3601 GetImageListLength(image));
3602 if (proceed == MagickFalse)
3603 status=MagickFalse;
3604 }
3605 scene++;
3606 }
3607 if (GetNextImageInList(next) != (Image *) NULL)
3608 {
3609 morph_images=DestroyImageList(morph_images);
3610 return((Image *) NULL);
3611 }
3612 return(GetFirstImageInList(morph_images));
3613}
3614
3615/*
3616%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3617% %
3618% %
3619% %
3620% P l a s m a I m a g e %
3621% %
3622% %
3623% %
3624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3625%
3626% PlasmaImage() initializes an image with plasma fractal values. The image
3627% must be initialized with a base color and the random number generator
3628% seeded before this method is called.
3629%
3630% The format of the PlasmaImage method is:
3631%
3632% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristy5cbc0162011-08-29 00:36:28 +00003633% size_t attenuate,size_t depth,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003634%
3635% A description of each parameter follows:
3636%
3637% o image: the image.
3638%
3639% o segment: Define the region to apply plasma fractals values.
3640%
glennrp7dae1ca2010-09-16 12:17:35 +00003641% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003642%
3643% o depth: Limit the plasma recursion depth.
3644%
cristy5cbc0162011-08-29 00:36:28 +00003645% o exception: return any errors or warnings in this structure.
3646%
cristy3ed852e2009-09-05 21:47:34 +00003647*/
3648
3649static inline Quantum PlasmaPixel(RandomInfo *random_info,
3650 const MagickRealType pixel,const MagickRealType noise)
3651{
3652 Quantum
3653 plasma;
3654
cristyce70c172010-01-07 17:15:30 +00003655 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003656 noise/2.0);
3657 return(plasma);
3658}
3659
cristyda1f9c12011-10-02 21:39:49 +00003660static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
3661 CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3662 const SegmentInfo *segment,size_t attenuate,size_t depth,
3663 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003664{
cristy3ed852e2009-09-05 21:47:34 +00003665 MagickRealType
3666 plasma;
3667
cristyda1f9c12011-10-02 21:39:49 +00003668 PixelChannel
3669 channel;
3670
3671 PixelTrait
3672 traits;
3673
3674 register const Quantum
3675 *restrict u,
3676 *restrict v;
3677
3678 register Quantum
3679 *restrict q;
3680
3681 register ssize_t
3682 i;
cristy3ed852e2009-09-05 21:47:34 +00003683
cristy9d314ff2011-03-09 01:30:28 +00003684 ssize_t
3685 x,
3686 x_mid,
3687 y,
3688 y_mid;
3689
cristy3ed852e2009-09-05 21:47:34 +00003690 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3691 return(MagickTrue);
3692 if (depth != 0)
3693 {
3694 SegmentInfo
3695 local_info;
3696
3697 /*
3698 Divide the area into quadrants and recurse.
3699 */
3700 depth--;
3701 attenuate++;
cristybb503372010-05-27 20:51:26 +00003702 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3703 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003704 local_info=(*segment);
3705 local_info.x2=(double) x_mid;
3706 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003707 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3708 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003709 local_info=(*segment);
3710 local_info.y1=(double) y_mid;
3711 local_info.x2=(double) x_mid;
cristyda1f9c12011-10-02 21:39:49 +00003712 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3713 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003714 local_info=(*segment);
3715 local_info.x1=(double) x_mid;
3716 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003717 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3718 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003719 local_info=(*segment);
3720 local_info.x1=(double) x_mid;
3721 local_info.y1=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003722 return(PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3723 &local_info,attenuate,depth,exception));
cristy3ed852e2009-09-05 21:47:34 +00003724 }
cristybb503372010-05-27 20:51:26 +00003725 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3726 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003727 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3728 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3729 return(MagickFalse);
3730 /*
3731 Average pixels and apply plasma.
3732 */
cristy3ed852e2009-09-05 21:47:34 +00003733 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3734 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3735 {
cristy3ed852e2009-09-05 21:47:34 +00003736 /*
3737 Left pixel.
3738 */
cristybb503372010-05-27 20:51:26 +00003739 x=(ssize_t) ceil(segment->x1-0.5);
cristy1707c6c2012-01-18 23:30:54 +00003740 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3741 exception);
3742 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3743 exception);
cristyc5c6f662010-09-22 14:23:02 +00003744 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003745 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3746 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003747 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003748 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3749 {
cristye2a912b2011-12-05 20:02:07 +00003750 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003751 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003752 if (traits == UndefinedPixelTrait)
3753 continue;
3754 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3755 }
cristyc5c6f662010-09-22 14:23:02 +00003756 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003757 if (segment->x1 != segment->x2)
3758 {
3759 /*
3760 Right pixel.
3761 */
cristybb503372010-05-27 20:51:26 +00003762 x=(ssize_t) ceil(segment->x2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003763 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3764 1,1,exception);
3765 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3766 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003767 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003768 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3769 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003770 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003771 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3772 {
cristye2a912b2011-12-05 20:02:07 +00003773 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003774 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003775 if (traits == UndefinedPixelTrait)
3776 continue;
3777 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3778 }
cristyc5c6f662010-09-22 14:23:02 +00003779 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003780 }
3781 }
3782 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3783 {
3784 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3785 {
cristy3ed852e2009-09-05 21:47:34 +00003786 /*
3787 Bottom pixel.
3788 */
cristybb503372010-05-27 20:51:26 +00003789 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003790 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3791 1,1,exception);
3792 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3793 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003794 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003795 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3796 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003797 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003798 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3799 {
cristye2a912b2011-12-05 20:02:07 +00003800 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003801 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003802 if (traits == UndefinedPixelTrait)
3803 continue;
3804 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3805 }
cristyc5c6f662010-09-22 14:23:02 +00003806 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003807 }
3808 if (segment->y1 != segment->y2)
3809 {
cristy3ed852e2009-09-05 21:47:34 +00003810 /*
3811 Top pixel.
3812 */
cristybb503372010-05-27 20:51:26 +00003813 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003814 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3815 1,1,exception);
3816 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3817 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003818 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003819 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3820 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003821 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003822 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3823 {
cristye2a912b2011-12-05 20:02:07 +00003824 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003825 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003826 if (traits == UndefinedPixelTrait)
3827 continue;
3828 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3829 }
cristyc5c6f662010-09-22 14:23:02 +00003830 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003831 }
3832 }
3833 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3834 {
cristy3ed852e2009-09-05 21:47:34 +00003835 /*
3836 Middle pixel.
3837 */
cristybb503372010-05-27 20:51:26 +00003838 x=(ssize_t) ceil(segment->x1-0.5);
3839 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003840 u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
cristybb503372010-05-27 20:51:26 +00003841 x=(ssize_t) ceil(segment->x2-0.5);
3842 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003843 v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003844 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003845 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3846 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003847 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003848 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3849 {
cristye2a912b2011-12-05 20:02:07 +00003850 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003851 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003852 if (traits == UndefinedPixelTrait)
3853 continue;
3854 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3855 }
cristyc5c6f662010-09-22 14:23:02 +00003856 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003857 }
3858 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3859 return(MagickTrue);
3860 return(MagickFalse);
3861}
cristyda1f9c12011-10-02 21:39:49 +00003862
cristy3ed852e2009-09-05 21:47:34 +00003863MagickExport MagickBooleanType PlasmaImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00003864 const SegmentInfo *segment,size_t attenuate,size_t depth,
3865 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003866{
cristyc5c6f662010-09-22 14:23:02 +00003867 CacheView
cristyda1f9c12011-10-02 21:39:49 +00003868 *image_view,
3869 *u_view,
3870 *v_view;
cristyc5c6f662010-09-22 14:23:02 +00003871
cristy3ed852e2009-09-05 21:47:34 +00003872 MagickBooleanType
3873 status;
3874
3875 RandomInfo
3876 *random_info;
3877
3878 if (image->debug != MagickFalse)
3879 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3880 assert(image != (Image *) NULL);
3881 assert(image->signature == MagickSignature);
3882 if (image->debug != MagickFalse)
3883 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy5cbc0162011-08-29 00:36:28 +00003884 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristyc5c6f662010-09-22 14:23:02 +00003885 return(MagickFalse);
3886 image_view=AcquireCacheView(image);
cristyda1f9c12011-10-02 21:39:49 +00003887 u_view=AcquireCacheView(image);
3888 v_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003889 random_info=AcquireRandomInfo();
cristyda1f9c12011-10-02 21:39:49 +00003890 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
3891 attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003892 random_info=DestroyRandomInfo(random_info);
cristyda1f9c12011-10-02 21:39:49 +00003893 v_view=DestroyCacheView(v_view);
3894 u_view=DestroyCacheView(u_view);
cristyc5c6f662010-09-22 14:23:02 +00003895 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003896 return(status);
3897}
3898
3899/*
3900%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3901% %
3902% %
3903% %
3904% P o l a r o i d I m a g e %
3905% %
3906% %
3907% %
3908%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3909%
3910% PolaroidImage() simulates a Polaroid picture.
3911%
3912% The format of the AnnotateImage method is:
3913%
3914% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003915% const char *caption,const double angle,
3916% const PixelInterpolateMethod method,ExceptionInfo exception)
cristy3ed852e2009-09-05 21:47:34 +00003917%
3918% A description of each parameter follows:
3919%
3920% o image: the image.
3921%
3922% o draw_info: the draw info.
3923%
cristye9e3d382011-12-14 01:50:13 +00003924% o caption: the Polaroid caption.
3925%
cristycee97112010-05-28 00:44:52 +00003926% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003927%
cristy5c4e2582011-09-11 19:21:03 +00003928% o method: the pixel interpolation method.
3929%
cristy3ed852e2009-09-05 21:47:34 +00003930% o exception: return any errors or warnings in this structure.
3931%
3932*/
3933MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003934 const char *caption,const double angle,const PixelInterpolateMethod method,
cristy5c4e2582011-09-11 19:21:03 +00003935 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003936{
cristy3ed852e2009-09-05 21:47:34 +00003937 Image
3938 *bend_image,
3939 *caption_image,
3940 *flop_image,
3941 *picture_image,
3942 *polaroid_image,
3943 *rotate_image,
3944 *trim_image;
3945
cristybb503372010-05-27 20:51:26 +00003946 size_t
cristy3ed852e2009-09-05 21:47:34 +00003947 height;
3948
cristy9d314ff2011-03-09 01:30:28 +00003949 ssize_t
3950 quantum;
3951
cristy3ed852e2009-09-05 21:47:34 +00003952 /*
3953 Simulate a Polaroid picture.
3954 */
3955 assert(image != (Image *) NULL);
3956 assert(image->signature == MagickSignature);
3957 if (image->debug != MagickFalse)
3958 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3959 assert(exception != (ExceptionInfo *) NULL);
3960 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003961 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003962 image->rows)/25.0,10.0);
3963 height=image->rows+2*quantum;
3964 caption_image=(Image *) NULL;
cristye9e3d382011-12-14 01:50:13 +00003965 if (caption != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003966 {
3967 char
cristye9e3d382011-12-14 01:50:13 +00003968 geometry[MaxTextExtent],
3969 *text;
cristy3ed852e2009-09-05 21:47:34 +00003970
3971 DrawInfo
3972 *annotate_info;
3973
cristy3ed852e2009-09-05 21:47:34 +00003974 MagickBooleanType
3975 status;
3976
cristy9d314ff2011-03-09 01:30:28 +00003977 ssize_t
3978 count;
3979
cristy3ed852e2009-09-05 21:47:34 +00003980 TypeMetric
3981 metrics;
3982
3983 /*
3984 Generate caption image.
3985 */
3986 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3987 if (caption_image == (Image *) NULL)
3988 return((Image *) NULL);
3989 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
cristye9e3d382011-12-14 01:50:13 +00003990 text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
3991 exception);
3992 (void) CloneString(&annotate_info->text,text);
cristy6b1d05e2010-09-22 19:17:27 +00003993 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
cristye9e3d382011-12-14 01:50:13 +00003994 &text,exception);
3995 status=SetImageExtent(caption_image,image->columns,(size_t) ((count+1)*
3996 (metrics.ascent-metrics.descent)+0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00003997 if (status == MagickFalse)
3998 caption_image=DestroyImage(caption_image);
3999 else
4000 {
4001 caption_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004002 (void) SetImageBackgroundColor(caption_image,exception);
cristye9e3d382011-12-14 01:50:13 +00004003 (void) CloneString(&annotate_info->text,text);
cristyb51dff52011-05-19 16:55:47 +00004004 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00004005 metrics.ascent);
4006 if (annotate_info->gravity == UndefinedGravity)
4007 (void) CloneString(&annotate_info->geometry,AcquireString(
4008 geometry));
cristy5cbc0162011-08-29 00:36:28 +00004009 (void) AnnotateImage(caption_image,annotate_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004010 height+=caption_image->rows;
4011 }
4012 annotate_info=DestroyDrawInfo(annotate_info);
cristye9e3d382011-12-14 01:50:13 +00004013 text=DestroyString(text);
cristy3ed852e2009-09-05 21:47:34 +00004014 }
4015 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4016 exception);
4017 if (picture_image == (Image *) NULL)
4018 {
4019 if (caption_image != (Image *) NULL)
4020 caption_image=DestroyImage(caption_image);
4021 return((Image *) NULL);
4022 }
4023 picture_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004024 (void) SetImageBackgroundColor(picture_image,exception);
cristye941a752011-10-15 01:52:48 +00004025 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum,
4026 exception);
cristy3ed852e2009-09-05 21:47:34 +00004027 if (caption_image != (Image *) NULL)
4028 {
cristy1707c6c2012-01-18 23:30:54 +00004029 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,quantum,
4030 (ssize_t) (image->rows+3*quantum/2),exception);
cristy3ed852e2009-09-05 21:47:34 +00004031 caption_image=DestroyImage(caption_image);
4032 }
cristy9950d572011-10-01 18:22:35 +00004033 (void) QueryColorCompliance("none",AllCompliance,
4034 &picture_image->background_color,exception);
cristy63240882011-08-05 19:05:27 +00004035 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004036 rotate_image=RotateImage(picture_image,90.0,exception);
4037 picture_image=DestroyImage(picture_image);
4038 if (rotate_image == (Image *) NULL)
4039 return((Image *) NULL);
4040 picture_image=rotate_image;
4041 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
cristy5c4e2582011-09-11 19:21:03 +00004042 picture_image->columns,method,exception);
cristy3ed852e2009-09-05 21:47:34 +00004043 picture_image=DestroyImage(picture_image);
4044 if (bend_image == (Image *) NULL)
4045 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004046 picture_image=bend_image;
4047 rotate_image=RotateImage(picture_image,-90.0,exception);
4048 picture_image=DestroyImage(picture_image);
4049 if (rotate_image == (Image *) NULL)
4050 return((Image *) NULL);
4051 picture_image=rotate_image;
4052 picture_image->background_color=image->background_color;
cristy78ec1a92011-12-09 09:25:34 +00004053 polaroid_image=ShadowImage(picture_image,80.0,2.0,0.0,quantum/3,quantum/3,
cristy3ed852e2009-09-05 21:47:34 +00004054 exception);
4055 if (polaroid_image == (Image *) NULL)
4056 {
4057 picture_image=DestroyImage(picture_image);
4058 return(picture_image);
4059 }
4060 flop_image=FlopImage(polaroid_image,exception);
4061 polaroid_image=DestroyImage(polaroid_image);
4062 if (flop_image == (Image *) NULL)
4063 {
4064 picture_image=DestroyImage(picture_image);
4065 return(picture_image);
4066 }
4067 polaroid_image=flop_image;
cristy1707c6c2012-01-18 23:30:54 +00004068 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,(ssize_t)
4069 (-0.01*picture_image->columns/2.0),0L,exception);
cristy3ed852e2009-09-05 21:47:34 +00004070 picture_image=DestroyImage(picture_image);
cristy9950d572011-10-01 18:22:35 +00004071 (void) QueryColorCompliance("none",AllCompliance,
4072 &polaroid_image->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00004073 rotate_image=RotateImage(polaroid_image,angle,exception);
4074 polaroid_image=DestroyImage(polaroid_image);
4075 if (rotate_image == (Image *) NULL)
4076 return((Image *) NULL);
4077 polaroid_image=rotate_image;
4078 trim_image=TrimImage(polaroid_image,exception);
4079 polaroid_image=DestroyImage(polaroid_image);
4080 if (trim_image == (Image *) NULL)
4081 return((Image *) NULL);
4082 polaroid_image=trim_image;
4083 return(polaroid_image);
4084}
4085
4086/*
4087%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4088% %
4089% %
4090% %
cristy3ed852e2009-09-05 21:47:34 +00004091% S e p i a T o n e I m a g e %
4092% %
4093% %
4094% %
4095%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4096%
4097% MagickSepiaToneImage() applies a special effect to the image, similar to the
4098% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4099% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4100% threshold of 80% is a good starting point for a reasonable tone.
4101%
4102% The format of the SepiaToneImage method is:
4103%
4104% Image *SepiaToneImage(const Image *image,const double threshold,
4105% ExceptionInfo *exception)
4106%
4107% A description of each parameter follows:
4108%
4109% o image: the image.
4110%
4111% o threshold: the tone threshold.
4112%
4113% o exception: return any errors or warnings in this structure.
4114%
4115*/
4116MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4117 ExceptionInfo *exception)
4118{
4119#define SepiaToneImageTag "SepiaTone/Image"
4120
cristyc4c8d132010-01-07 01:58:38 +00004121 CacheView
4122 *image_view,
4123 *sepia_view;
4124
cristy3ed852e2009-09-05 21:47:34 +00004125 Image
4126 *sepia_image;
4127
cristy3ed852e2009-09-05 21:47:34 +00004128 MagickBooleanType
4129 status;
4130
cristybb503372010-05-27 20:51:26 +00004131 MagickOffsetType
4132 progress;
4133
4134 ssize_t
4135 y;
4136
cristy3ed852e2009-09-05 21:47:34 +00004137 /*
4138 Initialize sepia-toned image attributes.
4139 */
4140 assert(image != (const Image *) NULL);
4141 assert(image->signature == MagickSignature);
4142 if (image->debug != MagickFalse)
4143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4144 assert(exception != (ExceptionInfo *) NULL);
4145 assert(exception->signature == MagickSignature);
cristy1707c6c2012-01-18 23:30:54 +00004146 sepia_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004147 if (sepia_image == (Image *) NULL)
4148 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004149 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004150 {
cristy3ed852e2009-09-05 21:47:34 +00004151 sepia_image=DestroyImage(sepia_image);
4152 return((Image *) NULL);
4153 }
4154 /*
4155 Tone each row of the image.
4156 */
4157 status=MagickTrue;
4158 progress=0;
4159 image_view=AcquireCacheView(image);
4160 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004161#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004162 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004163#endif
cristybb503372010-05-27 20:51:26 +00004164 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004165 {
cristy4c08aed2011-07-01 19:47:50 +00004166 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004167 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004168
cristybb503372010-05-27 20:51:26 +00004169 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004170 x;
4171
cristy4c08aed2011-07-01 19:47:50 +00004172 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004173 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004174
4175 if (status == MagickFalse)
4176 continue;
4177 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00004178 q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004179 exception);
cristy4c08aed2011-07-01 19:47:50 +00004180 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004181 {
4182 status=MagickFalse;
4183 continue;
4184 }
cristybb503372010-05-27 20:51:26 +00004185 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004186 {
4187 MagickRealType
4188 intensity,
4189 tone;
4190
cristy4c08aed2011-07-01 19:47:50 +00004191 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004192 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4193 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004194 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004195 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4196 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004197 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004198 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004199 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004200 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004201 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4202 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4203 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4204 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004205 p+=GetPixelChannels(image);
4206 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004207 }
4208 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4209 status=MagickFalse;
4210 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4211 {
4212 MagickBooleanType
4213 proceed;
4214
cristyb5d5f722009-11-04 03:03:49 +00004215#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004216 #pragma omp critical (MagickCore_SepiaToneImage)
cristy3ed852e2009-09-05 21:47:34 +00004217#endif
4218 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4219 image->rows);
4220 if (proceed == MagickFalse)
4221 status=MagickFalse;
4222 }
4223 }
4224 sepia_view=DestroyCacheView(sepia_view);
4225 image_view=DestroyCacheView(image_view);
cristye23ec9d2011-08-16 18:15:40 +00004226 (void) NormalizeImage(sepia_image,exception);
4227 (void) ContrastImage(sepia_image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004228 if (status == MagickFalse)
4229 sepia_image=DestroyImage(sepia_image);
4230 return(sepia_image);
4231}
4232
4233/*
4234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4235% %
4236% %
4237% %
4238% S h a d o w I m a g e %
4239% %
4240% %
4241% %
4242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4243%
4244% ShadowImage() simulates a shadow from the specified image and returns it.
4245%
4246% The format of the ShadowImage method is:
4247%
cristy70cddf72011-12-10 22:42:42 +00004248% Image *ShadowImage(const Image *image,const double alpha,
cristyeb6e6582011-12-09 09:14:23 +00004249% const double sigma,const double bias,const ssize_t x_offset,
4250% const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004251%
4252% A description of each parameter follows:
4253%
4254% o image: the image.
4255%
cristy70cddf72011-12-10 22:42:42 +00004256% o alpha: percentage transparency.
cristy3ed852e2009-09-05 21:47:34 +00004257%
4258% o sigma: the standard deviation of the Gaussian, in pixels.
4259%
cristyeb6e6582011-12-09 09:14:23 +00004260% o bias: the bias.
4261%
cristy3ed852e2009-09-05 21:47:34 +00004262% o x_offset: the shadow x-offset.
4263%
4264% o y_offset: the shadow y-offset.
4265%
4266% o exception: return any errors or warnings in this structure.
4267%
4268*/
cristy70cddf72011-12-10 22:42:42 +00004269MagickExport Image *ShadowImage(const Image *image,const double alpha,
cristyeb6e6582011-12-09 09:14:23 +00004270 const double sigma,const double bias,const ssize_t x_offset,
4271 const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004272{
4273#define ShadowImageTag "Shadow/Image"
4274
cristy70cddf72011-12-10 22:42:42 +00004275 CacheView
4276 *image_view;
4277
cristybd5a96c2011-08-21 00:04:26 +00004278 ChannelType
4279 channel_mask;
4280
cristy3ed852e2009-09-05 21:47:34 +00004281 Image
4282 *border_image,
4283 *clone_image,
4284 *shadow_image;
4285
cristy70cddf72011-12-10 22:42:42 +00004286 MagickBooleanType
4287 status;
4288
cristy3ed852e2009-09-05 21:47:34 +00004289 RectangleInfo
4290 border_info;
4291
cristy70cddf72011-12-10 22:42:42 +00004292 ssize_t
4293 y;
4294
cristy3ed852e2009-09-05 21:47:34 +00004295 assert(image != (Image *) NULL);
4296 assert(image->signature == MagickSignature);
4297 if (image->debug != MagickFalse)
4298 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4299 assert(exception != (ExceptionInfo *) NULL);
4300 assert(exception->signature == MagickSignature);
4301 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4302 if (clone_image == (Image *) NULL)
4303 return((Image *) NULL);
cristy387430f2012-02-07 13:09:46 +00004304 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod,
4305 exception);
cristybb503372010-05-27 20:51:26 +00004306 border_info.width=(size_t) floor(2.0*sigma+0.5);
4307 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004308 border_info.x=0;
4309 border_info.y=0;
cristy9950d572011-10-01 18:22:35 +00004310 (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4311 exception);
cristy70cddf72011-12-10 22:42:42 +00004312 clone_image->matte=MagickTrue;
4313 border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
cristy3ed852e2009-09-05 21:47:34 +00004314 clone_image=DestroyImage(clone_image);
4315 if (border_image == (Image *) NULL)
4316 return((Image *) NULL);
4317 if (border_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004318 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004319 /*
4320 Shadow image.
4321 */
cristy70cddf72011-12-10 22:42:42 +00004322 status=MagickTrue;
4323 image_view=AcquireCacheView(border_image);
4324 for (y=0; y < (ssize_t) border_image->rows; y++)
4325 {
4326 PixelInfo
4327 background_color;
4328
4329 register Quantum
4330 *restrict q;
4331
4332 register ssize_t
4333 x;
4334
4335 if (status == MagickFalse)
4336 continue;
4337 q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4338 exception);
4339 if (q == (Quantum *) NULL)
4340 {
4341 status=MagickFalse;
4342 continue;
4343 }
4344 background_color=border_image->background_color;
4345 background_color.matte=MagickTrue;
4346 for (x=0; x < (ssize_t) border_image->columns; x++)
4347 {
4348 if (border_image->matte != MagickFalse)
4349 background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4350 SetPixelInfoPixel(border_image,&background_color,q);
4351 q+=GetPixelChannels(border_image);
4352 }
4353 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4354 status=MagickFalse;
4355 }
4356 image_view=DestroyCacheView(image_view);
4357 if (status == MagickFalse)
4358 {
4359 border_image=DestroyImage(border_image);
4360 return((Image *) NULL);
4361 }
cristybd5a96c2011-08-21 00:04:26 +00004362 channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
cristyb27bb772011-12-11 16:12:50 +00004363 shadow_image=BlurImage(border_image,0.0,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00004364 border_image=DestroyImage(border_image);
4365 if (shadow_image == (Image *) NULL)
4366 return((Image *) NULL);
cristyae1969f2011-12-10 03:07:36 +00004367 (void) SetPixelChannelMapMask(shadow_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004368 if (shadow_image->page.width == 0)
4369 shadow_image->page.width=shadow_image->columns;
4370 if (shadow_image->page.height == 0)
4371 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004372 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4373 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4374 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4375 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004376 return(shadow_image);
4377}
4378
4379/*
4380%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4381% %
4382% %
4383% %
4384% S k e t c h I m a g e %
4385% %
4386% %
4387% %
4388%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4389%
4390% SketchImage() simulates a pencil sketch. We convolve the image with a
4391% Gaussian operator of the given radius and standard deviation (sigma). For
4392% reasonable results, radius should be larger than sigma. Use a radius of 0
4393% and SketchImage() selects a suitable radius for you. Angle gives the angle
4394% of the sketch.
4395%
4396% The format of the SketchImage method is:
4397%
4398% Image *SketchImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00004399% const double sigma,const double angle,const double bias,
4400% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004401%
4402% A description of each parameter follows:
4403%
4404% o image: the image.
4405%
cristy574cc262011-08-05 01:23:58 +00004406% o radius: the radius of the Gaussian, in pixels, not counting the
4407% center pixel.
cristy3ed852e2009-09-05 21:47:34 +00004408%
4409% o sigma: the standard deviation of the Gaussian, in pixels.
4410%
cristyf7ef0252011-09-09 14:50:06 +00004411% o angle: apply the effect along this angle.
4412%
4413% o bias: the bias.
cristy3ed852e2009-09-05 21:47:34 +00004414%
4415% o exception: return any errors or warnings in this structure.
4416%
4417*/
4418MagickExport Image *SketchImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00004419 const double sigma,const double angle,const double bias,
4420 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004421{
cristyfa112112010-01-04 17:48:07 +00004422 CacheView
4423 *random_view;
4424
cristy3ed852e2009-09-05 21:47:34 +00004425 Image
4426 *blend_image,
4427 *blur_image,
4428 *dodge_image,
4429 *random_image,
4430 *sketch_image;
4431
cristy3ed852e2009-09-05 21:47:34 +00004432 MagickBooleanType
4433 status;
4434
cristy3ed852e2009-09-05 21:47:34 +00004435 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004436 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004437
cristy9d314ff2011-03-09 01:30:28 +00004438 ssize_t
4439 y;
4440
cristy3ed852e2009-09-05 21:47:34 +00004441 /*
4442 Sketch image.
4443 */
4444 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4445 MagickTrue,exception);
4446 if (random_image == (Image *) NULL)
4447 return((Image *) NULL);
4448 status=MagickTrue;
cristy1b784432009-12-19 02:20:40 +00004449 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004450 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004451#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004452 #pragma omp parallel for schedule(static,4) shared(status)
cristy1b784432009-12-19 02:20:40 +00004453#endif
cristybb503372010-05-27 20:51:26 +00004454 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004455 {
cristy5c9e6f22010-09-17 17:31:01 +00004456 const int
4457 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004458
cristybb503372010-05-27 20:51:26 +00004459 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004460 x;
4461
cristy4c08aed2011-07-01 19:47:50 +00004462 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004463 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004464
cristy1b784432009-12-19 02:20:40 +00004465 if (status == MagickFalse)
4466 continue;
cristy3ed852e2009-09-05 21:47:34 +00004467 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4468 exception);
cristyacd2ed22011-08-30 01:44:23 +00004469 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004470 {
4471 status=MagickFalse;
4472 continue;
4473 }
cristybb503372010-05-27 20:51:26 +00004474 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004475 {
cristy76f512e2011-09-12 01:26:56 +00004476 MagickRealType
4477 value;
4478
4479 register ssize_t
4480 i;
4481
cristy10a6c612012-01-29 21:41:05 +00004482 if (GetPixelMask(random_image,q) != 0)
4483 {
4484 q+=GetPixelChannels(random_image);
4485 continue;
4486 }
cristy76f512e2011-09-12 01:26:56 +00004487 value=GetPseudoRandomValue(random_info[id]);
4488 for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4489 {
cristyabace412011-12-11 15:56:53 +00004490 PixelChannel
4491 channel;
4492
cristy76f512e2011-09-12 01:26:56 +00004493 PixelTrait
4494 traits;
4495
cristyabace412011-12-11 15:56:53 +00004496 channel=GetPixelChannelMapChannel(image,i);
4497 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004498 if (traits == UndefinedPixelTrait)
4499 continue;
4500 q[i]=ClampToQuantum(QuantumRange*value);
4501 }
cristyed231572011-07-14 02:18:59 +00004502 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004503 }
4504 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4505 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004506 }
4507 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004508 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004509 if (status == MagickFalse)
4510 {
4511 random_image=DestroyImage(random_image);
4512 return(random_image);
4513 }
cristyf7ef0252011-09-09 14:50:06 +00004514 blur_image=MotionBlurImage(random_image,radius,sigma,angle,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00004515 random_image=DestroyImage(random_image);
4516 if (blur_image == (Image *) NULL)
4517 return((Image *) NULL);
cristy6bfd6902011-12-09 01:33:45 +00004518 dodge_image=EdgeImage(blur_image,radius,1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004519 blur_image=DestroyImage(blur_image);
4520 if (dodge_image == (Image *) NULL)
4521 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +00004522 (void) NormalizeImage(dodge_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00004523 (void) NegateImage(dodge_image,MagickFalse,exception);
cristye941a752011-10-15 01:52:48 +00004524 (void) TransformImage(&dodge_image,(char *) NULL,"50%",exception);
cristy3ed852e2009-09-05 21:47:34 +00004525 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4526 if (sketch_image == (Image *) NULL)
4527 {
4528 dodge_image=DestroyImage(dodge_image);
4529 return((Image *) NULL);
4530 }
cristye941a752011-10-15 01:52:48 +00004531 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0,
4532 exception);
cristy3ed852e2009-09-05 21:47:34 +00004533 dodge_image=DestroyImage(dodge_image);
4534 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4535 if (blend_image == (Image *) NULL)
4536 {
4537 sketch_image=DestroyImage(sketch_image);
4538 return((Image *) NULL);
4539 }
4540 (void) SetImageArtifact(blend_image,"compose:args","20x80");
cristye941a752011-10-15 01:52:48 +00004541 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0,
4542 exception);
cristy3ed852e2009-09-05 21:47:34 +00004543 blend_image=DestroyImage(blend_image);
4544 return(sketch_image);
4545}
4546
4547/*
4548%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4549% %
4550% %
4551% %
4552% S o l a r i z e I m a g e %
4553% %
4554% %
4555% %
4556%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4557%
4558% SolarizeImage() applies a special effect to the image, similar to the effect
4559% achieved in a photo darkroom by selectively exposing areas of photo
4560% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4561% measure of the extent of the solarization.
4562%
4563% The format of the SolarizeImage method is:
4564%
cristy5cbc0162011-08-29 00:36:28 +00004565% MagickBooleanType SolarizeImage(Image *image,const double threshold,
4566% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004567%
4568% A description of each parameter follows:
4569%
4570% o image: the image.
4571%
4572% o threshold: Define the extent of the solarization.
4573%
cristy5cbc0162011-08-29 00:36:28 +00004574% o exception: return any errors or warnings in this structure.
4575%
cristy3ed852e2009-09-05 21:47:34 +00004576*/
4577MagickExport MagickBooleanType SolarizeImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00004578 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004579{
4580#define SolarizeImageTag "Solarize/Image"
4581
cristyc4c8d132010-01-07 01:58:38 +00004582 CacheView
4583 *image_view;
4584
cristy3ed852e2009-09-05 21:47:34 +00004585 MagickBooleanType
4586 status;
4587
cristybb503372010-05-27 20:51:26 +00004588 MagickOffsetType
4589 progress;
4590
4591 ssize_t
4592 y;
4593
cristy3ed852e2009-09-05 21:47:34 +00004594 assert(image != (Image *) NULL);
4595 assert(image->signature == MagickSignature);
4596 if (image->debug != MagickFalse)
4597 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4598 if (image->storage_class == PseudoClass)
4599 {
cristybb503372010-05-27 20:51:26 +00004600 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004601 i;
4602
4603 /*
4604 Solarize colormap.
4605 */
cristybb503372010-05-27 20:51:26 +00004606 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004607 {
4608 if ((MagickRealType) image->colormap[i].red > threshold)
4609 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4610 if ((MagickRealType) image->colormap[i].green > threshold)
4611 image->colormap[i].green=(Quantum) QuantumRange-
4612 image->colormap[i].green;
4613 if ((MagickRealType) image->colormap[i].blue > threshold)
4614 image->colormap[i].blue=(Quantum) QuantumRange-
4615 image->colormap[i].blue;
4616 }
4617 }
4618 /*
4619 Solarize image.
4620 */
4621 status=MagickTrue;
4622 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00004623 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004624#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004625 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004626#endif
cristybb503372010-05-27 20:51:26 +00004627 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004628 {
cristybb503372010-05-27 20:51:26 +00004629 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004630 x;
4631
cristy4c08aed2011-07-01 19:47:50 +00004632 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004633 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004634
4635 if (status == MagickFalse)
4636 continue;
cristy5cbc0162011-08-29 00:36:28 +00004637 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00004638 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004639 {
4640 status=MagickFalse;
4641 continue;
4642 }
cristybb503372010-05-27 20:51:26 +00004643 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004644 {
cristy76f512e2011-09-12 01:26:56 +00004645 register ssize_t
4646 i;
4647
cristy10a6c612012-01-29 21:41:05 +00004648 if (GetPixelMask(image,q) != 0)
4649 {
4650 q+=GetPixelChannels(image);
4651 continue;
4652 }
cristy76f512e2011-09-12 01:26:56 +00004653 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4654 {
cristyabace412011-12-11 15:56:53 +00004655 PixelChannel
4656 channel;
4657
cristy76f512e2011-09-12 01:26:56 +00004658 PixelTrait
4659 traits;
4660
cristyabace412011-12-11 15:56:53 +00004661 channel=GetPixelChannelMapChannel(image,i);
4662 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004663 if ((traits == UndefinedPixelTrait) ||
4664 ((traits & CopyPixelTrait) != 0))
4665 continue;
4666 if ((MagickRealType) q[i] > threshold)
4667 q[i]=QuantumRange-q[i];
4668 }
cristyed231572011-07-14 02:18:59 +00004669 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004670 }
4671 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4672 status=MagickFalse;
4673 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4674 {
4675 MagickBooleanType
4676 proceed;
4677
cristyb5d5f722009-11-04 03:03:49 +00004678#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004679 #pragma omp critical (MagickCore_SolarizeImage)
cristy3ed852e2009-09-05 21:47:34 +00004680#endif
4681 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4682 if (proceed == MagickFalse)
4683 status=MagickFalse;
4684 }
4685 }
4686 image_view=DestroyCacheView(image_view);
4687 return(status);
4688}
4689
4690/*
4691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4692% %
4693% %
4694% %
4695% S t e g a n o I m a g e %
4696% %
4697% %
4698% %
4699%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4700%
4701% SteganoImage() hides a digital watermark within the image. Recover
4702% the hidden watermark later to prove that the authenticity of an image.
4703% Offset defines the start position within the image to hide the watermark.
4704%
4705% The format of the SteganoImage method is:
4706%
4707% Image *SteganoImage(const Image *image,Image *watermark,
4708% ExceptionInfo *exception)
4709%
4710% A description of each parameter follows:
4711%
4712% o image: the image.
4713%
4714% o watermark: the watermark image.
4715%
4716% o exception: return any errors or warnings in this structure.
4717%
4718*/
4719MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4720 ExceptionInfo *exception)
4721{
cristye1bf8ad2010-09-19 17:07:03 +00004722#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004723#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004724 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004725#define SteganoImageTag "Stegano/Image"
4726
cristyb0d3bb92010-09-22 14:37:58 +00004727 CacheView
4728 *stegano_view,
4729 *watermark_view;
4730
cristy3ed852e2009-09-05 21:47:34 +00004731 Image
4732 *stegano_image;
4733
4734 int
4735 c;
4736
cristy3ed852e2009-09-05 21:47:34 +00004737 MagickBooleanType
4738 status;
4739
cristy101ab702011-10-13 13:06:32 +00004740 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004741 pixel;
4742
cristy4c08aed2011-07-01 19:47:50 +00004743 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004744 *q;
4745
cristye1bf8ad2010-09-19 17:07:03 +00004746 register ssize_t
4747 x;
4748
cristybb503372010-05-27 20:51:26 +00004749 size_t
cristyeaedf062010-05-29 22:36:02 +00004750 depth,
4751 one;
cristy3ed852e2009-09-05 21:47:34 +00004752
cristye1bf8ad2010-09-19 17:07:03 +00004753 ssize_t
4754 i,
4755 j,
4756 k,
4757 y;
4758
cristy3ed852e2009-09-05 21:47:34 +00004759 /*
4760 Initialize steganographic image attributes.
4761 */
4762 assert(image != (const Image *) NULL);
4763 assert(image->signature == MagickSignature);
4764 if (image->debug != MagickFalse)
4765 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4766 assert(watermark != (const Image *) NULL);
4767 assert(watermark->signature == MagickSignature);
4768 assert(exception != (ExceptionInfo *) NULL);
4769 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004770 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004771 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4772 if (stegano_image == (Image *) NULL)
4773 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004774 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004775 {
cristy3ed852e2009-09-05 21:47:34 +00004776 stegano_image=DestroyImage(stegano_image);
4777 return((Image *) NULL);
4778 }
4779 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4780 /*
4781 Hide watermark in low-order bits of image.
4782 */
4783 c=0;
4784 i=0;
4785 j=0;
4786 depth=stegano_image->depth;
4787 k=image->offset;
cristyda16f162011-02-19 23:52:17 +00004788 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004789 watermark_view=AcquireCacheView(watermark);
4790 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004791 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004792 {
cristybb503372010-05-27 20:51:26 +00004793 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004794 {
cristybb503372010-05-27 20:51:26 +00004795 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004796 {
cristyda1f9c12011-10-02 21:39:49 +00004797 Quantum
cristy5f95f4f2011-10-23 01:01:01 +00004798 virtual_pixel[CompositePixelChannel];
cristyda1f9c12011-10-02 21:39:49 +00004799
cristy1707c6c2012-01-18 23:30:54 +00004800 ssize_t
4801 offset;
4802
cristyda1f9c12011-10-02 21:39:49 +00004803 (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,virtual_pixel,
4804 exception);
4805 pixel.red=(double) virtual_pixel[RedPixelChannel];
4806 pixel.green=(double) virtual_pixel[GreenPixelChannel];
4807 pixel.blue=(double) virtual_pixel[BluePixelChannel];
4808 pixel.alpha=(double) virtual_pixel[AlphaPixelChannel];
cristy1707c6c2012-01-18 23:30:54 +00004809 offset=k/(ssize_t) stegano_image->columns;
4810 if (offset >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004811 break;
cristyb0d3bb92010-09-22 14:37:58 +00004812 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4813 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4814 exception);
cristyacd2ed22011-08-30 01:44:23 +00004815 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004816 break;
4817 switch (c)
4818 {
4819 case 0:
4820 {
cristy4c08aed2011-07-01 19:47:50 +00004821 SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004822 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004823 break;
4824 }
4825 case 1:
4826 {
cristy4c08aed2011-07-01 19:47:50 +00004827 SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004828 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004829 break;
4830 }
4831 case 2:
4832 {
cristy4c08aed2011-07-01 19:47:50 +00004833 SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004834 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004835 break;
4836 }
4837 }
cristyb0d3bb92010-09-22 14:37:58 +00004838 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004839 break;
4840 c++;
4841 if (c == 3)
4842 c=0;
4843 k++;
cristybb503372010-05-27 20:51:26 +00004844 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004845 k=0;
4846 if (k == image->offset)
4847 j++;
4848 }
4849 }
cristy8b27a6d2010-02-14 03:31:15 +00004850 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004851 {
cristy8b27a6d2010-02-14 03:31:15 +00004852 MagickBooleanType
4853 proceed;
4854
4855 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4856 (depth-i),depth);
4857 if (proceed == MagickFalse)
4858 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004859 }
4860 }
cristyb0d3bb92010-09-22 14:37:58 +00004861 stegano_view=DestroyCacheView(stegano_view);
4862 watermark_view=DestroyCacheView(watermark_view);
cristy3ed852e2009-09-05 21:47:34 +00004863 if (stegano_image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00004864 (void) SyncImage(stegano_image,exception);
cristyda16f162011-02-19 23:52:17 +00004865 if (status == MagickFalse)
4866 {
4867 stegano_image=DestroyImage(stegano_image);
4868 return((Image *) NULL);
4869 }
cristy3ed852e2009-09-05 21:47:34 +00004870 return(stegano_image);
4871}
4872
4873/*
4874%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4875% %
4876% %
4877% %
4878% S t e r e o A n a g l y p h I m a g e %
4879% %
4880% %
4881% %
4882%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4883%
4884% StereoAnaglyphImage() combines two images and produces a single image that
4885% is the composite of a left and right image of a stereo pair. Special
4886% red-green stereo glasses are required to view this effect.
4887%
4888% The format of the StereoAnaglyphImage method is:
4889%
4890% Image *StereoImage(const Image *left_image,const Image *right_image,
4891% ExceptionInfo *exception)
4892% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004893% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004894% ExceptionInfo *exception)
4895%
4896% A description of each parameter follows:
4897%
4898% o left_image: the left image.
4899%
4900% o right_image: the right image.
4901%
4902% o exception: return any errors or warnings in this structure.
4903%
4904% o x_offset: amount, in pixels, by which the left image is offset to the
4905% right of the right image.
4906%
4907% o y_offset: amount, in pixels, by which the left image is offset to the
4908% bottom of the right image.
4909%
4910%
4911*/
4912MagickExport Image *StereoImage(const Image *left_image,
4913 const Image *right_image,ExceptionInfo *exception)
4914{
4915 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4916}
4917
4918MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004919 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004920 ExceptionInfo *exception)
4921{
4922#define StereoImageTag "Stereo/Image"
4923
4924 const Image
4925 *image;
4926
4927 Image
4928 *stereo_image;
4929
cristy3ed852e2009-09-05 21:47:34 +00004930 MagickBooleanType
4931 status;
4932
cristy9d314ff2011-03-09 01:30:28 +00004933 ssize_t
4934 y;
4935
cristy3ed852e2009-09-05 21:47:34 +00004936 assert(left_image != (const Image *) NULL);
4937 assert(left_image->signature == MagickSignature);
4938 if (left_image->debug != MagickFalse)
4939 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4940 left_image->filename);
4941 assert(right_image != (const Image *) NULL);
4942 assert(right_image->signature == MagickSignature);
4943 assert(exception != (ExceptionInfo *) NULL);
4944 assert(exception->signature == MagickSignature);
4945 assert(right_image != (const Image *) NULL);
4946 image=left_image;
4947 if ((left_image->columns != right_image->columns) ||
4948 (left_image->rows != right_image->rows))
4949 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4950 /*
4951 Initialize stereo image attributes.
4952 */
4953 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4954 MagickTrue,exception);
4955 if (stereo_image == (Image *) NULL)
4956 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004957 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004958 {
cristy3ed852e2009-09-05 21:47:34 +00004959 stereo_image=DestroyImage(stereo_image);
4960 return((Image *) NULL);
4961 }
4962 /*
4963 Copy left image to red channel and right image to blue channel.
4964 */
cristyda16f162011-02-19 23:52:17 +00004965 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004966 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004967 {
cristy4c08aed2011-07-01 19:47:50 +00004968 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004969 *restrict p,
4970 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004971
cristybb503372010-05-27 20:51:26 +00004972 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004973 x;
4974
cristy4c08aed2011-07-01 19:47:50 +00004975 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004976 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004977
4978 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4979 exception);
4980 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4981 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy76f512e2011-09-12 01:26:56 +00004982 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
4983 (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004984 break;
cristybb503372010-05-27 20:51:26 +00004985 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004986 {
cristy4c08aed2011-07-01 19:47:50 +00004987 SetPixelRed(image,GetPixelRed(left_image,p),r);
cristy76f512e2011-09-12 01:26:56 +00004988 SetPixelGreen(image,GetPixelGreen(right_image,q),r);
4989 SetPixelBlue(image,GetPixelBlue(right_image,q),r);
4990 if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
4991 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4992 GetPixelAlpha(right_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00004993 p+=GetPixelChannels(left_image);
cristy76f512e2011-09-12 01:26:56 +00004994 q+=GetPixelChannels(right_image);
4995 r+=GetPixelChannels(stereo_image);
cristy3ed852e2009-09-05 21:47:34 +00004996 }
4997 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4998 break;
cristy8b27a6d2010-02-14 03:31:15 +00004999 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005000 {
cristy8b27a6d2010-02-14 03:31:15 +00005001 MagickBooleanType
5002 proceed;
5003
cristybb503372010-05-27 20:51:26 +00005004 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
5005 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00005006 if (proceed == MagickFalse)
5007 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005008 }
5009 }
cristyda16f162011-02-19 23:52:17 +00005010 if (status == MagickFalse)
5011 {
5012 stereo_image=DestroyImage(stereo_image);
5013 return((Image *) NULL);
5014 }
cristy3ed852e2009-09-05 21:47:34 +00005015 return(stereo_image);
5016}
5017
5018/*
5019%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5020% %
5021% %
5022% %
5023% S w i r l I m a g e %
5024% %
5025% %
5026% %
5027%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5028%
5029% SwirlImage() swirls the pixels about the center of the image, where
5030% degrees indicates the sweep of the arc through which each pixel is moved.
5031% You get a more dramatic effect as the degrees move from 1 to 360.
5032%
5033% The format of the SwirlImage method is:
5034%
5035% Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005036% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005037%
5038% A description of each parameter follows:
5039%
5040% o image: the image.
5041%
5042% o degrees: Define the tightness of the swirling effect.
5043%
cristy76f512e2011-09-12 01:26:56 +00005044% o method: the pixel interpolation method.
5045%
cristy3ed852e2009-09-05 21:47:34 +00005046% o exception: return any errors or warnings in this structure.
5047%
5048*/
5049MagickExport Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005050 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005051{
5052#define SwirlImageTag "Swirl/Image"
5053
cristyfa112112010-01-04 17:48:07 +00005054 CacheView
5055 *image_view,
5056 *swirl_view;
5057
cristy3ed852e2009-09-05 21:47:34 +00005058 Image
5059 *swirl_image;
5060
cristy3ed852e2009-09-05 21:47:34 +00005061 MagickBooleanType
5062 status;
5063
cristybb503372010-05-27 20:51:26 +00005064 MagickOffsetType
5065 progress;
5066
cristy3ed852e2009-09-05 21:47:34 +00005067 MagickRealType
5068 radius;
5069
5070 PointInfo
5071 center,
5072 scale;
5073
cristybb503372010-05-27 20:51:26 +00005074 ssize_t
5075 y;
5076
cristy3ed852e2009-09-05 21:47:34 +00005077 /*
5078 Initialize swirl image attributes.
5079 */
5080 assert(image != (const Image *) NULL);
5081 assert(image->signature == MagickSignature);
5082 if (image->debug != MagickFalse)
5083 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5084 assert(exception != (ExceptionInfo *) NULL);
5085 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00005086 swirl_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005087 if (swirl_image == (Image *) NULL)
5088 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005089 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005090 {
cristy3ed852e2009-09-05 21:47:34 +00005091 swirl_image=DestroyImage(swirl_image);
5092 return((Image *) NULL);
5093 }
cristy4c08aed2011-07-01 19:47:50 +00005094 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005095 swirl_image->matte=MagickTrue;
5096 /*
5097 Compute scaling factor.
5098 */
5099 center.x=(double) image->columns/2.0;
5100 center.y=(double) image->rows/2.0;
5101 radius=MagickMax(center.x,center.y);
5102 scale.x=1.0;
5103 scale.y=1.0;
5104 if (image->columns > image->rows)
5105 scale.y=(double) image->columns/(double) image->rows;
5106 else
5107 if (image->columns < image->rows)
5108 scale.x=(double) image->rows/(double) image->columns;
5109 degrees=(double) DegreesToRadians(degrees);
5110 /*
5111 Swirl image.
5112 */
5113 status=MagickTrue;
5114 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00005115 image_view=AcquireCacheView(image);
5116 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005117#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005118 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005119#endif
cristybb503372010-05-27 20:51:26 +00005120 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005121 {
cristy3ed852e2009-09-05 21:47:34 +00005122 MagickRealType
5123 distance;
5124
5125 PointInfo
5126 delta;
5127
cristy6d188022011-09-12 13:23:33 +00005128 register const Quantum
5129 *restrict p;
5130
cristybb503372010-05-27 20:51:26 +00005131 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005132 x;
5133
cristy4c08aed2011-07-01 19:47:50 +00005134 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005135 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005136
5137 if (status == MagickFalse)
5138 continue;
cristy6d188022011-09-12 13:23:33 +00005139 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00005140 q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00005141 exception);
cristy6d188022011-09-12 13:23:33 +00005142 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005143 {
5144 status=MagickFalse;
5145 continue;
5146 }
cristy3ed852e2009-09-05 21:47:34 +00005147 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00005148 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005149 {
5150 /*
5151 Determine if the pixel is within an ellipse.
5152 */
cristy10a6c612012-01-29 21:41:05 +00005153 if (GetPixelMask(image,p) != 0)
5154 {
5155 p+=GetPixelChannels(image);
5156 q+=GetPixelChannels(swirl_image);
5157 continue;
5158 }
cristy3ed852e2009-09-05 21:47:34 +00005159 delta.x=scale.x*(double) (x-center.x);
5160 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00005161 if (distance >= (radius*radius))
5162 {
cristy1707c6c2012-01-18 23:30:54 +00005163 register ssize_t
5164 i;
5165
cristy6d188022011-09-12 13:23:33 +00005166 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy1707c6c2012-01-18 23:30:54 +00005167 {
5168 PixelChannel
5169 channel;
5170
5171 PixelTrait
5172 swirl_traits,
5173 traits;
5174
5175 channel=GetPixelChannelMapChannel(image,i);
5176 traits=GetPixelChannelMapTraits(image,channel);
5177 swirl_traits=GetPixelChannelMapTraits(swirl_image,channel);
5178 if ((traits == UndefinedPixelTrait) ||
5179 (swirl_traits == UndefinedPixelTrait))
5180 continue;
5181 SetPixelChannel(swirl_image,channel,p[i],q);
5182 }
cristy6d188022011-09-12 13:23:33 +00005183 }
5184 else
cristy3ed852e2009-09-05 21:47:34 +00005185 {
5186 MagickRealType
5187 cosine,
5188 factor,
5189 sine;
5190
5191 /*
5192 Swirl the pixel.
5193 */
5194 factor=1.0-sqrt((double) distance)/radius;
5195 sine=sin((double) (degrees*factor*factor));
5196 cosine=cos((double) (degrees*factor*factor));
cristy76f512e2011-09-12 01:26:56 +00005197 status=InterpolatePixelChannels(image,image_view,swirl_image,method,
5198 ((cosine*delta.x-sine*delta.y)/scale.x+center.x),(double)
5199 ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00005200 }
cristy6d188022011-09-12 13:23:33 +00005201 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00005202 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00005203 }
5204 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5205 status=MagickFalse;
5206 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5207 {
5208 MagickBooleanType
5209 proceed;
5210
cristyb5d5f722009-11-04 03:03:49 +00005211#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005212 #pragma omp critical (MagickCore_SwirlImage)
cristy3ed852e2009-09-05 21:47:34 +00005213#endif
5214 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5215 if (proceed == MagickFalse)
5216 status=MagickFalse;
5217 }
5218 }
5219 swirl_view=DestroyCacheView(swirl_view);
5220 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005221 if (status == MagickFalse)
5222 swirl_image=DestroyImage(swirl_image);
5223 return(swirl_image);
5224}
5225
5226/*
5227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5228% %
5229% %
5230% %
5231% T i n t I m a g e %
5232% %
5233% %
5234% %
5235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5236%
5237% TintImage() applies a color vector to each pixel in the image. The length
5238% of the vector is 0 for black and white and at its maximum for the midtones.
5239% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5240%
5241% The format of the TintImage method is:
5242%
cristyb817c3f2011-10-03 14:00:35 +00005243% Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005244% const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005245%
5246% A description of each parameter follows:
5247%
5248% o image: the image.
5249%
cristyb817c3f2011-10-03 14:00:35 +00005250% o blend: A color value used for tinting.
cristy3ed852e2009-09-05 21:47:34 +00005251%
5252% o tint: A color value used for tinting.
5253%
5254% o exception: return any errors or warnings in this structure.
5255%
5256*/
cristyb817c3f2011-10-03 14:00:35 +00005257MagickExport Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005258 const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005259{
5260#define TintImageTag "Tint/Image"
5261
cristyc4c8d132010-01-07 01:58:38 +00005262 CacheView
5263 *image_view,
5264 *tint_view;
5265
cristy3ed852e2009-09-05 21:47:34 +00005266 GeometryInfo
5267 geometry_info;
5268
5269 Image
5270 *tint_image;
5271
cristy3ed852e2009-09-05 21:47:34 +00005272 MagickBooleanType
5273 status;
5274
cristybb503372010-05-27 20:51:26 +00005275 MagickOffsetType
5276 progress;
cristy3ed852e2009-09-05 21:47:34 +00005277
cristy28474bf2011-09-11 23:32:52 +00005278 MagickRealType
5279 intensity;
5280
cristy4c08aed2011-07-01 19:47:50 +00005281 PixelInfo
cristy1707c6c2012-01-18 23:30:54 +00005282 color_vector;
cristy3ed852e2009-09-05 21:47:34 +00005283
cristybb503372010-05-27 20:51:26 +00005284 MagickStatusType
5285 flags;
5286
5287 ssize_t
5288 y;
5289
cristy3ed852e2009-09-05 21:47:34 +00005290 /*
5291 Allocate tint image.
5292 */
5293 assert(image != (const Image *) NULL);
5294 assert(image->signature == MagickSignature);
5295 if (image->debug != MagickFalse)
5296 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5297 assert(exception != (ExceptionInfo *) NULL);
5298 assert(exception->signature == MagickSignature);
5299 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5300 if (tint_image == (Image *) NULL)
5301 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005302 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005303 {
cristy3ed852e2009-09-05 21:47:34 +00005304 tint_image=DestroyImage(tint_image);
5305 return((Image *) NULL);
5306 }
cristyaed9c382011-10-03 17:54:21 +00005307 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005308 return(tint_image);
5309 /*
5310 Determine RGB values of the color.
5311 */
cristy1707c6c2012-01-18 23:30:54 +00005312 GetPixelInfo(image,&color_vector);
cristyb817c3f2011-10-03 14:00:35 +00005313 flags=ParseGeometry(blend,&geometry_info);
cristy1707c6c2012-01-18 23:30:54 +00005314 color_vector.red=geometry_info.rho;
5315 color_vector.green=geometry_info.rho;
5316 color_vector.blue=geometry_info.rho;
5317 color_vector.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005318 if ((flags & SigmaValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005319 color_vector.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00005320 if ((flags & XiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005321 color_vector.blue=geometry_info.xi;
cristyb817c3f2011-10-03 14:00:35 +00005322 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005323 color_vector.alpha=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005324 if (image->colorspace == CMYKColorspace)
5325 {
cristy1707c6c2012-01-18 23:30:54 +00005326 color_vector.black=geometry_info.rho;
cristy76f512e2011-09-12 01:26:56 +00005327 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005328 color_vector.black=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005329 if ((flags & ChiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005330 color_vector.alpha=geometry_info.chi;
cristy76f512e2011-09-12 01:26:56 +00005331 }
cristy28474bf2011-09-11 23:32:52 +00005332 intensity=(MagickRealType) GetPixelInfoIntensity(tint);
cristy1707c6c2012-01-18 23:30:54 +00005333 color_vector.red=(MagickRealType) (color_vector.red*tint->red/100.0-
5334 intensity);
5335 color_vector.green=(MagickRealType) (color_vector.green*tint->green/100.0-
5336 intensity);
5337 color_vector.blue=(MagickRealType) (color_vector.blue*tint->blue/100.0-
5338 intensity);
5339 color_vector.black=(MagickRealType) (color_vector.black*tint->black/100.0-
5340 intensity);
5341 color_vector.alpha=(MagickRealType) (color_vector.alpha*tint->alpha/100.0-
5342 intensity);
cristy3ed852e2009-09-05 21:47:34 +00005343 /*
5344 Tint image.
5345 */
5346 status=MagickTrue;
5347 progress=0;
5348 image_view=AcquireCacheView(image);
5349 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005350#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005351 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005352#endif
cristybb503372010-05-27 20:51:26 +00005353 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005354 {
cristy4c08aed2011-07-01 19:47:50 +00005355 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005356 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005357
cristy4c08aed2011-07-01 19:47:50 +00005358 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005359 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005360
cristy6b91acb2011-04-19 12:23:54 +00005361 register ssize_t
5362 x;
5363
cristy3ed852e2009-09-05 21:47:34 +00005364 if (status == MagickFalse)
5365 continue;
5366 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5367 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5368 exception);
cristy4c08aed2011-07-01 19:47:50 +00005369 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005370 {
5371 status=MagickFalse;
5372 continue;
5373 }
cristybb503372010-05-27 20:51:26 +00005374 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005375 {
cristy4c08aed2011-07-01 19:47:50 +00005376 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005377 pixel;
5378
5379 MagickRealType
5380 weight;
5381
cristy1707c6c2012-01-18 23:30:54 +00005382 register ssize_t
5383 i;
5384
cristy10a6c612012-01-29 21:41:05 +00005385 if (GetPixelMask(image,p) != 0)
5386 {
5387 p+=GetPixelChannels(image);
5388 q+=GetPixelChannels(tint_image);
5389 continue;
5390 }
cristy1707c6c2012-01-18 23:30:54 +00005391 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
5392 {
5393 PixelChannel
5394 channel;
5395
5396 PixelTrait
5397 tint_traits,
5398 traits;
5399
5400 channel=GetPixelChannelMapChannel(image,i);
5401 traits=GetPixelChannelMapTraits(image,channel);
5402 tint_traits=GetPixelChannelMapTraits(tint_image,channel);
5403 if ((traits == UndefinedPixelTrait) ||
5404 (tint_traits == UndefinedPixelTrait))
5405 continue;
5406 if ((tint_traits & CopyPixelTrait) != 0)
5407 {
5408 SetPixelChannel(tint_image,channel,p[i],q);
5409 continue;
5410 }
5411 }
5412 GetPixelInfo(image,&pixel);
5413 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5414 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5415 (1.0-(4.0*(weight*weight)));
5416 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5417 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5418 (1.0-(4.0*(weight*weight)));
5419 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5420 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5421 (1.0-(4.0*(weight*weight)));
5422 weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5423 pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5424 (1.0-(4.0*(weight*weight)));
5425 SetPixelInfoPixel(tint_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005426 p+=GetPixelChannels(image);
5427 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005428 }
5429 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5430 status=MagickFalse;
5431 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5432 {
5433 MagickBooleanType
5434 proceed;
5435
cristyb5d5f722009-11-04 03:03:49 +00005436#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005437 #pragma omp critical (MagickCore_TintImage)
cristy3ed852e2009-09-05 21:47:34 +00005438#endif
5439 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5440 if (proceed == MagickFalse)
5441 status=MagickFalse;
5442 }
5443 }
5444 tint_view=DestroyCacheView(tint_view);
5445 image_view=DestroyCacheView(image_view);
5446 if (status == MagickFalse)
5447 tint_image=DestroyImage(tint_image);
5448 return(tint_image);
5449}
5450
5451/*
5452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5453% %
5454% %
5455% %
5456% V i g n e t t e I m a g e %
5457% %
5458% %
5459% %
5460%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5461%
5462% VignetteImage() softens the edges of the image in vignette style.
5463%
5464% The format of the VignetteImage method is:
5465%
5466% Image *VignetteImage(const Image *image,const double radius,
cristyeb6e6582011-12-09 09:14:23 +00005467% const double sigma,const double bias,const ssize_t x,const ssize_t y,
cristy05c0c9a2011-09-05 23:16:13 +00005468% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005469%
5470% A description of each parameter follows:
5471%
5472% o image: the image.
5473%
5474% o radius: the radius of the pixel neighborhood.
5475%
5476% o sigma: the standard deviation of the Gaussian, in pixels.
5477%
cristyeb6e6582011-12-09 09:14:23 +00005478% o bias: the bias.
5479%
cristy3ed852e2009-09-05 21:47:34 +00005480% o x, y: Define the x and y ellipse offset.
5481%
5482% o exception: return any errors or warnings in this structure.
5483%
5484*/
5485MagickExport Image *VignetteImage(const Image *image,const double radius,
cristyeb6e6582011-12-09 09:14:23 +00005486 const double sigma,const double bias,const ssize_t x,const ssize_t y,
5487 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005488{
5489 char
5490 ellipse[MaxTextExtent];
5491
5492 DrawInfo
5493 *draw_info;
5494
5495 Image
5496 *canvas_image,
5497 *blur_image,
5498 *oval_image,
5499 *vignette_image;
5500
5501 assert(image != (Image *) NULL);
5502 assert(image->signature == MagickSignature);
5503 if (image->debug != MagickFalse)
5504 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5505 assert(exception != (ExceptionInfo *) NULL);
5506 assert(exception->signature == MagickSignature);
5507 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5508 if (canvas_image == (Image *) NULL)
5509 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005510 if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005511 {
cristy3ed852e2009-09-05 21:47:34 +00005512 canvas_image=DestroyImage(canvas_image);
5513 return((Image *) NULL);
5514 }
5515 canvas_image->matte=MagickTrue;
cristy98621462011-12-31 22:31:11 +00005516 oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
5517 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005518 if (oval_image == (Image *) NULL)
5519 {
5520 canvas_image=DestroyImage(canvas_image);
5521 return((Image *) NULL);
5522 }
cristy9950d572011-10-01 18:22:35 +00005523 (void) QueryColorCompliance("#000000",AllCompliance,
5524 &oval_image->background_color,exception);
cristyea1a8aa2011-10-20 13:24:06 +00005525 (void) SetImageBackgroundColor(oval_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005526 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00005527 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5528 exception);
5529 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5530 exception);
cristy1707c6c2012-01-18 23:30:54 +00005531 (void) FormatLocaleString(ellipse,MaxTextExtent,"ellipse %g,%g,%g,%g,"
5532 "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5533 image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005534 draw_info->primitive=AcquireString(ellipse);
cristy018f07f2011-09-04 21:15:19 +00005535 (void) DrawImage(oval_image,draw_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005536 draw_info=DestroyDrawInfo(draw_info);
cristyeb6e6582011-12-09 09:14:23 +00005537 blur_image=BlurImage(oval_image,radius,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00005538 oval_image=DestroyImage(oval_image);
5539 if (blur_image == (Image *) NULL)
5540 {
5541 canvas_image=DestroyImage(canvas_image);
5542 return((Image *) NULL);
5543 }
5544 blur_image->matte=MagickFalse;
cristy98621462011-12-31 22:31:11 +00005545 (void) CompositeImage(canvas_image,IntensityCompositeOp,blur_image,0,0,
cristye941a752011-10-15 01:52:48 +00005546 exception);
cristy3ed852e2009-09-05 21:47:34 +00005547 blur_image=DestroyImage(blur_image);
5548 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5549 canvas_image=DestroyImage(canvas_image);
5550 return(vignette_image);
5551}
5552
5553/*
5554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5555% %
5556% %
5557% %
5558% W a v e I m a g e %
5559% %
5560% %
5561% %
5562%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5563%
5564% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005565% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005566% by the given parameters.
5567%
5568% The format of the WaveImage method is:
5569%
5570% Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005571% const double wave_length,const PixelInterpolateMethod method,
5572% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005573%
5574% A description of each parameter follows:
5575%
5576% o image: the image.
5577%
5578% o amplitude, wave_length: Define the amplitude and wave length of the
5579% sine wave.
5580%
cristy5c4e2582011-09-11 19:21:03 +00005581% o interpolate: the pixel interpolation method.
5582%
cristy3ed852e2009-09-05 21:47:34 +00005583% o exception: return any errors or warnings in this structure.
5584%
5585*/
5586MagickExport Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005587 const double wave_length,const PixelInterpolateMethod method,
5588 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005589{
5590#define WaveImageTag "Wave/Image"
5591
cristyfa112112010-01-04 17:48:07 +00005592 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005593 *image_view,
cristyfa112112010-01-04 17:48:07 +00005594 *wave_view;
5595
cristy3ed852e2009-09-05 21:47:34 +00005596 Image
5597 *wave_image;
5598
cristy3ed852e2009-09-05 21:47:34 +00005599 MagickBooleanType
5600 status;
5601
cristybb503372010-05-27 20:51:26 +00005602 MagickOffsetType
5603 progress;
5604
cristy3ed852e2009-09-05 21:47:34 +00005605 MagickRealType
5606 *sine_map;
5607
cristybb503372010-05-27 20:51:26 +00005608 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005609 i;
5610
cristybb503372010-05-27 20:51:26 +00005611 ssize_t
5612 y;
5613
cristy3ed852e2009-09-05 21:47:34 +00005614 /*
5615 Initialize wave image attributes.
5616 */
5617 assert(image != (Image *) NULL);
5618 assert(image->signature == MagickSignature);
5619 if (image->debug != MagickFalse)
5620 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5621 assert(exception != (ExceptionInfo *) NULL);
5622 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005623 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005624 fabs(amplitude)),MagickTrue,exception);
5625 if (wave_image == (Image *) NULL)
5626 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005627 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005628 {
cristy3ed852e2009-09-05 21:47:34 +00005629 wave_image=DestroyImage(wave_image);
5630 return((Image *) NULL);
5631 }
cristy4c08aed2011-07-01 19:47:50 +00005632 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005633 wave_image->matte=MagickTrue;
5634 /*
5635 Allocate sine map.
5636 */
5637 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5638 sizeof(*sine_map));
5639 if (sine_map == (MagickRealType *) NULL)
5640 {
5641 wave_image=DestroyImage(wave_image);
5642 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5643 }
cristybb503372010-05-27 20:51:26 +00005644 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005645 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5646 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005647 /*
5648 Wave image.
5649 */
5650 status=MagickTrue;
5651 progress=0;
cristyd76c51e2011-03-26 00:21:26 +00005652 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005653 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005654 (void) SetCacheViewVirtualPixelMethod(image_view,
5655 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005656#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005657 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005658#endif
cristybb503372010-05-27 20:51:26 +00005659 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005660 {
cristy4c08aed2011-07-01 19:47:50 +00005661 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005662 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005663
cristye97bb922011-04-03 01:36:52 +00005664 register ssize_t
5665 x;
5666
cristy3ed852e2009-09-05 21:47:34 +00005667 if (status == MagickFalse)
5668 continue;
5669 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5670 exception);
cristyacd2ed22011-08-30 01:44:23 +00005671 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005672 {
5673 status=MagickFalse;
5674 continue;
5675 }
cristybb503372010-05-27 20:51:26 +00005676 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005677 {
cristy5c4e2582011-09-11 19:21:03 +00005678 status=InterpolatePixelChannels(image,image_view,wave_image,method,
5679 (double) x,(double) (y-sine_map[x]),q,exception);
cristyed231572011-07-14 02:18:59 +00005680 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005681 }
5682 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5683 status=MagickFalse;
5684 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5685 {
5686 MagickBooleanType
5687 proceed;
5688
cristyb5d5f722009-11-04 03:03:49 +00005689#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005690 #pragma omp critical (MagickCore_WaveImage)
cristy3ed852e2009-09-05 21:47:34 +00005691#endif
5692 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5693 if (proceed == MagickFalse)
5694 status=MagickFalse;
5695 }
5696 }
5697 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005698 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005699 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5700 if (status == MagickFalse)
5701 wave_image=DestroyImage(wave_image);
5702 return(wave_image);
5703}