blob: 600c28ba5f211d66a28d4ec9480e8172e0c3107b [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 {
983 MagickRealType
984 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 }
cristybb503372010-05-27 20:51:26 +00001005 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +00001006 {
cristybb503372010-05-27 20:51:26 +00001007 register ssize_t
cristye6365592010-04-02 17:31:23 +00001008 v;
1009
cristybb503372010-05-27 20:51:26 +00001010 size_t
cristye6365592010-04-02 17:31:23 +00001011 height;
1012
1013 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +00001014 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +00001015 {
cristy4c08aed2011-07-01 19:47:50 +00001016 pixel=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
1017 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +00001018 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001019 pixel+=ColorMatrix[v][3]*GetPixelBlack(image,p);
1020 if (image->matte != MagickFalse)
1021 pixel+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
cristye6365592010-04-02 17:31:23 +00001022 pixel+=QuantumRange*ColorMatrix[v][5];
1023 switch (v)
1024 {
cristy4c08aed2011-07-01 19:47:50 +00001025 case 0: SetPixelRed(color_image,ClampToQuantum(pixel),q); break;
1026 case 1: SetPixelGreen(color_image,ClampToQuantum(pixel),q); break;
1027 case 2: SetPixelBlue(color_image,ClampToQuantum(pixel),q); break;
cristye6365592010-04-02 17:31:23 +00001028 case 3:
1029 {
cristy4c08aed2011-07-01 19:47:50 +00001030 if (image->colorspace == CMYKColorspace)
1031 SetPixelBlack(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001032 break;
1033 }
1034 case 4:
1035 {
cristy4c08aed2011-07-01 19:47:50 +00001036 if (image->matte != MagickFalse)
1037 SetPixelAlpha(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001038 break;
1039 }
1040 }
1041 }
cristyed231572011-07-14 02:18:59 +00001042 p+=GetPixelChannels(image);
1043 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +00001044 }
1045 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1046 status=MagickFalse;
1047 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1048 {
1049 MagickBooleanType
1050 proceed;
1051
1052#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00001053 #pragma omp critical (MagickCore_ColorMatrixImage)
cristye6365592010-04-02 17:31:23 +00001054#endif
1055 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1056 image->rows);
1057 if (proceed == MagickFalse)
1058 status=MagickFalse;
1059 }
1060 }
1061 color_view=DestroyCacheView(color_view);
1062 image_view=DestroyCacheView(image_view);
1063 if (status == MagickFalse)
1064 color_image=DestroyImage(color_image);
1065 return(color_image);
1066}
1067
1068/*
1069%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1070% %
1071% %
1072% %
cristy3ed852e2009-09-05 21:47:34 +00001073+ D e s t r o y F x I n f o %
1074% %
1075% %
1076% %
1077%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1078%
1079% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1080%
1081% The format of the DestroyFxInfo method is:
1082%
1083% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1084%
1085% A description of each parameter follows:
1086%
1087% o fx_info: the fx info.
1088%
1089*/
cristy7832dc22011-09-05 01:21:53 +00001090MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
cristy3ed852e2009-09-05 21:47:34 +00001091{
cristybb503372010-05-27 20:51:26 +00001092 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001093 i;
1094
1095 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1096 fx_info->expression=DestroyString(fx_info->expression);
1097 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1098 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001099 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001100 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1101 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001102 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1103 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1104 return(fx_info);
1105}
1106
1107/*
1108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1109% %
1110% %
1111% %
cristy3ed852e2009-09-05 21:47:34 +00001112+ 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 %
1113% %
1114% %
1115% %
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117%
1118% FxEvaluateChannelExpression() evaluates an expression and returns the
1119% results.
1120%
1121% The format of the FxEvaluateExpression method is:
1122%
1123% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001124% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001125% MagickRealType *alpha,Exceptioninfo *exception)
1126% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1127% MagickRealType *alpha,Exceptioninfo *exception)
1128%
1129% A description of each parameter follows:
1130%
1131% o fx_info: the fx info.
1132%
1133% o channel: the channel.
1134%
1135% o x,y: the pixel position.
1136%
1137% o alpha: the result.
1138%
1139% o exception: return any errors or warnings in this structure.
1140%
1141*/
1142
cristy351842f2010-03-07 15:27:38 +00001143static inline double MagickMax(const double x,const double y)
1144{
1145 if (x > y)
1146 return(x);
1147 return(y);
1148}
1149
1150static inline double MagickMin(const double x,const double y)
1151{
1152 if (x < y)
1153 return(x);
1154 return(y);
1155}
1156
cristy3ed852e2009-09-05 21:47:34 +00001157static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001158 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001159{
1160 char
1161 key[MaxTextExtent],
1162 statistic[MaxTextExtent];
1163
1164 const char
1165 *value;
1166
1167 register const char
1168 *p;
1169
1170 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1171 if (*p == '.')
1172 switch (*++p) /* e.g. depth.r */
1173 {
cristy541ae572011-08-05 19:08:59 +00001174 case 'r': channel=RedPixelChannel; break;
1175 case 'g': channel=GreenPixelChannel; break;
1176 case 'b': channel=BluePixelChannel; break;
1177 case 'c': channel=CyanPixelChannel; break;
1178 case 'm': channel=MagentaPixelChannel; break;
1179 case 'y': channel=YellowPixelChannel; break;
1180 case 'k': channel=BlackPixelChannel; break;
cristy3ed852e2009-09-05 21:47:34 +00001181 default: break;
1182 }
cristyb51dff52011-05-19 16:55:47 +00001183 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001184 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001185 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1186 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001187 return(QuantumScale*StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001188 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1189 if (LocaleNCompare(symbol,"depth",5) == 0)
1190 {
cristybb503372010-05-27 20:51:26 +00001191 size_t
cristy3ed852e2009-09-05 21:47:34 +00001192 depth;
1193
cristyfefab1b2011-07-05 00:33:22 +00001194 depth=GetImageDepth(image,exception);
1195 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001196 }
1197 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1198 {
1199 double
1200 kurtosis,
1201 skewness;
1202
cristyd42d9952011-07-08 14:21:50 +00001203 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001204 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001205 }
1206 if (LocaleNCompare(symbol,"maxima",6) == 0)
1207 {
1208 double
1209 maxima,
1210 minima;
1211
cristyd42d9952011-07-08 14:21:50 +00001212 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001213 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001214 }
1215 if (LocaleNCompare(symbol,"mean",4) == 0)
1216 {
1217 double
1218 mean,
1219 standard_deviation;
1220
cristyd42d9952011-07-08 14:21:50 +00001221 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001222 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001223 }
1224 if (LocaleNCompare(symbol,"minima",6) == 0)
1225 {
1226 double
1227 maxima,
1228 minima;
1229
cristyd42d9952011-07-08 14:21:50 +00001230 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001231 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001232 }
1233 if (LocaleNCompare(symbol,"skewness",8) == 0)
1234 {
1235 double
1236 kurtosis,
1237 skewness;
1238
cristyd42d9952011-07-08 14:21:50 +00001239 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001240 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001241 }
1242 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1243 {
1244 double
1245 mean,
1246 standard_deviation;
1247
cristyd42d9952011-07-08 14:21:50 +00001248 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001249 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001250 standard_deviation);
1251 }
1252 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1253 ConstantString(statistic));
cristydbdd0e32011-11-04 23:29:40 +00001254 return(QuantumScale*StringToDouble(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001255}
1256
1257static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001258 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001259 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001260
cristyb0aad4c2011-11-02 19:30:35 +00001261static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
1262{
1263 if (beta != 0)
1264 return(FxGCD(beta,alpha % beta));
1265 return(alpha);
1266}
1267
cristy3ed852e2009-09-05 21:47:34 +00001268static inline const char *FxSubexpression(const char *expression,
1269 ExceptionInfo *exception)
1270{
1271 const char
1272 *subexpression;
1273
cristybb503372010-05-27 20:51:26 +00001274 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001275 level;
1276
1277 level=0;
1278 subexpression=expression;
1279 while ((*subexpression != '\0') &&
1280 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1281 {
1282 if (strchr("(",(int) *subexpression) != (char *) NULL)
1283 level++;
1284 else
1285 if (strchr(")",(int) *subexpression) != (char *) NULL)
1286 level--;
1287 subexpression++;
1288 }
1289 if (*subexpression == '\0')
1290 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1291 "UnbalancedParenthesis","`%s'",expression);
1292 return(subexpression);
1293}
1294
cristy0568ffc2011-07-25 16:54:14 +00001295static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001296 const ssize_t x,const ssize_t y,const char *expression,
1297 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001298{
1299 char
1300 *q,
1301 subexpression[MaxTextExtent],
1302 symbol[MaxTextExtent];
1303
1304 const char
1305 *p,
1306 *value;
1307
1308 Image
1309 *image;
1310
cristy4c08aed2011-07-01 19:47:50 +00001311 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001312 pixel;
1313
1314 MagickRealType
1315 alpha,
1316 beta;
1317
1318 PointInfo
1319 point;
1320
cristybb503372010-05-27 20:51:26 +00001321 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001322 i;
1323
1324 size_t
cristy1707c6c2012-01-18 23:30:54 +00001325 length,
cristy3ed852e2009-09-05 21:47:34 +00001326 level;
1327
1328 p=expression;
1329 i=GetImageIndexInList(fx_info->images);
1330 level=0;
1331 point.x=(double) x;
1332 point.y=(double) y;
1333 if (isalpha((int) *(p+1)) == 0)
1334 {
1335 if (strchr("suv",(int) *p) != (char *) NULL)
1336 {
1337 switch (*p)
1338 {
1339 case 's':
1340 default:
1341 {
1342 i=GetImageIndexInList(fx_info->images);
1343 break;
1344 }
1345 case 'u': i=0; break;
1346 case 'v': i=1; break;
1347 }
1348 p++;
1349 if (*p == '[')
1350 {
1351 level++;
1352 q=subexpression;
1353 for (p++; *p != '\0'; )
1354 {
1355 if (*p == '[')
1356 level++;
1357 else
1358 if (*p == ']')
1359 {
1360 level--;
1361 if (level == 0)
1362 break;
1363 }
1364 *q++=(*p++);
1365 }
1366 *q='\0';
1367 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1368 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001369 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001370 p++;
1371 }
1372 if (*p == '.')
1373 p++;
1374 }
1375 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1376 {
1377 p++;
1378 if (*p == '{')
1379 {
1380 level++;
1381 q=subexpression;
1382 for (p++; *p != '\0'; )
1383 {
1384 if (*p == '{')
1385 level++;
1386 else
1387 if (*p == '}')
1388 {
1389 level--;
1390 if (level == 0)
1391 break;
1392 }
1393 *q++=(*p++);
1394 }
1395 *q='\0';
1396 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1397 &beta,exception);
1398 point.x=alpha;
1399 point.y=beta;
1400 p++;
1401 }
1402 else
1403 if (*p == '[')
1404 {
1405 level++;
1406 q=subexpression;
1407 for (p++; *p != '\0'; )
1408 {
1409 if (*p == '[')
1410 level++;
1411 else
1412 if (*p == ']')
1413 {
1414 level--;
1415 if (level == 0)
1416 break;
1417 }
1418 *q++=(*p++);
1419 }
1420 *q='\0';
1421 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1422 &beta,exception);
1423 point.x+=alpha;
1424 point.y+=beta;
1425 p++;
1426 }
1427 if (*p == '.')
1428 p++;
1429 }
1430 }
1431 length=GetImageListLength(fx_info->images);
1432 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001433 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001434 i%=length;
1435 image=GetImageFromList(fx_info->images,i);
1436 if (image == (Image *) NULL)
1437 {
1438 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1439 "NoSuchImage","`%s'",expression);
1440 return(0.0);
1441 }
cristy4c08aed2011-07-01 19:47:50 +00001442 GetPixelInfo(image,&pixel);
1443 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001444 point.x,point.y,&pixel,exception);
cristy1707c6c2012-01-18 23:30:54 +00001445 if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1446 (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001447 (LocaleCompare(p,"saturation") != 0) &&
1448 (LocaleCompare(p,"lightness") != 0))
1449 {
1450 char
1451 name[MaxTextExtent];
1452
1453 (void) CopyMagickString(name,p,MaxTextExtent);
1454 for (q=name+(strlen(name)-1); q > name; q--)
1455 {
1456 if (*q == ')')
1457 break;
1458 if (*q == '.')
1459 {
1460 *q='\0';
1461 break;
1462 }
1463 }
1464 if ((strlen(name) > 2) &&
1465 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1466 {
cristy4c08aed2011-07-01 19:47:50 +00001467 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001468 *color;
1469
cristy4c08aed2011-07-01 19:47:50 +00001470 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1471 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001472 {
1473 pixel=(*color);
1474 p+=strlen(name);
1475 }
1476 else
cristy1707c6c2012-01-18 23:30:54 +00001477 {
1478 MagickBooleanType
1479 status;
1480
1481 status=QueryColorCompliance(name,AllCompliance,&pixel,
1482 fx_info->exception);
1483 if (status != MagickFalse)
1484 {
1485 (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1486 name),ClonePixelInfo(&pixel));
1487 p+=strlen(name);
1488 }
1489 }
cristy3ed852e2009-09-05 21:47:34 +00001490 }
1491 }
1492 (void) CopyMagickString(symbol,p,MaxTextExtent);
1493 StripString(symbol);
1494 if (*symbol == '\0')
1495 {
1496 switch (channel)
1497 {
cristy0568ffc2011-07-25 16:54:14 +00001498 case RedPixelChannel: return(QuantumScale*pixel.red);
1499 case GreenPixelChannel: return(QuantumScale*pixel.green);
1500 case BluePixelChannel: return(QuantumScale*pixel.blue);
1501 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001502 {
1503 if (image->colorspace != CMYKColorspace)
1504 {
1505 (void) ThrowMagickException(exception,GetMagickModule(),
cristy1a020e42011-12-06 18:13:23 +00001506 ImageError,"ColorSeparatedImageRequired","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00001507 image->filename);
1508 return(0.0);
1509 }
cristy4c08aed2011-07-01 19:47:50 +00001510 return(QuantumScale*pixel.black);
1511 }
cristy0568ffc2011-07-25 16:54:14 +00001512 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001513 {
1514 MagickRealType
1515 alpha;
1516
1517 if (pixel.matte == MagickFalse)
1518 return(1.0);
1519 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1520 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001521 }
cristya382aca2011-12-06 18:22:48 +00001522 case IndexPixelChannel:
1523 return(0.0);
cristyb3a73b52011-07-26 01:34:43 +00001524 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001525 {
cristy4c08aed2011-07-01 19:47:50 +00001526 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001527 }
cristy3ed852e2009-09-05 21:47:34 +00001528 default:
1529 break;
1530 }
1531 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1532 "UnableToParseExpression","`%s'",p);
1533 return(0.0);
1534 }
1535 switch (*symbol)
1536 {
1537 case 'A':
1538 case 'a':
1539 {
1540 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001541 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001542 break;
1543 }
1544 case 'B':
1545 case 'b':
1546 {
1547 if (LocaleCompare(symbol,"b") == 0)
1548 return(QuantumScale*pixel.blue);
1549 break;
1550 }
1551 case 'C':
1552 case 'c':
1553 {
1554 if (LocaleNCompare(symbol,"channel",7) == 0)
1555 {
1556 GeometryInfo
1557 channel_info;
1558
1559 MagickStatusType
1560 flags;
1561
1562 flags=ParseGeometry(symbol+7,&channel_info);
1563 if (image->colorspace == CMYKColorspace)
1564 switch (channel)
1565 {
cristy0568ffc2011-07-25 16:54:14 +00001566 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001567 {
1568 if ((flags & RhoValue) == 0)
1569 return(0.0);
1570 return(channel_info.rho);
1571 }
cristy0568ffc2011-07-25 16:54:14 +00001572 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001573 {
1574 if ((flags & SigmaValue) == 0)
1575 return(0.0);
1576 return(channel_info.sigma);
1577 }
cristy0568ffc2011-07-25 16:54:14 +00001578 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001579 {
1580 if ((flags & XiValue) == 0)
1581 return(0.0);
1582 return(channel_info.xi);
1583 }
cristy0568ffc2011-07-25 16:54:14 +00001584 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001585 {
1586 if ((flags & PsiValue) == 0)
1587 return(0.0);
1588 return(channel_info.psi);
1589 }
cristy0568ffc2011-07-25 16:54:14 +00001590 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001591 {
1592 if ((flags & ChiValue) == 0)
1593 return(0.0);
1594 return(channel_info.chi);
1595 }
1596 default:
1597 return(0.0);
1598 }
1599 switch (channel)
1600 {
cristy0568ffc2011-07-25 16:54:14 +00001601 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001602 {
1603 if ((flags & RhoValue) == 0)
1604 return(0.0);
1605 return(channel_info.rho);
1606 }
cristy0568ffc2011-07-25 16:54:14 +00001607 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001608 {
1609 if ((flags & SigmaValue) == 0)
1610 return(0.0);
1611 return(channel_info.sigma);
1612 }
cristy0568ffc2011-07-25 16:54:14 +00001613 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001614 {
1615 if ((flags & XiValue) == 0)
1616 return(0.0);
1617 return(channel_info.xi);
1618 }
cristy0568ffc2011-07-25 16:54:14 +00001619 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001620 {
1621 if ((flags & ChiValue) == 0)
1622 return(0.0);
1623 return(channel_info.chi);
1624 }
cristy0568ffc2011-07-25 16:54:14 +00001625 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001626 {
1627 if ((flags & PsiValue) == 0)
1628 return(0.0);
1629 return(channel_info.psi);
1630 }
cristy3ed852e2009-09-05 21:47:34 +00001631 default:
1632 return(0.0);
1633 }
1634 return(0.0);
1635 }
1636 if (LocaleCompare(symbol,"c") == 0)
1637 return(QuantumScale*pixel.red);
1638 break;
1639 }
1640 case 'D':
1641 case 'd':
1642 {
1643 if (LocaleNCompare(symbol,"depth",5) == 0)
1644 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1645 break;
1646 }
1647 case 'G':
1648 case 'g':
1649 {
1650 if (LocaleCompare(symbol,"g") == 0)
1651 return(QuantumScale*pixel.green);
1652 break;
1653 }
1654 case 'K':
1655 case 'k':
1656 {
1657 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1658 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1659 if (LocaleCompare(symbol,"k") == 0)
1660 {
1661 if (image->colorspace != CMYKColorspace)
1662 {
1663 (void) ThrowMagickException(exception,GetMagickModule(),
1664 OptionError,"ColorSeparatedImageRequired","`%s'",
1665 image->filename);
1666 return(0.0);
1667 }
cristy4c08aed2011-07-01 19:47:50 +00001668 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001669 }
1670 break;
1671 }
1672 case 'H':
1673 case 'h':
1674 {
1675 if (LocaleCompare(symbol,"h") == 0)
1676 return((MagickRealType) image->rows);
1677 if (LocaleCompare(symbol,"hue") == 0)
1678 {
1679 double
1680 hue,
1681 lightness,
1682 saturation;
1683
cristyda1f9c12011-10-02 21:39:49 +00001684 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1685 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001686 return(hue);
1687 }
1688 break;
1689 }
1690 case 'I':
1691 case 'i':
1692 {
1693 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1694 (LocaleCompare(symbol,"image.minima") == 0) ||
1695 (LocaleCompare(symbol,"image.maxima") == 0) ||
1696 (LocaleCompare(symbol,"image.mean") == 0) ||
1697 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1698 (LocaleCompare(symbol,"image.skewness") == 0) ||
1699 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1700 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1701 if (LocaleCompare(symbol,"image.resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001702 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001703 if (LocaleCompare(symbol,"image.resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001704 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001705 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001706 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001707 if (LocaleCompare(symbol,"i") == 0)
1708 return((MagickRealType) x);
1709 break;
1710 }
1711 case 'J':
1712 case 'j':
1713 {
1714 if (LocaleCompare(symbol,"j") == 0)
1715 return((MagickRealType) y);
1716 break;
1717 }
1718 case 'L':
1719 case 'l':
1720 {
1721 if (LocaleCompare(symbol,"lightness") == 0)
1722 {
1723 double
1724 hue,
1725 lightness,
1726 saturation;
1727
cristyda1f9c12011-10-02 21:39:49 +00001728 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1729 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001730 return(lightness);
1731 }
1732 if (LocaleCompare(symbol,"luminance") == 0)
1733 {
1734 double
1735 luminence;
1736
1737 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1738 return(QuantumScale*luminence);
1739 }
1740 break;
1741 }
1742 case 'M':
1743 case 'm':
1744 {
1745 if (LocaleNCompare(symbol,"maxima",6) == 0)
1746 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1747 if (LocaleNCompare(symbol,"mean",4) == 0)
1748 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1749 if (LocaleNCompare(symbol,"minima",6) == 0)
1750 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1751 if (LocaleCompare(symbol,"m") == 0)
1752 return(QuantumScale*pixel.blue);
1753 break;
1754 }
1755 case 'N':
1756 case 'n':
1757 {
1758 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001759 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001760 break;
1761 }
1762 case 'O':
1763 case 'o':
1764 {
1765 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001766 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001767 break;
1768 }
1769 case 'P':
1770 case 'p':
1771 {
1772 if (LocaleCompare(symbol,"page.height") == 0)
1773 return((MagickRealType) image->page.height);
1774 if (LocaleCompare(symbol,"page.width") == 0)
1775 return((MagickRealType) image->page.width);
1776 if (LocaleCompare(symbol,"page.x") == 0)
1777 return((MagickRealType) image->page.x);
1778 if (LocaleCompare(symbol,"page.y") == 0)
1779 return((MagickRealType) image->page.y);
1780 break;
1781 }
1782 case 'R':
1783 case 'r':
1784 {
1785 if (LocaleCompare(symbol,"resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001786 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001787 if (LocaleCompare(symbol,"resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001788 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001789 if (LocaleCompare(symbol,"r") == 0)
1790 return(QuantumScale*pixel.red);
1791 break;
1792 }
1793 case 'S':
1794 case 's':
1795 {
1796 if (LocaleCompare(symbol,"saturation") == 0)
1797 {
1798 double
1799 hue,
1800 lightness,
1801 saturation;
1802
cristyda1f9c12011-10-02 21:39:49 +00001803 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1804 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001805 return(saturation);
1806 }
1807 if (LocaleNCompare(symbol,"skewness",8) == 0)
1808 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1809 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1810 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1811 break;
1812 }
1813 case 'T':
1814 case 't':
1815 {
1816 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001817 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001818 break;
1819 }
1820 case 'W':
1821 case 'w':
1822 {
1823 if (LocaleCompare(symbol,"w") == 0)
1824 return((MagickRealType) image->columns);
1825 break;
1826 }
1827 case 'Y':
1828 case 'y':
1829 {
1830 if (LocaleCompare(symbol,"y") == 0)
1831 return(QuantumScale*pixel.green);
1832 break;
1833 }
1834 case 'Z':
1835 case 'z':
1836 {
1837 if (LocaleCompare(symbol,"z") == 0)
1838 {
1839 MagickRealType
1840 depth;
1841
cristyfefab1b2011-07-05 00:33:22 +00001842 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001843 return(depth);
1844 }
1845 break;
1846 }
1847 default:
1848 break;
1849 }
1850 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1851 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001852 return((MagickRealType) StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001853 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1854 "UnableToParseExpression","`%s'",symbol);
1855 return(0.0);
1856}
1857
1858static const char *FxOperatorPrecedence(const char *expression,
1859 ExceptionInfo *exception)
1860{
1861 typedef enum
1862 {
1863 UndefinedPrecedence,
1864 NullPrecedence,
1865 BitwiseComplementPrecedence,
1866 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001867 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001868 MultiplyPrecedence,
1869 AdditionPrecedence,
1870 ShiftPrecedence,
1871 RelationalPrecedence,
1872 EquivalencyPrecedence,
1873 BitwiseAndPrecedence,
1874 BitwiseOrPrecedence,
1875 LogicalAndPrecedence,
1876 LogicalOrPrecedence,
1877 TernaryPrecedence,
1878 AssignmentPrecedence,
1879 CommaPrecedence,
1880 SeparatorPrecedence
1881 } FxPrecedence;
1882
1883 FxPrecedence
1884 precedence,
1885 target;
1886
1887 register const char
1888 *subexpression;
1889
1890 register int
1891 c;
1892
cristybb503372010-05-27 20:51:26 +00001893 size_t
cristy3ed852e2009-09-05 21:47:34 +00001894 level;
1895
1896 c=0;
1897 level=0;
1898 subexpression=(const char *) NULL;
1899 target=NullPrecedence;
1900 while (*expression != '\0')
1901 {
1902 precedence=UndefinedPrecedence;
1903 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1904 {
1905 expression++;
1906 continue;
1907 }
cristy488fa882010-03-01 22:34:24 +00001908 switch (*expression)
1909 {
1910 case 'A':
1911 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001912 {
cristyb33454f2011-08-03 02:10:45 +00001913#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001914 if (LocaleNCompare(expression,"acosh",5) == 0)
1915 {
1916 expression+=5;
1917 break;
1918 }
cristyb33454f2011-08-03 02:10:45 +00001919#endif
1920#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001921 if (LocaleNCompare(expression,"asinh",5) == 0)
1922 {
1923 expression+=5;
1924 break;
1925 }
cristyb33454f2011-08-03 02:10:45 +00001926#endif
1927#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001928 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001929 {
1930 expression+=5;
1931 break;
1932 }
cristyb33454f2011-08-03 02:10:45 +00001933#endif
cristy488fa882010-03-01 22:34:24 +00001934 break;
cristy3ed852e2009-09-05 21:47:34 +00001935 }
cristy62d455f2011-11-03 11:42:28 +00001936 case 'E':
1937 case 'e':
1938 {
1939 if ((LocaleNCompare(expression,"E+",2) == 0) ||
1940 (LocaleNCompare(expression,"E-",2) == 0))
1941 {
1942 expression+=2; /* scientific notation */
1943 break;
1944 }
1945 }
cristy488fa882010-03-01 22:34:24 +00001946 case 'J':
1947 case 'j':
1948 {
1949 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1950 (LocaleNCompare(expression,"j1",2) == 0))
1951 {
1952 expression+=2;
1953 break;
1954 }
1955 break;
1956 }
cristy2def9322010-06-18 23:59:37 +00001957 case '#':
1958 {
1959 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1960 expression++;
1961 break;
1962 }
cristy488fa882010-03-01 22:34:24 +00001963 default:
1964 break;
1965 }
cristy3ed852e2009-09-05 21:47:34 +00001966 if ((c == (int) '{') || (c == (int) '['))
1967 level++;
1968 else
1969 if ((c == (int) '}') || (c == (int) ']'))
1970 level--;
1971 if (level == 0)
1972 switch ((unsigned char) *expression)
1973 {
1974 case '~':
1975 case '!':
1976 {
1977 precedence=BitwiseComplementPrecedence;
1978 break;
1979 }
1980 case '^':
cristy6621e252010-08-13 00:42:57 +00001981 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001982 {
1983 precedence=ExponentPrecedence;
1984 break;
1985 }
1986 default:
1987 {
1988 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1989 (strchr(")",c) != (char *) NULL))) &&
1990 (((islower((int) ((char) *expression)) != 0) ||
1991 (strchr("(",(int) *expression) != (char *) NULL)) ||
1992 ((isdigit((int) ((char) c)) == 0) &&
1993 (isdigit((int) ((char) *expression)) != 0))) &&
1994 (strchr("xy",(int) *expression) == (char *) NULL))
1995 precedence=MultiplyPrecedence;
1996 break;
1997 }
1998 case '*':
1999 case '/':
2000 case '%':
2001 {
2002 precedence=MultiplyPrecedence;
2003 break;
2004 }
2005 case '+':
2006 case '-':
2007 {
2008 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
2009 (isalpha(c) != 0))
2010 precedence=AdditionPrecedence;
2011 break;
2012 }
2013 case LeftShiftOperator:
2014 case RightShiftOperator:
2015 {
2016 precedence=ShiftPrecedence;
2017 break;
2018 }
2019 case '<':
2020 case LessThanEqualOperator:
2021 case GreaterThanEqualOperator:
2022 case '>':
2023 {
2024 precedence=RelationalPrecedence;
2025 break;
2026 }
2027 case EqualOperator:
2028 case NotEqualOperator:
2029 {
2030 precedence=EquivalencyPrecedence;
2031 break;
2032 }
2033 case '&':
2034 {
2035 precedence=BitwiseAndPrecedence;
2036 break;
2037 }
2038 case '|':
2039 {
2040 precedence=BitwiseOrPrecedence;
2041 break;
2042 }
2043 case LogicalAndOperator:
2044 {
2045 precedence=LogicalAndPrecedence;
2046 break;
2047 }
2048 case LogicalOrOperator:
2049 {
2050 precedence=LogicalOrPrecedence;
2051 break;
2052 }
cristy116af162010-08-13 01:25:47 +00002053 case ExponentialNotation:
2054 {
2055 precedence=ExponentialNotationPrecedence;
2056 break;
2057 }
cristy3ed852e2009-09-05 21:47:34 +00002058 case ':':
2059 case '?':
2060 {
2061 precedence=TernaryPrecedence;
2062 break;
2063 }
2064 case '=':
2065 {
2066 precedence=AssignmentPrecedence;
2067 break;
2068 }
2069 case ',':
2070 {
2071 precedence=CommaPrecedence;
2072 break;
2073 }
2074 case ';':
2075 {
2076 precedence=SeparatorPrecedence;
2077 break;
2078 }
2079 }
2080 if ((precedence == BitwiseComplementPrecedence) ||
2081 (precedence == TernaryPrecedence) ||
2082 (precedence == AssignmentPrecedence))
2083 {
2084 if (precedence > target)
2085 {
2086 /*
2087 Right-to-left associativity.
2088 */
2089 target=precedence;
2090 subexpression=expression;
2091 }
2092 }
2093 else
2094 if (precedence >= target)
2095 {
2096 /*
2097 Left-to-right associativity.
2098 */
2099 target=precedence;
2100 subexpression=expression;
2101 }
2102 if (strchr("(",(int) *expression) != (char *) NULL)
2103 expression=FxSubexpression(expression,exception);
2104 c=(int) (*expression++);
2105 }
2106 return(subexpression);
2107}
2108
2109static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002110 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002111 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002112{
2113 char
2114 *q,
2115 subexpression[MaxTextExtent];
2116
2117 MagickRealType
2118 alpha,
2119 gamma;
2120
2121 register const char
2122 *p;
2123
2124 *beta=0.0;
2125 if (exception->severity != UndefinedException)
2126 return(0.0);
2127 while (isspace((int) *expression) != 0)
2128 expression++;
2129 if (*expression == '\0')
2130 {
2131 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2132 "MissingExpression","`%s'",expression);
2133 return(0.0);
2134 }
cristy66322f02010-05-17 11:40:48 +00002135 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002136 p=FxOperatorPrecedence(expression,exception);
2137 if (p != (const char *) NULL)
2138 {
2139 (void) CopyMagickString(subexpression,expression,(size_t)
2140 (p-expression+1));
2141 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2142 exception);
2143 switch ((unsigned char) *p)
2144 {
2145 case '~':
2146 {
2147 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002148 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002149 return(*beta);
2150 }
2151 case '!':
2152 {
2153 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2154 return(*beta == 0.0 ? 1.0 : 0.0);
2155 }
2156 case '^':
2157 {
2158 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2159 channel,x,y,++p,beta,exception));
2160 return(*beta);
2161 }
2162 case '*':
cristy116af162010-08-13 01:25:47 +00002163 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002164 {
2165 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2166 return(alpha*(*beta));
2167 }
2168 case '/':
2169 {
2170 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2171 if (*beta == 0.0)
2172 {
2173 if (exception->severity == UndefinedException)
2174 (void) ThrowMagickException(exception,GetMagickModule(),
2175 OptionError,"DivideByZero","`%s'",expression);
2176 return(0.0);
2177 }
2178 return(alpha/(*beta));
2179 }
2180 case '%':
2181 {
2182 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2183 *beta=fabs(floor(((double) *beta)+0.5));
2184 if (*beta == 0.0)
2185 {
2186 (void) ThrowMagickException(exception,GetMagickModule(),
2187 OptionError,"DivideByZero","`%s'",expression);
2188 return(0.0);
2189 }
2190 return(fmod((double) alpha,(double) *beta));
2191 }
2192 case '+':
2193 {
2194 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2195 return(alpha+(*beta));
2196 }
2197 case '-':
2198 {
2199 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2200 return(alpha-(*beta));
2201 }
2202 case LeftShiftOperator:
2203 {
2204 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002205 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002206 return(*beta);
2207 }
2208 case RightShiftOperator:
2209 {
2210 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002211 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002212 return(*beta);
2213 }
2214 case '<':
2215 {
2216 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2217 return(alpha < *beta ? 1.0 : 0.0);
2218 }
2219 case LessThanEqualOperator:
2220 {
2221 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2222 return(alpha <= *beta ? 1.0 : 0.0);
2223 }
2224 case '>':
2225 {
2226 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2227 return(alpha > *beta ? 1.0 : 0.0);
2228 }
2229 case GreaterThanEqualOperator:
2230 {
2231 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2232 return(alpha >= *beta ? 1.0 : 0.0);
2233 }
2234 case EqualOperator:
2235 {
2236 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2237 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2238 }
2239 case NotEqualOperator:
2240 {
2241 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2242 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2243 }
2244 case '&':
2245 {
2246 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002247 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002248 return(*beta);
2249 }
2250 case '|':
2251 {
2252 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002253 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002254 return(*beta);
2255 }
2256 case LogicalAndOperator:
2257 {
2258 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2259 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2260 return(*beta);
2261 }
2262 case LogicalOrOperator:
2263 {
2264 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2265 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2266 return(*beta);
2267 }
2268 case '?':
2269 {
2270 MagickRealType
2271 gamma;
2272
2273 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2274 q=subexpression;
2275 p=StringToken(":",&q);
2276 if (q == (char *) NULL)
2277 {
2278 (void) ThrowMagickException(exception,GetMagickModule(),
2279 OptionError,"UnableToParseExpression","`%s'",subexpression);
2280 return(0.0);
2281 }
2282 if (fabs((double) alpha) > MagickEpsilon)
2283 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2284 else
2285 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2286 return(gamma);
2287 }
2288 case '=':
2289 {
2290 char
2291 numeric[MaxTextExtent];
2292
2293 q=subexpression;
2294 while (isalpha((int) ((unsigned char) *q)) != 0)
2295 q++;
2296 if (*q != '\0')
2297 {
2298 (void) ThrowMagickException(exception,GetMagickModule(),
2299 OptionError,"UnableToParseExpression","`%s'",subexpression);
2300 return(0.0);
2301 }
2302 ClearMagickException(exception);
2303 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002304 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002305 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002306 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2307 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2308 subexpression),ConstantString(numeric));
2309 return(*beta);
2310 }
2311 case ',':
2312 {
2313 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2314 return(alpha);
2315 }
2316 case ';':
2317 {
2318 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2319 return(*beta);
2320 }
2321 default:
2322 {
2323 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2324 exception);
2325 return(gamma);
2326 }
2327 }
2328 }
2329 if (strchr("(",(int) *expression) != (char *) NULL)
2330 {
2331 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2332 subexpression[strlen(subexpression)-1]='\0';
2333 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2334 exception);
2335 return(gamma);
2336 }
cristy8b8a3ae2010-10-23 18:49:46 +00002337 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002338 {
2339 case '+':
2340 {
2341 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2342 exception);
2343 return(1.0*gamma);
2344 }
2345 case '-':
2346 {
2347 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2348 exception);
2349 return(-1.0*gamma);
2350 }
2351 case '~':
2352 {
2353 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2354 exception);
cristybb503372010-05-27 20:51:26 +00002355 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002356 }
2357 case 'A':
2358 case 'a':
2359 {
2360 if (LocaleNCompare(expression,"abs",3) == 0)
2361 {
2362 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2363 exception);
2364 return((MagickRealType) fabs((double) alpha));
2365 }
cristyb33454f2011-08-03 02:10:45 +00002366#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002367 if (LocaleNCompare(expression,"acosh",5) == 0)
2368 {
2369 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2370 exception);
2371 return((MagickRealType) acosh((double) alpha));
2372 }
cristyb33454f2011-08-03 02:10:45 +00002373#endif
cristy3ed852e2009-09-05 21:47:34 +00002374 if (LocaleNCompare(expression,"acos",4) == 0)
2375 {
2376 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2377 exception);
2378 return((MagickRealType) acos((double) alpha));
2379 }
cristy43c22f42010-03-30 12:34:07 +00002380#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002381 if (LocaleNCompare(expression,"airy",4) == 0)
2382 {
2383 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2384 exception);
2385 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002386 return(1.0);
2387 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002388 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002389 }
cristy43c22f42010-03-30 12:34:07 +00002390#endif
cristyb33454f2011-08-03 02:10:45 +00002391#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002392 if (LocaleNCompare(expression,"asinh",5) == 0)
2393 {
2394 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2395 exception);
2396 return((MagickRealType) asinh((double) alpha));
2397 }
cristyb33454f2011-08-03 02:10:45 +00002398#endif
cristy3ed852e2009-09-05 21:47:34 +00002399 if (LocaleNCompare(expression,"asin",4) == 0)
2400 {
2401 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2402 exception);
2403 return((MagickRealType) asin((double) alpha));
2404 }
2405 if (LocaleNCompare(expression,"alt",3) == 0)
2406 {
2407 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2408 exception);
cristybb503372010-05-27 20:51:26 +00002409 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002410 }
2411 if (LocaleNCompare(expression,"atan2",5) == 0)
2412 {
2413 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2414 exception);
2415 return((MagickRealType) atan2((double) alpha,(double) *beta));
2416 }
cristyb33454f2011-08-03 02:10:45 +00002417#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002418 if (LocaleNCompare(expression,"atanh",5) == 0)
2419 {
2420 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2421 exception);
2422 return((MagickRealType) atanh((double) alpha));
2423 }
cristyb33454f2011-08-03 02:10:45 +00002424#endif
cristy3ed852e2009-09-05 21:47:34 +00002425 if (LocaleNCompare(expression,"atan",4) == 0)
2426 {
2427 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2428 exception);
2429 return((MagickRealType) atan((double) alpha));
2430 }
2431 if (LocaleCompare(expression,"a") == 0)
2432 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2433 break;
2434 }
2435 case 'B':
2436 case 'b':
2437 {
2438 if (LocaleCompare(expression,"b") == 0)
2439 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2440 break;
2441 }
2442 case 'C':
2443 case 'c':
2444 {
2445 if (LocaleNCompare(expression,"ceil",4) == 0)
2446 {
2447 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2448 exception);
2449 return((MagickRealType) ceil((double) alpha));
2450 }
2451 if (LocaleNCompare(expression,"cosh",4) == 0)
2452 {
2453 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2454 exception);
2455 return((MagickRealType) cosh((double) alpha));
2456 }
2457 if (LocaleNCompare(expression,"cos",3) == 0)
2458 {
2459 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2460 exception);
2461 return((MagickRealType) cos((double) alpha));
2462 }
2463 if (LocaleCompare(expression,"c") == 0)
2464 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2465 break;
2466 }
2467 case 'D':
2468 case 'd':
2469 {
2470 if (LocaleNCompare(expression,"debug",5) == 0)
2471 {
2472 const char
2473 *type;
2474
2475 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2476 exception);
2477 if (fx_info->images->colorspace == CMYKColorspace)
2478 switch (channel)
2479 {
cristy0568ffc2011-07-25 16:54:14 +00002480 case CyanPixelChannel: type="cyan"; break;
2481 case MagentaPixelChannel: type="magenta"; break;
2482 case YellowPixelChannel: type="yellow"; break;
2483 case AlphaPixelChannel: type="opacity"; break;
2484 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002485 default: type="unknown"; break;
2486 }
2487 else
2488 switch (channel)
2489 {
cristy0568ffc2011-07-25 16:54:14 +00002490 case RedPixelChannel: type="red"; break;
2491 case GreenPixelChannel: type="green"; break;
2492 case BluePixelChannel: type="blue"; break;
2493 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002494 default: type="unknown"; break;
2495 }
2496 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2497 if (strlen(subexpression) > 1)
2498 subexpression[strlen(subexpression)-1]='\0';
2499 if (fx_info->file != (FILE *) NULL)
cristy1707c6c2012-01-18 23:30:54 +00002500 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2501 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2502 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002503 return(0.0);
2504 }
cristy5597a8d2011-11-04 00:25:32 +00002505 if (LocaleNCompare(expression,"drc",3) == 0)
2506 {
2507 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2508 exception);
2509 return((MagickRealType) (alpha/(*beta*(alpha-1.0)+1.0)));
2510 }
cristy3ed852e2009-09-05 21:47:34 +00002511 break;
2512 }
2513 case 'E':
2514 case 'e':
2515 {
2516 if (LocaleCompare(expression,"epsilon") == 0)
2517 return((MagickRealType) MagickEpsilon);
2518 if (LocaleNCompare(expression,"exp",3) == 0)
2519 {
2520 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2521 exception);
2522 return((MagickRealType) exp((double) alpha));
2523 }
2524 if (LocaleCompare(expression,"e") == 0)
2525 return((MagickRealType) 2.7182818284590452354);
2526 break;
2527 }
2528 case 'F':
2529 case 'f':
2530 {
2531 if (LocaleNCompare(expression,"floor",5) == 0)
2532 {
2533 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2534 exception);
2535 return((MagickRealType) floor((double) alpha));
2536 }
2537 break;
2538 }
2539 case 'G':
2540 case 'g':
2541 {
cristy9eeedea2011-11-02 19:04:05 +00002542 if (LocaleNCompare(expression,"gauss",5) == 0)
2543 {
2544 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2545 exception);
2546 gamma=exp((double) (-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2547 return((MagickRealType) gamma);
2548 }
cristyb0aad4c2011-11-02 19:30:35 +00002549 if (LocaleNCompare(expression,"gcd",3) == 0)
2550 {
2551 MagickOffsetType
2552 gcd;
2553
2554 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2555 exception);
cristy1707c6c2012-01-18 23:30:54 +00002556 gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2557 0.5));
cristyb0aad4c2011-11-02 19:30:35 +00002558 return((MagickRealType) gcd);
2559 }
cristy3ed852e2009-09-05 21:47:34 +00002560 if (LocaleCompare(expression,"g") == 0)
2561 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2562 break;
2563 }
2564 case 'H':
2565 case 'h':
2566 {
2567 if (LocaleCompare(expression,"h") == 0)
2568 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2569 if (LocaleCompare(expression,"hue") == 0)
2570 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2571 if (LocaleNCompare(expression,"hypot",5) == 0)
2572 {
2573 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2574 exception);
2575 return((MagickRealType) hypot((double) alpha,(double) *beta));
2576 }
2577 break;
2578 }
2579 case 'K':
2580 case 'k':
2581 {
2582 if (LocaleCompare(expression,"k") == 0)
2583 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2584 break;
2585 }
2586 case 'I':
2587 case 'i':
2588 {
2589 if (LocaleCompare(expression,"intensity") == 0)
2590 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2591 if (LocaleNCompare(expression,"int",3) == 0)
2592 {
2593 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2594 exception);
cristy16788e42010-08-13 13:44:26 +00002595 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002596 }
cristy82b20722011-11-05 21:52:36 +00002597#if defined(MAGICKCORE_HAVE_ISNAN)
cristy639399c2011-11-02 19:16:15 +00002598 if (LocaleNCompare(expression,"isnan",5) == 0)
2599 {
2600 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2601 exception);
cristy17a10202011-11-02 19:17:04 +00002602 return((MagickRealType) !!isnan((double) alpha));
cristy639399c2011-11-02 19:16:15 +00002603 }
cristy82b20722011-11-05 21:52:36 +00002604#endif
cristy3ed852e2009-09-05 21:47:34 +00002605 if (LocaleCompare(expression,"i") == 0)
2606 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2607 break;
2608 }
2609 case 'J':
2610 case 'j':
2611 {
2612 if (LocaleCompare(expression,"j") == 0)
2613 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002614#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002615 if (LocaleNCompare(expression,"j0",2) == 0)
2616 {
2617 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2618 exception);
2619 return((MagickRealType) j0((double) alpha));
2620 }
cristy161b9262010-03-20 19:34:32 +00002621#endif
2622#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002623 if (LocaleNCompare(expression,"j1",2) == 0)
2624 {
2625 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2626 exception);
2627 return((MagickRealType) j1((double) alpha));
2628 }
cristy161b9262010-03-20 19:34:32 +00002629#endif
cristyaa018fa2010-04-08 23:03:54 +00002630#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002631 if (LocaleNCompare(expression,"jinc",4) == 0)
2632 {
2633 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2634 exception);
cristy0946a822010-03-12 17:14:58 +00002635 if (alpha == 0.0)
2636 return(1.0);
cristy1707c6c2012-01-18 23:30:54 +00002637 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/(MagickPI*
2638 alpha));
cristyfce2f7b2010-03-12 00:29:49 +00002639 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002640 }
cristyaa018fa2010-04-08 23:03:54 +00002641#endif
cristy3ed852e2009-09-05 21:47:34 +00002642 break;
2643 }
2644 case 'L':
2645 case 'l':
2646 {
2647 if (LocaleNCompare(expression,"ln",2) == 0)
2648 {
2649 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2650 exception);
2651 return((MagickRealType) log((double) alpha));
2652 }
cristyc8ed5322010-08-31 12:07:59 +00002653 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002654 {
cristyc8ed5322010-08-31 12:07:59 +00002655 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002656 exception);
2657 return((MagickRealType) log10((double) alpha))/log10(2.0);
2658 }
2659 if (LocaleNCompare(expression,"log",3) == 0)
2660 {
2661 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2662 exception);
2663 return((MagickRealType) log10((double) alpha));
2664 }
2665 if (LocaleCompare(expression,"lightness") == 0)
2666 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2667 break;
2668 }
2669 case 'M':
2670 case 'm':
2671 {
2672 if (LocaleCompare(expression,"MaxRGB") == 0)
2673 return((MagickRealType) QuantumRange);
2674 if (LocaleNCompare(expression,"maxima",6) == 0)
2675 break;
2676 if (LocaleNCompare(expression,"max",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002677 {
2678 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2679 exception);
2680 return(alpha > *beta ? alpha : *beta);
2681 }
cristy3ed852e2009-09-05 21:47:34 +00002682 if (LocaleNCompare(expression,"minima",6) == 0)
2683 break;
2684 if (LocaleNCompare(expression,"min",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002685 {
2686 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2687 exception);
2688 return(alpha < *beta ? alpha : *beta);
2689 }
cristy3ed852e2009-09-05 21:47:34 +00002690 if (LocaleNCompare(expression,"mod",3) == 0)
2691 {
2692 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2693 exception);
cristy984049c2011-11-03 18:34:58 +00002694 gamma=alpha-floor((double) (alpha/(*beta)))*(*beta);
2695 return(gamma);
cristy3ed852e2009-09-05 21:47:34 +00002696 }
2697 if (LocaleCompare(expression,"m") == 0)
2698 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2699 break;
2700 }
2701 case 'N':
2702 case 'n':
2703 {
cristyad3502e2011-11-02 19:10:45 +00002704 if (LocaleNCompare(expression,"not",3) == 0)
2705 {
2706 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2707 exception);
2708 return((MagickRealType) (alpha < MagickEpsilon));
2709 }
cristy3ed852e2009-09-05 21:47:34 +00002710 if (LocaleCompare(expression,"n") == 0)
2711 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2712 break;
2713 }
2714 case 'O':
2715 case 'o':
2716 {
2717 if (LocaleCompare(expression,"Opaque") == 0)
2718 return(1.0);
2719 if (LocaleCompare(expression,"o") == 0)
2720 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2721 break;
2722 }
2723 case 'P':
2724 case 'p':
2725 {
cristy670aa3c2011-11-03 00:54:00 +00002726 if (LocaleCompare(expression,"phi") == 0)
2727 return((MagickRealType) MagickPHI);
cristy3ed852e2009-09-05 21:47:34 +00002728 if (LocaleCompare(expression,"pi") == 0)
2729 return((MagickRealType) MagickPI);
2730 if (LocaleNCompare(expression,"pow",3) == 0)
2731 {
2732 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2733 exception);
2734 return((MagickRealType) pow((double) alpha,(double) *beta));
2735 }
2736 if (LocaleCompare(expression,"p") == 0)
2737 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2738 break;
2739 }
2740 case 'Q':
2741 case 'q':
2742 {
2743 if (LocaleCompare(expression,"QuantumRange") == 0)
2744 return((MagickRealType) QuantumRange);
2745 if (LocaleCompare(expression,"QuantumScale") == 0)
2746 return((MagickRealType) QuantumScale);
2747 break;
2748 }
2749 case 'R':
2750 case 'r':
2751 {
2752 if (LocaleNCompare(expression,"rand",4) == 0)
2753 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2754 if (LocaleNCompare(expression,"round",5) == 0)
2755 {
2756 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2757 exception);
cristy16788e42010-08-13 13:44:26 +00002758 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002759 }
2760 if (LocaleCompare(expression,"r") == 0)
2761 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2762 break;
2763 }
2764 case 'S':
2765 case 's':
2766 {
2767 if (LocaleCompare(expression,"saturation") == 0)
2768 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2769 if (LocaleNCompare(expression,"sign",4) == 0)
2770 {
2771 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2772 exception);
2773 return(alpha < 0.0 ? -1.0 : 1.0);
2774 }
cristya6a09e72010-03-02 14:51:02 +00002775 if (LocaleNCompare(expression,"sinc",4) == 0)
2776 {
2777 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2778 exception);
2779 if (alpha == 0)
2780 return(1.0);
2781 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2782 (MagickPI*alpha));
2783 return(gamma);
2784 }
cristy3ed852e2009-09-05 21:47:34 +00002785 if (LocaleNCompare(expression,"sinh",4) == 0)
2786 {
2787 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2788 exception);
2789 return((MagickRealType) sinh((double) alpha));
2790 }
2791 if (LocaleNCompare(expression,"sin",3) == 0)
2792 {
2793 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2794 exception);
2795 return((MagickRealType) sin((double) alpha));
2796 }
2797 if (LocaleNCompare(expression,"sqrt",4) == 0)
2798 {
2799 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2800 exception);
2801 return((MagickRealType) sqrt((double) alpha));
2802 }
cristy9eeedea2011-11-02 19:04:05 +00002803 if (LocaleNCompare(expression,"squish",6) == 0)
2804 {
2805 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2806 exception);
2807 return((MagickRealType) (1.0/(1.0+exp((double) (4.0*alpha)))));
2808 }
cristy3ed852e2009-09-05 21:47:34 +00002809 if (LocaleCompare(expression,"s") == 0)
2810 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2811 break;
2812 }
2813 case 'T':
2814 case 't':
2815 {
2816 if (LocaleNCompare(expression,"tanh",4) == 0)
2817 {
2818 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2819 exception);
2820 return((MagickRealType) tanh((double) alpha));
2821 }
2822 if (LocaleNCompare(expression,"tan",3) == 0)
2823 {
2824 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2825 exception);
2826 return((MagickRealType) tan((double) alpha));
2827 }
2828 if (LocaleCompare(expression,"Transparent") == 0)
2829 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002830 if (LocaleNCompare(expression,"trunc",5) == 0)
2831 {
2832 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2833 exception);
2834 if (alpha >= 0.0)
2835 return((MagickRealType) floor((double) alpha));
2836 return((MagickRealType) ceil((double) alpha));
2837 }
cristy3ed852e2009-09-05 21:47:34 +00002838 if (LocaleCompare(expression,"t") == 0)
2839 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2840 break;
2841 }
2842 case 'U':
2843 case 'u':
2844 {
2845 if (LocaleCompare(expression,"u") == 0)
2846 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2847 break;
2848 }
2849 case 'V':
2850 case 'v':
2851 {
2852 if (LocaleCompare(expression,"v") == 0)
2853 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2854 break;
2855 }
2856 case 'W':
2857 case 'w':
2858 {
cristy9eeedea2011-11-02 19:04:05 +00002859 if (LocaleNCompare(expression,"while",5) == 0)
2860 {
2861 do
2862 {
2863 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2864 exception);
2865 } while (fabs((double) alpha) >= MagickEpsilon);
2866 return((MagickRealType) *beta);
2867 }
cristy3ed852e2009-09-05 21:47:34 +00002868 if (LocaleCompare(expression,"w") == 0)
2869 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2870 break;
2871 }
2872 case 'Y':
2873 case 'y':
2874 {
2875 if (LocaleCompare(expression,"y") == 0)
2876 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2877 break;
2878 }
2879 case 'Z':
2880 case 'z':
2881 {
2882 if (LocaleCompare(expression,"z") == 0)
2883 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2884 break;
2885 }
2886 default:
2887 break;
2888 }
2889 q=(char *) expression;
cristydbdd0e32011-11-04 23:29:40 +00002890 alpha=InterpretSiPrefixValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002891 if (q == expression)
2892 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2893 return(alpha);
2894}
2895
cristy7832dc22011-09-05 01:21:53 +00002896MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
cristy3ed852e2009-09-05 21:47:34 +00002897 MagickRealType *alpha,ExceptionInfo *exception)
2898{
2899 MagickBooleanType
2900 status;
2901
cristy541ae572011-08-05 19:08:59 +00002902 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2903 exception);
cristy3ed852e2009-09-05 21:47:34 +00002904 return(status);
2905}
2906
2907MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2908 MagickRealType *alpha,ExceptionInfo *exception)
2909{
2910 FILE
2911 *file;
2912
2913 MagickBooleanType
2914 status;
2915
2916 file=fx_info->file;
2917 fx_info->file=(FILE *) NULL;
cristy541ae572011-08-05 19:08:59 +00002918 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2919 exception);
cristy3ed852e2009-09-05 21:47:34 +00002920 fx_info->file=file;
2921 return(status);
2922}
2923
cristy7832dc22011-09-05 01:21:53 +00002924MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002925 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002926 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002927{
2928 MagickRealType
2929 beta;
2930
2931 beta=0.0;
2932 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2933 exception);
2934 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2935}
2936
2937/*
2938%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2939% %
2940% %
2941% %
2942% F x I m a g e %
2943% %
2944% %
2945% %
2946%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2947%
2948% FxImage() applies a mathematical expression to the specified image.
2949%
2950% The format of the FxImage method is:
2951%
2952% Image *FxImage(const Image *image,const char *expression,
2953% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002954%
2955% A description of each parameter follows:
2956%
2957% o image: the image.
2958%
cristy3ed852e2009-09-05 21:47:34 +00002959% o expression: A mathematical expression.
2960%
2961% o exception: return any errors or warnings in this structure.
2962%
2963*/
2964
2965static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2966{
cristybb503372010-05-27 20:51:26 +00002967 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002968 i;
2969
2970 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002971 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002972 if (fx_info[i] != (FxInfo *) NULL)
2973 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002974 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002975 return(fx_info);
2976}
2977
2978static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2979 ExceptionInfo *exception)
2980{
2981 char
2982 *fx_expression;
2983
2984 FxInfo
2985 **fx_info;
2986
2987 MagickRealType
2988 alpha;
2989
cristybb503372010-05-27 20:51:26 +00002990 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002991 i;
2992
cristybb503372010-05-27 20:51:26 +00002993 size_t
cristy3ed852e2009-09-05 21:47:34 +00002994 number_threads;
2995
2996 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002997 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002998 if (fx_info == (FxInfo **) NULL)
2999 return((FxInfo **) NULL);
3000 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
3001 if (*expression != '@')
3002 fx_expression=ConstantString(expression);
3003 else
3004 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00003005 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00003006 {
3007 fx_info[i]=AcquireFxInfo(image,fx_expression);
3008 if (fx_info[i] == (FxInfo *) NULL)
3009 return(DestroyFxThreadSet(fx_info));
3010 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
3011 }
3012 fx_expression=DestroyString(fx_expression);
3013 return(fx_info);
3014}
3015
3016MagickExport Image *FxImage(const Image *image,const char *expression,
3017 ExceptionInfo *exception)
3018{
cristy3ed852e2009-09-05 21:47:34 +00003019#define FxImageTag "Fx/Image"
3020
cristyfa112112010-01-04 17:48:07 +00003021 CacheView
cristy79cedc72011-07-25 00:41:15 +00003022 *fx_view,
3023 *image_view;
cristyfa112112010-01-04 17:48:07 +00003024
cristy3ed852e2009-09-05 21:47:34 +00003025 FxInfo
cristyfa112112010-01-04 17:48:07 +00003026 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00003027
3028 Image
3029 *fx_image;
3030
cristy3ed852e2009-09-05 21:47:34 +00003031 MagickBooleanType
3032 status;
3033
cristybb503372010-05-27 20:51:26 +00003034 MagickOffsetType
3035 progress;
3036
cristy3ed852e2009-09-05 21:47:34 +00003037 MagickRealType
3038 alpha;
3039
cristybb503372010-05-27 20:51:26 +00003040 ssize_t
3041 y;
3042
cristy3ed852e2009-09-05 21:47:34 +00003043 assert(image != (Image *) NULL);
3044 assert(image->signature == MagickSignature);
3045 if (image->debug != MagickFalse)
3046 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00003047 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003048 if (fx_image == (Image *) NULL)
3049 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003050 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003051 {
cristy3ed852e2009-09-05 21:47:34 +00003052 fx_image=DestroyImage(fx_image);
3053 return((Image *) NULL);
3054 }
3055 fx_info=AcquireFxThreadSet(image,expression,exception);
3056 if (fx_info == (FxInfo **) NULL)
3057 {
3058 fx_image=DestroyImage(fx_image);
3059 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3060 }
3061 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3062 if (status == MagickFalse)
3063 {
3064 fx_image=DestroyImage(fx_image);
3065 fx_info=DestroyFxThreadSet(fx_info);
3066 return((Image *) NULL);
3067 }
3068 /*
3069 Fx image.
3070 */
3071 status=MagickTrue;
3072 progress=0;
cristy79cedc72011-07-25 00:41:15 +00003073 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003074 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003075#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003076 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003077#endif
cristybb503372010-05-27 20:51:26 +00003078 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003079 {
cristy5c9e6f22010-09-17 17:31:01 +00003080 const int
3081 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003082
cristy79cedc72011-07-25 00:41:15 +00003083 register const Quantum
3084 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003085
cristy4c08aed2011-07-01 19:47:50 +00003086 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003087 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003088
cristy79cedc72011-07-25 00:41:15 +00003089 register ssize_t
3090 x;
3091
cristy3ed852e2009-09-05 21:47:34 +00003092 if (status == MagickFalse)
3093 continue;
cristy79cedc72011-07-25 00:41:15 +00003094 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003095 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003096 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003097 {
3098 status=MagickFalse;
3099 continue;
3100 }
cristybb503372010-05-27 20:51:26 +00003101 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003102 {
cristy79cedc72011-07-25 00:41:15 +00003103 register ssize_t
3104 i;
3105
3106 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3107 {
3108 MagickRealType
3109 alpha;
3110
3111 PixelChannel
3112 channel;
3113
3114 PixelTrait
3115 fx_traits,
3116 traits;
3117
cristye2a912b2011-12-05 20:02:07 +00003118 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003119 traits=GetPixelChannelMapTraits(image,channel);
cristy79cedc72011-07-25 00:41:15 +00003120 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
cristy010d7d12011-08-31 01:02:48 +00003121 if ((traits == UndefinedPixelTrait) ||
3122 (fx_traits == UndefinedPixelTrait))
cristy79cedc72011-07-25 00:41:15 +00003123 continue;
cristyd09f8802012-02-04 16:44:10 +00003124 if (((fx_traits & CopyPixelTrait) != 0) ||
3125 (GetPixelMask(image,p) != 0))
cristy79cedc72011-07-25 00:41:15 +00003126 {
cristy0beccfa2011-09-25 20:47:53 +00003127 SetPixelChannel(fx_image,channel,p[i],q);
cristy79cedc72011-07-25 00:41:15 +00003128 continue;
3129 }
3130 alpha=0.0;
cristya382aca2011-12-06 18:22:48 +00003131 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3132 exception);
cristyb3a73b52011-07-26 01:34:43 +00003133 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003134 }
3135 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003136 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003137 }
3138 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3139 status=MagickFalse;
3140 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3141 {
3142 MagickBooleanType
3143 proceed;
3144
cristyb5d5f722009-11-04 03:03:49 +00003145#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003146 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003147#endif
3148 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3149 if (proceed == MagickFalse)
3150 status=MagickFalse;
3151 }
3152 }
cristy3ed852e2009-09-05 21:47:34 +00003153 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003154 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003155 fx_info=DestroyFxThreadSet(fx_info);
3156 if (status == MagickFalse)
3157 fx_image=DestroyImage(fx_image);
3158 return(fx_image);
3159}
3160
3161/*
3162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3163% %
3164% %
3165% %
3166% I m p l o d e I m a g e %
3167% %
3168% %
3169% %
3170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3171%
3172% ImplodeImage() creates a new image that is a copy of an existing
3173% one with the image pixels "implode" by the specified percentage. It
3174% allocates the memory necessary for the new Image structure and returns a
3175% pointer to the new image.
3176%
3177% The format of the ImplodeImage method is:
3178%
3179% Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003180% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003181%
3182% A description of each parameter follows:
3183%
3184% o implode_image: Method ImplodeImage returns a pointer to the image
3185% after it is implode. A null image is returned if there is a memory
3186% shortage.
3187%
3188% o image: the image.
3189%
3190% o amount: Define the extent of the implosion.
3191%
cristy76f512e2011-09-12 01:26:56 +00003192% o method: the pixel interpolation method.
3193%
cristy3ed852e2009-09-05 21:47:34 +00003194% o exception: return any errors or warnings in this structure.
3195%
3196*/
3197MagickExport Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003198 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003199{
3200#define ImplodeImageTag "Implode/Image"
3201
cristyfa112112010-01-04 17:48:07 +00003202 CacheView
3203 *image_view,
3204 *implode_view;
3205
cristy3ed852e2009-09-05 21:47:34 +00003206 Image
3207 *implode_image;
3208
cristy3ed852e2009-09-05 21:47:34 +00003209 MagickBooleanType
3210 status;
3211
cristybb503372010-05-27 20:51:26 +00003212 MagickOffsetType
3213 progress;
3214
cristy3ed852e2009-09-05 21:47:34 +00003215 MagickRealType
3216 radius;
3217
3218 PointInfo
3219 center,
3220 scale;
3221
cristybb503372010-05-27 20:51:26 +00003222 ssize_t
3223 y;
3224
cristy3ed852e2009-09-05 21:47:34 +00003225 /*
3226 Initialize implode image attributes.
3227 */
3228 assert(image != (Image *) NULL);
3229 assert(image->signature == MagickSignature);
3230 if (image->debug != MagickFalse)
3231 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3232 assert(exception != (ExceptionInfo *) NULL);
3233 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00003234 implode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3235 exception);
cristy3ed852e2009-09-05 21:47:34 +00003236 if (implode_image == (Image *) NULL)
3237 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003238 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003239 {
cristy3ed852e2009-09-05 21:47:34 +00003240 implode_image=DestroyImage(implode_image);
3241 return((Image *) NULL);
3242 }
cristy4c08aed2011-07-01 19:47:50 +00003243 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003244 implode_image->matte=MagickTrue;
3245 /*
3246 Compute scaling factor.
3247 */
3248 scale.x=1.0;
3249 scale.y=1.0;
3250 center.x=0.5*image->columns;
3251 center.y=0.5*image->rows;
3252 radius=center.x;
3253 if (image->columns > image->rows)
3254 scale.y=(double) image->columns/(double) image->rows;
3255 else
3256 if (image->columns < image->rows)
3257 {
3258 scale.x=(double) image->rows/(double) image->columns;
3259 radius=center.y;
3260 }
3261 /*
3262 Implode image.
3263 */
3264 status=MagickTrue;
3265 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003266 image_view=AcquireCacheView(image);
3267 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003268#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003269 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003270#endif
cristybb503372010-05-27 20:51:26 +00003271 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003272 {
cristy3ed852e2009-09-05 21:47:34 +00003273 MagickRealType
3274 distance;
3275
3276 PointInfo
3277 delta;
3278
cristy6d188022011-09-12 13:23:33 +00003279 register const Quantum
3280 *restrict p;
3281
cristybb503372010-05-27 20:51:26 +00003282 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003283 x;
3284
cristy4c08aed2011-07-01 19:47:50 +00003285 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003286 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003287
3288 if (status == MagickFalse)
3289 continue;
cristy6d188022011-09-12 13:23:33 +00003290 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003291 q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003292 exception);
cristy6d188022011-09-12 13:23:33 +00003293 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003294 {
3295 status=MagickFalse;
3296 continue;
3297 }
cristy3ed852e2009-09-05 21:47:34 +00003298 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00003299 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003300 {
cristy6d188022011-09-12 13:23:33 +00003301 register ssize_t
3302 i;
3303
cristy3ed852e2009-09-05 21:47:34 +00003304 /*
3305 Determine if the pixel is within an ellipse.
3306 */
cristy10a6c612012-01-29 21:41:05 +00003307 if (GetPixelMask(image,p) != 0)
3308 {
3309 p+=GetPixelChannels(image);
3310 q+=GetPixelChannels(implode_image);
3311 continue;
3312 }
cristy3ed852e2009-09-05 21:47:34 +00003313 delta.x=scale.x*(double) (x-center.x);
3314 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00003315 if (distance >= (radius*radius))
cristya6d7a9b2012-01-18 20:04:48 +00003316 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy6d188022011-09-12 13:23:33 +00003317 {
cristya6d7a9b2012-01-18 20:04:48 +00003318 PixelChannel
3319 channel;
3320
cristy1707c6c2012-01-18 23:30:54 +00003321 PixelTrait
3322 implode_traits,
3323 traits;
3324
cristya6d7a9b2012-01-18 20:04:48 +00003325 channel=GetPixelChannelMapChannel(image,i);
cristy1707c6c2012-01-18 23:30:54 +00003326 traits=GetPixelChannelMapTraits(image,channel);
3327 implode_traits=GetPixelChannelMapTraits(implode_image,channel);
3328 if ((traits == UndefinedPixelTrait) ||
3329 (implode_traits == UndefinedPixelTrait))
3330 continue;
cristya6d7a9b2012-01-18 20:04:48 +00003331 SetPixelChannel(implode_image,channel,p[i],q);
cristy6d188022011-09-12 13:23:33 +00003332 }
3333 else
cristy3ed852e2009-09-05 21:47:34 +00003334 {
3335 double
3336 factor;
3337
3338 /*
3339 Implode the pixel.
3340 */
3341 factor=1.0;
3342 if (distance > 0.0)
cristy1707c6c2012-01-18 23:30:54 +00003343 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/radius/
3344 2)),-amount);
cristy76f512e2011-09-12 01:26:56 +00003345 status=InterpolatePixelChannels(image,image_view,implode_image,method,
3346 (double) (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3347 scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00003348 }
cristy6d188022011-09-12 13:23:33 +00003349 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003350 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003351 }
3352 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3353 status=MagickFalse;
3354 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3355 {
3356 MagickBooleanType
3357 proceed;
3358
cristyb5d5f722009-11-04 03:03:49 +00003359#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003360 #pragma omp critical (MagickCore_ImplodeImage)
cristy3ed852e2009-09-05 21:47:34 +00003361#endif
3362 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3363 if (proceed == MagickFalse)
3364 status=MagickFalse;
3365 }
3366 }
3367 implode_view=DestroyCacheView(implode_view);
3368 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003369 if (status == MagickFalse)
3370 implode_image=DestroyImage(implode_image);
3371 return(implode_image);
3372}
3373
3374/*
3375%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3376% %
3377% %
3378% %
3379% M o r p h I m a g e s %
3380% %
3381% %
3382% %
3383%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3384%
3385% The MorphImages() method requires a minimum of two images. The first
3386% image is transformed into the second by a number of intervening images
3387% as specified by frames.
3388%
3389% The format of the MorphImage method is:
3390%
cristybb503372010-05-27 20:51:26 +00003391% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003392% ExceptionInfo *exception)
3393%
3394% A description of each parameter follows:
3395%
3396% o image: the image.
3397%
3398% o number_frames: Define the number of in-between image to generate.
3399% The more in-between frames, the smoother the morph.
3400%
3401% o exception: return any errors or warnings in this structure.
3402%
3403*/
3404MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003405 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003406{
3407#define MorphImageTag "Morph/Image"
3408
3409 Image
3410 *morph_image,
3411 *morph_images;
3412
cristy9d314ff2011-03-09 01:30:28 +00003413 MagickBooleanType
3414 status;
cristy3ed852e2009-09-05 21:47:34 +00003415
3416 MagickOffsetType
3417 scene;
3418
3419 MagickRealType
3420 alpha,
3421 beta;
3422
3423 register const Image
3424 *next;
3425
cristybb503372010-05-27 20:51:26 +00003426 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003427 i;
3428
cristy9d314ff2011-03-09 01:30:28 +00003429 ssize_t
3430 y;
cristy3ed852e2009-09-05 21:47:34 +00003431
3432 /*
3433 Clone first frame in sequence.
3434 */
3435 assert(image != (Image *) NULL);
3436 assert(image->signature == MagickSignature);
3437 if (image->debug != MagickFalse)
3438 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3439 assert(exception != (ExceptionInfo *) NULL);
3440 assert(exception->signature == MagickSignature);
3441 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3442 if (morph_images == (Image *) NULL)
3443 return((Image *) NULL);
3444 if (GetNextImageInList(image) == (Image *) NULL)
3445 {
3446 /*
3447 Morph single image.
3448 */
cristybb503372010-05-27 20:51:26 +00003449 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003450 {
3451 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3452 if (morph_image == (Image *) NULL)
3453 {
3454 morph_images=DestroyImageList(morph_images);
3455 return((Image *) NULL);
3456 }
3457 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003458 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003459 {
cristy8b27a6d2010-02-14 03:31:15 +00003460 MagickBooleanType
3461 proceed;
3462
cristybb503372010-05-27 20:51:26 +00003463 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3464 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003465 if (proceed == MagickFalse)
3466 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003467 }
3468 }
3469 return(GetFirstImageInList(morph_images));
3470 }
3471 /*
3472 Morph image sequence.
3473 */
3474 status=MagickTrue;
3475 scene=0;
3476 next=image;
3477 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3478 {
cristybb503372010-05-27 20:51:26 +00003479 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003480 {
3481 CacheView
3482 *image_view,
3483 *morph_view;
3484
3485 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3486 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003487 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristybb503372010-05-27 20:51:26 +00003488 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
cristy15b98cd2010-09-12 19:42:50 +00003489 next->rows+beta*GetNextImageInList(next)->rows+0.5),
3490 next->filter,next->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003491 if (morph_image == (Image *) NULL)
3492 {
3493 morph_images=DestroyImageList(morph_images);
3494 return((Image *) NULL);
3495 }
cristy1707c6c2012-01-18 23:30:54 +00003496 status=SetImageStorageClass(morph_image,DirectClass,exception);
3497 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003498 {
cristy3ed852e2009-09-05 21:47:34 +00003499 morph_image=DestroyImage(morph_image);
3500 return((Image *) NULL);
3501 }
3502 AppendImageToList(&morph_images,morph_image);
3503 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003504 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3505 morph_images->rows,GetNextImageInList(next)->filter,
3506 GetNextImageInList(next)->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003507 if (morph_image == (Image *) NULL)
3508 {
3509 morph_images=DestroyImageList(morph_images);
3510 return((Image *) NULL);
3511 }
3512 image_view=AcquireCacheView(morph_image);
3513 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003514#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003515 #pragma omp parallel for schedule(static,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003516#endif
cristybb503372010-05-27 20:51:26 +00003517 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003518 {
3519 MagickBooleanType
3520 sync;
3521
cristy4c08aed2011-07-01 19:47:50 +00003522 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003523 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003524
cristybb503372010-05-27 20:51:26 +00003525 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003526 x;
3527
cristy4c08aed2011-07-01 19:47:50 +00003528 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003529 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003530
3531 if (status == MagickFalse)
3532 continue;
3533 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3534 exception);
3535 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3536 exception);
cristy4c08aed2011-07-01 19:47:50 +00003537 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003538 {
3539 status=MagickFalse;
3540 continue;
3541 }
cristybb503372010-05-27 20:51:26 +00003542 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003543 {
cristy1707c6c2012-01-18 23:30:54 +00003544 register ssize_t
3545 i;
3546
cristy10a6c612012-01-29 21:41:05 +00003547 for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
cristy1707c6c2012-01-18 23:30:54 +00003548 {
3549 PixelChannel
3550 channel;
3551
3552 PixelTrait
3553 morph_traits,
3554 traits;
3555
3556 channel=GetPixelChannelMapChannel(image,i);
3557 traits=GetPixelChannelMapTraits(image,channel);
3558 morph_traits=GetPixelChannelMapTraits(morph_image,channel);
3559 if ((traits == UndefinedPixelTrait) ||
3560 (morph_traits == UndefinedPixelTrait))
3561 continue;
cristyd09f8802012-02-04 16:44:10 +00003562 if (((morph_traits & CopyPixelTrait) != 0) ||
3563 (GetPixelMask(image,p) != 0))
cristy1707c6c2012-01-18 23:30:54 +00003564 {
3565 SetPixelChannel(morph_image,channel,p[i],q);
3566 continue;
3567 }
3568 SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3569 GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3570 }
cristyed231572011-07-14 02:18:59 +00003571 p+=GetPixelChannels(morph_image);
3572 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003573 }
3574 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3575 if (sync == MagickFalse)
3576 status=MagickFalse;
3577 }
3578 morph_view=DestroyCacheView(morph_view);
3579 image_view=DestroyCacheView(image_view);
3580 morph_image=DestroyImage(morph_image);
3581 }
cristybb503372010-05-27 20:51:26 +00003582 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003583 break;
3584 /*
3585 Clone last frame in sequence.
3586 */
3587 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3588 if (morph_image == (Image *) NULL)
3589 {
3590 morph_images=DestroyImageList(morph_images);
3591 return((Image *) NULL);
3592 }
3593 AppendImageToList(&morph_images,morph_image);
3594 morph_images=GetLastImageInList(morph_images);
3595 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3596 {
3597 MagickBooleanType
3598 proceed;
3599
cristyb5d5f722009-11-04 03:03:49 +00003600#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003601 #pragma omp critical (MagickCore_MorphImages)
cristy3ed852e2009-09-05 21:47:34 +00003602#endif
3603 proceed=SetImageProgress(image,MorphImageTag,scene,
3604 GetImageListLength(image));
3605 if (proceed == MagickFalse)
3606 status=MagickFalse;
3607 }
3608 scene++;
3609 }
3610 if (GetNextImageInList(next) != (Image *) NULL)
3611 {
3612 morph_images=DestroyImageList(morph_images);
3613 return((Image *) NULL);
3614 }
3615 return(GetFirstImageInList(morph_images));
3616}
3617
3618/*
3619%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3620% %
3621% %
3622% %
3623% P l a s m a I m a g e %
3624% %
3625% %
3626% %
3627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3628%
3629% PlasmaImage() initializes an image with plasma fractal values. The image
3630% must be initialized with a base color and the random number generator
3631% seeded before this method is called.
3632%
3633% The format of the PlasmaImage method is:
3634%
3635% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristy5cbc0162011-08-29 00:36:28 +00003636% size_t attenuate,size_t depth,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003637%
3638% A description of each parameter follows:
3639%
3640% o image: the image.
3641%
3642% o segment: Define the region to apply plasma fractals values.
3643%
glennrp7dae1ca2010-09-16 12:17:35 +00003644% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003645%
3646% o depth: Limit the plasma recursion depth.
3647%
cristy5cbc0162011-08-29 00:36:28 +00003648% o exception: return any errors or warnings in this structure.
3649%
cristy3ed852e2009-09-05 21:47:34 +00003650*/
3651
3652static inline Quantum PlasmaPixel(RandomInfo *random_info,
3653 const MagickRealType pixel,const MagickRealType noise)
3654{
3655 Quantum
3656 plasma;
3657
cristyce70c172010-01-07 17:15:30 +00003658 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003659 noise/2.0);
3660 return(plasma);
3661}
3662
cristyda1f9c12011-10-02 21:39:49 +00003663static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
3664 CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3665 const SegmentInfo *segment,size_t attenuate,size_t depth,
3666 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003667{
cristy3ed852e2009-09-05 21:47:34 +00003668 MagickRealType
3669 plasma;
3670
cristyda1f9c12011-10-02 21:39:49 +00003671 PixelChannel
3672 channel;
3673
3674 PixelTrait
3675 traits;
3676
3677 register const Quantum
3678 *restrict u,
3679 *restrict v;
3680
3681 register Quantum
3682 *restrict q;
3683
3684 register ssize_t
3685 i;
cristy3ed852e2009-09-05 21:47:34 +00003686
cristy9d314ff2011-03-09 01:30:28 +00003687 ssize_t
3688 x,
3689 x_mid,
3690 y,
3691 y_mid;
3692
cristy3ed852e2009-09-05 21:47:34 +00003693 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3694 return(MagickTrue);
3695 if (depth != 0)
3696 {
3697 SegmentInfo
3698 local_info;
3699
3700 /*
3701 Divide the area into quadrants and recurse.
3702 */
3703 depth--;
3704 attenuate++;
cristybb503372010-05-27 20:51:26 +00003705 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3706 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003707 local_info=(*segment);
3708 local_info.x2=(double) x_mid;
3709 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003710 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3711 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003712 local_info=(*segment);
3713 local_info.y1=(double) y_mid;
3714 local_info.x2=(double) x_mid;
cristyda1f9c12011-10-02 21:39:49 +00003715 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3716 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003717 local_info=(*segment);
3718 local_info.x1=(double) x_mid;
3719 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003720 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3721 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003722 local_info=(*segment);
3723 local_info.x1=(double) x_mid;
3724 local_info.y1=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003725 return(PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3726 &local_info,attenuate,depth,exception));
cristy3ed852e2009-09-05 21:47:34 +00003727 }
cristybb503372010-05-27 20:51:26 +00003728 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3729 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003730 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3731 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3732 return(MagickFalse);
3733 /*
3734 Average pixels and apply plasma.
3735 */
cristy3ed852e2009-09-05 21:47:34 +00003736 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3737 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3738 {
cristy3ed852e2009-09-05 21:47:34 +00003739 /*
3740 Left pixel.
3741 */
cristybb503372010-05-27 20:51:26 +00003742 x=(ssize_t) ceil(segment->x1-0.5);
cristy1707c6c2012-01-18 23:30:54 +00003743 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3744 exception);
3745 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3746 exception);
cristyc5c6f662010-09-22 14:23:02 +00003747 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003748 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3749 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003750 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003751 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3752 {
cristye2a912b2011-12-05 20:02:07 +00003753 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003754 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003755 if (traits == UndefinedPixelTrait)
3756 continue;
3757 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3758 }
cristyc5c6f662010-09-22 14:23:02 +00003759 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003760 if (segment->x1 != segment->x2)
3761 {
3762 /*
3763 Right pixel.
3764 */
cristybb503372010-05-27 20:51:26 +00003765 x=(ssize_t) ceil(segment->x2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003766 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3767 1,1,exception);
3768 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3769 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003770 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003771 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3772 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003773 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003774 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3775 {
cristye2a912b2011-12-05 20:02:07 +00003776 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003777 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003778 if (traits == UndefinedPixelTrait)
3779 continue;
3780 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3781 }
cristyc5c6f662010-09-22 14:23:02 +00003782 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003783 }
3784 }
3785 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3786 {
3787 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3788 {
cristy3ed852e2009-09-05 21:47:34 +00003789 /*
3790 Bottom pixel.
3791 */
cristybb503372010-05-27 20:51:26 +00003792 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003793 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3794 1,1,exception);
3795 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3796 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003797 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003798 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3799 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003800 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003801 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3802 {
cristye2a912b2011-12-05 20:02:07 +00003803 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003804 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003805 if (traits == UndefinedPixelTrait)
3806 continue;
3807 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3808 }
cristyc5c6f662010-09-22 14:23:02 +00003809 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003810 }
3811 if (segment->y1 != segment->y2)
3812 {
cristy3ed852e2009-09-05 21:47:34 +00003813 /*
3814 Top pixel.
3815 */
cristybb503372010-05-27 20:51:26 +00003816 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003817 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3818 1,1,exception);
3819 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3820 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003821 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003822 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3823 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003824 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003825 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3826 {
cristye2a912b2011-12-05 20:02:07 +00003827 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003828 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003829 if (traits == UndefinedPixelTrait)
3830 continue;
3831 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3832 }
cristyc5c6f662010-09-22 14:23:02 +00003833 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003834 }
3835 }
3836 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3837 {
cristy3ed852e2009-09-05 21:47:34 +00003838 /*
3839 Middle pixel.
3840 */
cristybb503372010-05-27 20:51:26 +00003841 x=(ssize_t) ceil(segment->x1-0.5);
3842 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003843 u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
cristybb503372010-05-27 20:51:26 +00003844 x=(ssize_t) ceil(segment->x2-0.5);
3845 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003846 v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003847 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003848 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3849 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003850 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003851 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3852 {
cristye2a912b2011-12-05 20:02:07 +00003853 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003854 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003855 if (traits == UndefinedPixelTrait)
3856 continue;
3857 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3858 }
cristyc5c6f662010-09-22 14:23:02 +00003859 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003860 }
3861 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3862 return(MagickTrue);
3863 return(MagickFalse);
3864}
cristyda1f9c12011-10-02 21:39:49 +00003865
cristy3ed852e2009-09-05 21:47:34 +00003866MagickExport MagickBooleanType PlasmaImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00003867 const SegmentInfo *segment,size_t attenuate,size_t depth,
3868 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003869{
cristyc5c6f662010-09-22 14:23:02 +00003870 CacheView
cristyda1f9c12011-10-02 21:39:49 +00003871 *image_view,
3872 *u_view,
3873 *v_view;
cristyc5c6f662010-09-22 14:23:02 +00003874
cristy3ed852e2009-09-05 21:47:34 +00003875 MagickBooleanType
3876 status;
3877
3878 RandomInfo
3879 *random_info;
3880
3881 if (image->debug != MagickFalse)
3882 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3883 assert(image != (Image *) NULL);
3884 assert(image->signature == MagickSignature);
3885 if (image->debug != MagickFalse)
3886 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy5cbc0162011-08-29 00:36:28 +00003887 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristyc5c6f662010-09-22 14:23:02 +00003888 return(MagickFalse);
3889 image_view=AcquireCacheView(image);
cristyda1f9c12011-10-02 21:39:49 +00003890 u_view=AcquireCacheView(image);
3891 v_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003892 random_info=AcquireRandomInfo();
cristyda1f9c12011-10-02 21:39:49 +00003893 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
3894 attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003895 random_info=DestroyRandomInfo(random_info);
cristyda1f9c12011-10-02 21:39:49 +00003896 v_view=DestroyCacheView(v_view);
3897 u_view=DestroyCacheView(u_view);
cristyc5c6f662010-09-22 14:23:02 +00003898 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003899 return(status);
3900}
3901
3902/*
3903%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3904% %
3905% %
3906% %
3907% P o l a r o i d I m a g e %
3908% %
3909% %
3910% %
3911%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3912%
3913% PolaroidImage() simulates a Polaroid picture.
3914%
3915% The format of the AnnotateImage method is:
3916%
3917% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003918% const char *caption,const double angle,
3919% const PixelInterpolateMethod method,ExceptionInfo exception)
cristy3ed852e2009-09-05 21:47:34 +00003920%
3921% A description of each parameter follows:
3922%
3923% o image: the image.
3924%
3925% o draw_info: the draw info.
3926%
cristye9e3d382011-12-14 01:50:13 +00003927% o caption: the Polaroid caption.
3928%
cristycee97112010-05-28 00:44:52 +00003929% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003930%
cristy5c4e2582011-09-11 19:21:03 +00003931% o method: the pixel interpolation method.
3932%
cristy3ed852e2009-09-05 21:47:34 +00003933% o exception: return any errors or warnings in this structure.
3934%
3935*/
3936MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003937 const char *caption,const double angle,const PixelInterpolateMethod method,
cristy5c4e2582011-09-11 19:21:03 +00003938 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003939{
cristy3ed852e2009-09-05 21:47:34 +00003940 Image
3941 *bend_image,
3942 *caption_image,
3943 *flop_image,
3944 *picture_image,
3945 *polaroid_image,
3946 *rotate_image,
3947 *trim_image;
3948
cristybb503372010-05-27 20:51:26 +00003949 size_t
cristy3ed852e2009-09-05 21:47:34 +00003950 height;
3951
cristy9d314ff2011-03-09 01:30:28 +00003952 ssize_t
3953 quantum;
3954
cristy3ed852e2009-09-05 21:47:34 +00003955 /*
3956 Simulate a Polaroid picture.
3957 */
3958 assert(image != (Image *) NULL);
3959 assert(image->signature == MagickSignature);
3960 if (image->debug != MagickFalse)
3961 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3962 assert(exception != (ExceptionInfo *) NULL);
3963 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003964 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003965 image->rows)/25.0,10.0);
3966 height=image->rows+2*quantum;
3967 caption_image=(Image *) NULL;
cristye9e3d382011-12-14 01:50:13 +00003968 if (caption != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003969 {
3970 char
cristye9e3d382011-12-14 01:50:13 +00003971 geometry[MaxTextExtent],
3972 *text;
cristy3ed852e2009-09-05 21:47:34 +00003973
3974 DrawInfo
3975 *annotate_info;
3976
cristy3ed852e2009-09-05 21:47:34 +00003977 MagickBooleanType
3978 status;
3979
cristy9d314ff2011-03-09 01:30:28 +00003980 ssize_t
3981 count;
3982
cristy3ed852e2009-09-05 21:47:34 +00003983 TypeMetric
3984 metrics;
3985
3986 /*
3987 Generate caption image.
3988 */
3989 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3990 if (caption_image == (Image *) NULL)
3991 return((Image *) NULL);
3992 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
cristye9e3d382011-12-14 01:50:13 +00003993 text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
3994 exception);
3995 (void) CloneString(&annotate_info->text,text);
cristy6b1d05e2010-09-22 19:17:27 +00003996 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
cristye9e3d382011-12-14 01:50:13 +00003997 &text,exception);
3998 status=SetImageExtent(caption_image,image->columns,(size_t) ((count+1)*
3999 (metrics.ascent-metrics.descent)+0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00004000 if (status == MagickFalse)
4001 caption_image=DestroyImage(caption_image);
4002 else
4003 {
4004 caption_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004005 (void) SetImageBackgroundColor(caption_image,exception);
cristye9e3d382011-12-14 01:50:13 +00004006 (void) CloneString(&annotate_info->text,text);
cristyb51dff52011-05-19 16:55:47 +00004007 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00004008 metrics.ascent);
4009 if (annotate_info->gravity == UndefinedGravity)
4010 (void) CloneString(&annotate_info->geometry,AcquireString(
4011 geometry));
cristy5cbc0162011-08-29 00:36:28 +00004012 (void) AnnotateImage(caption_image,annotate_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004013 height+=caption_image->rows;
4014 }
4015 annotate_info=DestroyDrawInfo(annotate_info);
cristye9e3d382011-12-14 01:50:13 +00004016 text=DestroyString(text);
cristy3ed852e2009-09-05 21:47:34 +00004017 }
4018 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4019 exception);
4020 if (picture_image == (Image *) NULL)
4021 {
4022 if (caption_image != (Image *) NULL)
4023 caption_image=DestroyImage(caption_image);
4024 return((Image *) NULL);
4025 }
4026 picture_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004027 (void) SetImageBackgroundColor(picture_image,exception);
cristye941a752011-10-15 01:52:48 +00004028 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum,
4029 exception);
cristy3ed852e2009-09-05 21:47:34 +00004030 if (caption_image != (Image *) NULL)
4031 {
cristy1707c6c2012-01-18 23:30:54 +00004032 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,quantum,
4033 (ssize_t) (image->rows+3*quantum/2),exception);
cristy3ed852e2009-09-05 21:47:34 +00004034 caption_image=DestroyImage(caption_image);
4035 }
cristy9950d572011-10-01 18:22:35 +00004036 (void) QueryColorCompliance("none",AllCompliance,
4037 &picture_image->background_color,exception);
cristy63240882011-08-05 19:05:27 +00004038 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004039 rotate_image=RotateImage(picture_image,90.0,exception);
4040 picture_image=DestroyImage(picture_image);
4041 if (rotate_image == (Image *) NULL)
4042 return((Image *) NULL);
4043 picture_image=rotate_image;
4044 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
cristy5c4e2582011-09-11 19:21:03 +00004045 picture_image->columns,method,exception);
cristy3ed852e2009-09-05 21:47:34 +00004046 picture_image=DestroyImage(picture_image);
4047 if (bend_image == (Image *) NULL)
4048 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004049 picture_image=bend_image;
4050 rotate_image=RotateImage(picture_image,-90.0,exception);
4051 picture_image=DestroyImage(picture_image);
4052 if (rotate_image == (Image *) NULL)
4053 return((Image *) NULL);
4054 picture_image=rotate_image;
4055 picture_image->background_color=image->background_color;
cristy78ec1a92011-12-09 09:25:34 +00004056 polaroid_image=ShadowImage(picture_image,80.0,2.0,0.0,quantum/3,quantum/3,
cristy3ed852e2009-09-05 21:47:34 +00004057 exception);
4058 if (polaroid_image == (Image *) NULL)
4059 {
4060 picture_image=DestroyImage(picture_image);
4061 return(picture_image);
4062 }
4063 flop_image=FlopImage(polaroid_image,exception);
4064 polaroid_image=DestroyImage(polaroid_image);
4065 if (flop_image == (Image *) NULL)
4066 {
4067 picture_image=DestroyImage(picture_image);
4068 return(picture_image);
4069 }
4070 polaroid_image=flop_image;
cristy1707c6c2012-01-18 23:30:54 +00004071 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,(ssize_t)
4072 (-0.01*picture_image->columns/2.0),0L,exception);
cristy3ed852e2009-09-05 21:47:34 +00004073 picture_image=DestroyImage(picture_image);
cristy9950d572011-10-01 18:22:35 +00004074 (void) QueryColorCompliance("none",AllCompliance,
4075 &polaroid_image->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00004076 rotate_image=RotateImage(polaroid_image,angle,exception);
4077 polaroid_image=DestroyImage(polaroid_image);
4078 if (rotate_image == (Image *) NULL)
4079 return((Image *) NULL);
4080 polaroid_image=rotate_image;
4081 trim_image=TrimImage(polaroid_image,exception);
4082 polaroid_image=DestroyImage(polaroid_image);
4083 if (trim_image == (Image *) NULL)
4084 return((Image *) NULL);
4085 polaroid_image=trim_image;
4086 return(polaroid_image);
4087}
4088
4089/*
4090%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4091% %
4092% %
4093% %
cristy3ed852e2009-09-05 21:47:34 +00004094% S e p i a T o n e I m a g e %
4095% %
4096% %
4097% %
4098%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4099%
4100% MagickSepiaToneImage() applies a special effect to the image, similar to the
4101% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4102% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4103% threshold of 80% is a good starting point for a reasonable tone.
4104%
4105% The format of the SepiaToneImage method is:
4106%
4107% Image *SepiaToneImage(const Image *image,const double threshold,
4108% ExceptionInfo *exception)
4109%
4110% A description of each parameter follows:
4111%
4112% o image: the image.
4113%
4114% o threshold: the tone threshold.
4115%
4116% o exception: return any errors or warnings in this structure.
4117%
4118*/
4119MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4120 ExceptionInfo *exception)
4121{
4122#define SepiaToneImageTag "SepiaTone/Image"
4123
cristyc4c8d132010-01-07 01:58:38 +00004124 CacheView
4125 *image_view,
4126 *sepia_view;
4127
cristy3ed852e2009-09-05 21:47:34 +00004128 Image
4129 *sepia_image;
4130
cristy3ed852e2009-09-05 21:47:34 +00004131 MagickBooleanType
4132 status;
4133
cristybb503372010-05-27 20:51:26 +00004134 MagickOffsetType
4135 progress;
4136
4137 ssize_t
4138 y;
4139
cristy3ed852e2009-09-05 21:47:34 +00004140 /*
4141 Initialize sepia-toned image attributes.
4142 */
4143 assert(image != (const Image *) NULL);
4144 assert(image->signature == MagickSignature);
4145 if (image->debug != MagickFalse)
4146 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4147 assert(exception != (ExceptionInfo *) NULL);
4148 assert(exception->signature == MagickSignature);
cristy1707c6c2012-01-18 23:30:54 +00004149 sepia_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004150 if (sepia_image == (Image *) NULL)
4151 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004152 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004153 {
cristy3ed852e2009-09-05 21:47:34 +00004154 sepia_image=DestroyImage(sepia_image);
4155 return((Image *) NULL);
4156 }
4157 /*
4158 Tone each row of the image.
4159 */
4160 status=MagickTrue;
4161 progress=0;
4162 image_view=AcquireCacheView(image);
4163 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004164#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004165 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004166#endif
cristybb503372010-05-27 20:51:26 +00004167 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004168 {
cristy4c08aed2011-07-01 19:47:50 +00004169 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004170 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004171
cristybb503372010-05-27 20:51:26 +00004172 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004173 x;
4174
cristy4c08aed2011-07-01 19:47:50 +00004175 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004176 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004177
4178 if (status == MagickFalse)
4179 continue;
4180 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00004181 q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004182 exception);
cristy4c08aed2011-07-01 19:47:50 +00004183 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004184 {
4185 status=MagickFalse;
4186 continue;
4187 }
cristybb503372010-05-27 20:51:26 +00004188 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004189 {
4190 MagickRealType
4191 intensity,
4192 tone;
4193
cristy4c08aed2011-07-01 19:47:50 +00004194 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004195 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4196 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004197 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004198 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4199 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004200 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004201 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004202 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004203 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004204 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4205 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4206 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4207 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004208 p+=GetPixelChannels(image);
4209 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004210 }
4211 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4212 status=MagickFalse;
4213 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4214 {
4215 MagickBooleanType
4216 proceed;
4217
cristyb5d5f722009-11-04 03:03:49 +00004218#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004219 #pragma omp critical (MagickCore_SepiaToneImage)
cristy3ed852e2009-09-05 21:47:34 +00004220#endif
4221 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4222 image->rows);
4223 if (proceed == MagickFalse)
4224 status=MagickFalse;
4225 }
4226 }
4227 sepia_view=DestroyCacheView(sepia_view);
4228 image_view=DestroyCacheView(image_view);
cristye23ec9d2011-08-16 18:15:40 +00004229 (void) NormalizeImage(sepia_image,exception);
4230 (void) ContrastImage(sepia_image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004231 if (status == MagickFalse)
4232 sepia_image=DestroyImage(sepia_image);
4233 return(sepia_image);
4234}
4235
4236/*
4237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4238% %
4239% %
4240% %
4241% S h a d o w I m a g e %
4242% %
4243% %
4244% %
4245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4246%
4247% ShadowImage() simulates a shadow from the specified image and returns it.
4248%
4249% The format of the ShadowImage method is:
4250%
cristy70cddf72011-12-10 22:42:42 +00004251% Image *ShadowImage(const Image *image,const double alpha,
cristyeb6e6582011-12-09 09:14:23 +00004252% const double sigma,const double bias,const ssize_t x_offset,
4253% const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004254%
4255% A description of each parameter follows:
4256%
4257% o image: the image.
4258%
cristy70cddf72011-12-10 22:42:42 +00004259% o alpha: percentage transparency.
cristy3ed852e2009-09-05 21:47:34 +00004260%
4261% o sigma: the standard deviation of the Gaussian, in pixels.
4262%
cristyeb6e6582011-12-09 09:14:23 +00004263% o bias: the bias.
4264%
cristy3ed852e2009-09-05 21:47:34 +00004265% o x_offset: the shadow x-offset.
4266%
4267% o y_offset: the shadow y-offset.
4268%
4269% o exception: return any errors or warnings in this structure.
4270%
4271*/
cristy70cddf72011-12-10 22:42:42 +00004272MagickExport Image *ShadowImage(const Image *image,const double alpha,
cristyeb6e6582011-12-09 09:14:23 +00004273 const double sigma,const double bias,const ssize_t x_offset,
4274 const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004275{
4276#define ShadowImageTag "Shadow/Image"
4277
cristy70cddf72011-12-10 22:42:42 +00004278 CacheView
4279 *image_view;
4280
cristybd5a96c2011-08-21 00:04:26 +00004281 ChannelType
4282 channel_mask;
4283
cristy3ed852e2009-09-05 21:47:34 +00004284 Image
4285 *border_image,
4286 *clone_image,
4287 *shadow_image;
4288
cristy70cddf72011-12-10 22:42:42 +00004289 MagickBooleanType
4290 status;
4291
cristy3ed852e2009-09-05 21:47:34 +00004292 RectangleInfo
4293 border_info;
4294
cristy70cddf72011-12-10 22:42:42 +00004295 ssize_t
4296 y;
4297
cristy3ed852e2009-09-05 21:47:34 +00004298 assert(image != (Image *) NULL);
4299 assert(image->signature == MagickSignature);
4300 if (image->debug != MagickFalse)
4301 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4302 assert(exception != (ExceptionInfo *) NULL);
4303 assert(exception->signature == MagickSignature);
4304 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4305 if (clone_image == (Image *) NULL)
4306 return((Image *) NULL);
cristy387430f2012-02-07 13:09:46 +00004307 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod,
4308 exception);
cristybb503372010-05-27 20:51:26 +00004309 border_info.width=(size_t) floor(2.0*sigma+0.5);
4310 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004311 border_info.x=0;
4312 border_info.y=0;
cristy9950d572011-10-01 18:22:35 +00004313 (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4314 exception);
cristy70cddf72011-12-10 22:42:42 +00004315 clone_image->matte=MagickTrue;
4316 border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
cristy3ed852e2009-09-05 21:47:34 +00004317 clone_image=DestroyImage(clone_image);
4318 if (border_image == (Image *) NULL)
4319 return((Image *) NULL);
4320 if (border_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004321 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004322 /*
4323 Shadow image.
4324 */
cristy70cddf72011-12-10 22:42:42 +00004325 status=MagickTrue;
4326 image_view=AcquireCacheView(border_image);
4327 for (y=0; y < (ssize_t) border_image->rows; y++)
4328 {
4329 PixelInfo
4330 background_color;
4331
4332 register Quantum
4333 *restrict q;
4334
4335 register ssize_t
4336 x;
4337
4338 if (status == MagickFalse)
4339 continue;
4340 q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4341 exception);
4342 if (q == (Quantum *) NULL)
4343 {
4344 status=MagickFalse;
4345 continue;
4346 }
4347 background_color=border_image->background_color;
4348 background_color.matte=MagickTrue;
4349 for (x=0; x < (ssize_t) border_image->columns; x++)
4350 {
4351 if (border_image->matte != MagickFalse)
4352 background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4353 SetPixelInfoPixel(border_image,&background_color,q);
4354 q+=GetPixelChannels(border_image);
4355 }
4356 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4357 status=MagickFalse;
4358 }
4359 image_view=DestroyCacheView(image_view);
4360 if (status == MagickFalse)
4361 {
4362 border_image=DestroyImage(border_image);
4363 return((Image *) NULL);
4364 }
cristybd5a96c2011-08-21 00:04:26 +00004365 channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
cristyb27bb772011-12-11 16:12:50 +00004366 shadow_image=BlurImage(border_image,0.0,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00004367 border_image=DestroyImage(border_image);
4368 if (shadow_image == (Image *) NULL)
4369 return((Image *) NULL);
cristyae1969f2011-12-10 03:07:36 +00004370 (void) SetPixelChannelMapMask(shadow_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004371 if (shadow_image->page.width == 0)
4372 shadow_image->page.width=shadow_image->columns;
4373 if (shadow_image->page.height == 0)
4374 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004375 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4376 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4377 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4378 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004379 return(shadow_image);
4380}
4381
4382/*
4383%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4384% %
4385% %
4386% %
4387% S k e t c h I m a g e %
4388% %
4389% %
4390% %
4391%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4392%
4393% SketchImage() simulates a pencil sketch. We convolve the image with a
4394% Gaussian operator of the given radius and standard deviation (sigma). For
4395% reasonable results, radius should be larger than sigma. Use a radius of 0
4396% and SketchImage() selects a suitable radius for you. Angle gives the angle
4397% of the sketch.
4398%
4399% The format of the SketchImage method is:
4400%
4401% Image *SketchImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00004402% const double sigma,const double angle,const double bias,
4403% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004404%
4405% A description of each parameter follows:
4406%
4407% o image: the image.
4408%
cristy574cc262011-08-05 01:23:58 +00004409% o radius: the radius of the Gaussian, in pixels, not counting the
4410% center pixel.
cristy3ed852e2009-09-05 21:47:34 +00004411%
4412% o sigma: the standard deviation of the Gaussian, in pixels.
4413%
cristyf7ef0252011-09-09 14:50:06 +00004414% o angle: apply the effect along this angle.
4415%
4416% o bias: the bias.
cristy3ed852e2009-09-05 21:47:34 +00004417%
4418% o exception: return any errors or warnings in this structure.
4419%
4420*/
4421MagickExport Image *SketchImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00004422 const double sigma,const double angle,const double bias,
4423 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004424{
cristyfa112112010-01-04 17:48:07 +00004425 CacheView
4426 *random_view;
4427
cristy3ed852e2009-09-05 21:47:34 +00004428 Image
4429 *blend_image,
4430 *blur_image,
4431 *dodge_image,
4432 *random_image,
4433 *sketch_image;
4434
cristy3ed852e2009-09-05 21:47:34 +00004435 MagickBooleanType
4436 status;
4437
cristy3ed852e2009-09-05 21:47:34 +00004438 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004439 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004440
cristy9d314ff2011-03-09 01:30:28 +00004441 ssize_t
4442 y;
4443
cristy3ed852e2009-09-05 21:47:34 +00004444 /*
4445 Sketch image.
4446 */
4447 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4448 MagickTrue,exception);
4449 if (random_image == (Image *) NULL)
4450 return((Image *) NULL);
4451 status=MagickTrue;
cristy1b784432009-12-19 02:20:40 +00004452 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004453 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004454#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004455 #pragma omp parallel for schedule(static,4) shared(status)
cristy1b784432009-12-19 02:20:40 +00004456#endif
cristybb503372010-05-27 20:51:26 +00004457 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004458 {
cristy5c9e6f22010-09-17 17:31:01 +00004459 const int
4460 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004461
cristybb503372010-05-27 20:51:26 +00004462 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004463 x;
4464
cristy4c08aed2011-07-01 19:47:50 +00004465 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004466 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004467
cristy1b784432009-12-19 02:20:40 +00004468 if (status == MagickFalse)
4469 continue;
cristy3ed852e2009-09-05 21:47:34 +00004470 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4471 exception);
cristyacd2ed22011-08-30 01:44:23 +00004472 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004473 {
4474 status=MagickFalse;
4475 continue;
4476 }
cristybb503372010-05-27 20:51:26 +00004477 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004478 {
cristy76f512e2011-09-12 01:26:56 +00004479 MagickRealType
4480 value;
4481
4482 register ssize_t
4483 i;
4484
cristy10a6c612012-01-29 21:41:05 +00004485 if (GetPixelMask(random_image,q) != 0)
4486 {
4487 q+=GetPixelChannels(random_image);
4488 continue;
4489 }
cristy76f512e2011-09-12 01:26:56 +00004490 value=GetPseudoRandomValue(random_info[id]);
4491 for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4492 {
cristyabace412011-12-11 15:56:53 +00004493 PixelChannel
4494 channel;
4495
cristy76f512e2011-09-12 01:26:56 +00004496 PixelTrait
4497 traits;
4498
cristyabace412011-12-11 15:56:53 +00004499 channel=GetPixelChannelMapChannel(image,i);
4500 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004501 if (traits == UndefinedPixelTrait)
4502 continue;
4503 q[i]=ClampToQuantum(QuantumRange*value);
4504 }
cristyed231572011-07-14 02:18:59 +00004505 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004506 }
4507 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4508 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004509 }
4510 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004511 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004512 if (status == MagickFalse)
4513 {
4514 random_image=DestroyImage(random_image);
4515 return(random_image);
4516 }
cristyf7ef0252011-09-09 14:50:06 +00004517 blur_image=MotionBlurImage(random_image,radius,sigma,angle,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00004518 random_image=DestroyImage(random_image);
4519 if (blur_image == (Image *) NULL)
4520 return((Image *) NULL);
cristy6bfd6902011-12-09 01:33:45 +00004521 dodge_image=EdgeImage(blur_image,radius,1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004522 blur_image=DestroyImage(blur_image);
4523 if (dodge_image == (Image *) NULL)
4524 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +00004525 (void) NormalizeImage(dodge_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00004526 (void) NegateImage(dodge_image,MagickFalse,exception);
cristye941a752011-10-15 01:52:48 +00004527 (void) TransformImage(&dodge_image,(char *) NULL,"50%",exception);
cristy3ed852e2009-09-05 21:47:34 +00004528 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4529 if (sketch_image == (Image *) NULL)
4530 {
4531 dodge_image=DestroyImage(dodge_image);
4532 return((Image *) NULL);
4533 }
cristye941a752011-10-15 01:52:48 +00004534 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0,
4535 exception);
cristy3ed852e2009-09-05 21:47:34 +00004536 dodge_image=DestroyImage(dodge_image);
4537 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4538 if (blend_image == (Image *) NULL)
4539 {
4540 sketch_image=DestroyImage(sketch_image);
4541 return((Image *) NULL);
4542 }
4543 (void) SetImageArtifact(blend_image,"compose:args","20x80");
cristye941a752011-10-15 01:52:48 +00004544 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0,
4545 exception);
cristy3ed852e2009-09-05 21:47:34 +00004546 blend_image=DestroyImage(blend_image);
4547 return(sketch_image);
4548}
4549
4550/*
4551%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4552% %
4553% %
4554% %
4555% S o l a r i z e I m a g e %
4556% %
4557% %
4558% %
4559%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4560%
4561% SolarizeImage() applies a special effect to the image, similar to the effect
4562% achieved in a photo darkroom by selectively exposing areas of photo
4563% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4564% measure of the extent of the solarization.
4565%
4566% The format of the SolarizeImage method is:
4567%
cristy5cbc0162011-08-29 00:36:28 +00004568% MagickBooleanType SolarizeImage(Image *image,const double threshold,
4569% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004570%
4571% A description of each parameter follows:
4572%
4573% o image: the image.
4574%
4575% o threshold: Define the extent of the solarization.
4576%
cristy5cbc0162011-08-29 00:36:28 +00004577% o exception: return any errors or warnings in this structure.
4578%
cristy3ed852e2009-09-05 21:47:34 +00004579*/
4580MagickExport MagickBooleanType SolarizeImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00004581 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004582{
4583#define SolarizeImageTag "Solarize/Image"
4584
cristyc4c8d132010-01-07 01:58:38 +00004585 CacheView
4586 *image_view;
4587
cristy3ed852e2009-09-05 21:47:34 +00004588 MagickBooleanType
4589 status;
4590
cristybb503372010-05-27 20:51:26 +00004591 MagickOffsetType
4592 progress;
4593
4594 ssize_t
4595 y;
4596
cristy3ed852e2009-09-05 21:47:34 +00004597 assert(image != (Image *) NULL);
4598 assert(image->signature == MagickSignature);
4599 if (image->debug != MagickFalse)
4600 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4601 if (image->storage_class == PseudoClass)
4602 {
cristybb503372010-05-27 20:51:26 +00004603 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004604 i;
4605
4606 /*
4607 Solarize colormap.
4608 */
cristybb503372010-05-27 20:51:26 +00004609 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004610 {
4611 if ((MagickRealType) image->colormap[i].red > threshold)
4612 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4613 if ((MagickRealType) image->colormap[i].green > threshold)
4614 image->colormap[i].green=(Quantum) QuantumRange-
4615 image->colormap[i].green;
4616 if ((MagickRealType) image->colormap[i].blue > threshold)
4617 image->colormap[i].blue=(Quantum) QuantumRange-
4618 image->colormap[i].blue;
4619 }
4620 }
4621 /*
4622 Solarize image.
4623 */
4624 status=MagickTrue;
4625 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00004626 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004627#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004628 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004629#endif
cristybb503372010-05-27 20:51:26 +00004630 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004631 {
cristybb503372010-05-27 20:51:26 +00004632 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004633 x;
4634
cristy4c08aed2011-07-01 19:47:50 +00004635 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004636 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004637
4638 if (status == MagickFalse)
4639 continue;
cristy5cbc0162011-08-29 00:36:28 +00004640 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00004641 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004642 {
4643 status=MagickFalse;
4644 continue;
4645 }
cristybb503372010-05-27 20:51:26 +00004646 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004647 {
cristy76f512e2011-09-12 01:26:56 +00004648 register ssize_t
4649 i;
4650
cristy10a6c612012-01-29 21:41:05 +00004651 if (GetPixelMask(image,q) != 0)
4652 {
4653 q+=GetPixelChannels(image);
4654 continue;
4655 }
cristy76f512e2011-09-12 01:26:56 +00004656 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4657 {
cristyabace412011-12-11 15:56:53 +00004658 PixelChannel
4659 channel;
4660
cristy76f512e2011-09-12 01:26:56 +00004661 PixelTrait
4662 traits;
4663
cristyabace412011-12-11 15:56:53 +00004664 channel=GetPixelChannelMapChannel(image,i);
4665 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004666 if ((traits == UndefinedPixelTrait) ||
4667 ((traits & CopyPixelTrait) != 0))
4668 continue;
4669 if ((MagickRealType) q[i] > threshold)
4670 q[i]=QuantumRange-q[i];
4671 }
cristyed231572011-07-14 02:18:59 +00004672 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004673 }
4674 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4675 status=MagickFalse;
4676 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4677 {
4678 MagickBooleanType
4679 proceed;
4680
cristyb5d5f722009-11-04 03:03:49 +00004681#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004682 #pragma omp critical (MagickCore_SolarizeImage)
cristy3ed852e2009-09-05 21:47:34 +00004683#endif
4684 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4685 if (proceed == MagickFalse)
4686 status=MagickFalse;
4687 }
4688 }
4689 image_view=DestroyCacheView(image_view);
4690 return(status);
4691}
4692
4693/*
4694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4695% %
4696% %
4697% %
4698% S t e g a n o I m a g e %
4699% %
4700% %
4701% %
4702%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4703%
4704% SteganoImage() hides a digital watermark within the image. Recover
4705% the hidden watermark later to prove that the authenticity of an image.
4706% Offset defines the start position within the image to hide the watermark.
4707%
4708% The format of the SteganoImage method is:
4709%
4710% Image *SteganoImage(const Image *image,Image *watermark,
4711% ExceptionInfo *exception)
4712%
4713% A description of each parameter follows:
4714%
4715% o image: the image.
4716%
4717% o watermark: the watermark image.
4718%
4719% o exception: return any errors or warnings in this structure.
4720%
4721*/
4722MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4723 ExceptionInfo *exception)
4724{
cristye1bf8ad2010-09-19 17:07:03 +00004725#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004726#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004727 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004728#define SteganoImageTag "Stegano/Image"
4729
cristyb0d3bb92010-09-22 14:37:58 +00004730 CacheView
4731 *stegano_view,
4732 *watermark_view;
4733
cristy3ed852e2009-09-05 21:47:34 +00004734 Image
4735 *stegano_image;
4736
4737 int
4738 c;
4739
cristy3ed852e2009-09-05 21:47:34 +00004740 MagickBooleanType
4741 status;
4742
cristy101ab702011-10-13 13:06:32 +00004743 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004744 pixel;
4745
cristy4c08aed2011-07-01 19:47:50 +00004746 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004747 *q;
4748
cristye1bf8ad2010-09-19 17:07:03 +00004749 register ssize_t
4750 x;
4751
cristybb503372010-05-27 20:51:26 +00004752 size_t
cristyeaedf062010-05-29 22:36:02 +00004753 depth,
4754 one;
cristy3ed852e2009-09-05 21:47:34 +00004755
cristye1bf8ad2010-09-19 17:07:03 +00004756 ssize_t
4757 i,
4758 j,
4759 k,
4760 y;
4761
cristy3ed852e2009-09-05 21:47:34 +00004762 /*
4763 Initialize steganographic image attributes.
4764 */
4765 assert(image != (const Image *) NULL);
4766 assert(image->signature == MagickSignature);
4767 if (image->debug != MagickFalse)
4768 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4769 assert(watermark != (const Image *) NULL);
4770 assert(watermark->signature == MagickSignature);
4771 assert(exception != (ExceptionInfo *) NULL);
4772 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004773 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004774 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4775 if (stegano_image == (Image *) NULL)
4776 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004777 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004778 {
cristy3ed852e2009-09-05 21:47:34 +00004779 stegano_image=DestroyImage(stegano_image);
4780 return((Image *) NULL);
4781 }
4782 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4783 /*
4784 Hide watermark in low-order bits of image.
4785 */
4786 c=0;
4787 i=0;
4788 j=0;
4789 depth=stegano_image->depth;
4790 k=image->offset;
cristyda16f162011-02-19 23:52:17 +00004791 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004792 watermark_view=AcquireCacheView(watermark);
4793 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004794 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004795 {
cristybb503372010-05-27 20:51:26 +00004796 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004797 {
cristybb503372010-05-27 20:51:26 +00004798 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004799 {
cristyda1f9c12011-10-02 21:39:49 +00004800 Quantum
cristy5f95f4f2011-10-23 01:01:01 +00004801 virtual_pixel[CompositePixelChannel];
cristyda1f9c12011-10-02 21:39:49 +00004802
cristy1707c6c2012-01-18 23:30:54 +00004803 ssize_t
4804 offset;
4805
cristyda1f9c12011-10-02 21:39:49 +00004806 (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,virtual_pixel,
4807 exception);
4808 pixel.red=(double) virtual_pixel[RedPixelChannel];
4809 pixel.green=(double) virtual_pixel[GreenPixelChannel];
4810 pixel.blue=(double) virtual_pixel[BluePixelChannel];
4811 pixel.alpha=(double) virtual_pixel[AlphaPixelChannel];
cristy1707c6c2012-01-18 23:30:54 +00004812 offset=k/(ssize_t) stegano_image->columns;
4813 if (offset >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004814 break;
cristyb0d3bb92010-09-22 14:37:58 +00004815 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4816 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4817 exception);
cristyacd2ed22011-08-30 01:44:23 +00004818 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004819 break;
4820 switch (c)
4821 {
4822 case 0:
4823 {
cristy4c08aed2011-07-01 19:47:50 +00004824 SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004825 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004826 break;
4827 }
4828 case 1:
4829 {
cristy4c08aed2011-07-01 19:47:50 +00004830 SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004831 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004832 break;
4833 }
4834 case 2:
4835 {
cristy4c08aed2011-07-01 19:47:50 +00004836 SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004837 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004838 break;
4839 }
4840 }
cristyb0d3bb92010-09-22 14:37:58 +00004841 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004842 break;
4843 c++;
4844 if (c == 3)
4845 c=0;
4846 k++;
cristybb503372010-05-27 20:51:26 +00004847 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004848 k=0;
4849 if (k == image->offset)
4850 j++;
4851 }
4852 }
cristy8b27a6d2010-02-14 03:31:15 +00004853 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004854 {
cristy8b27a6d2010-02-14 03:31:15 +00004855 MagickBooleanType
4856 proceed;
4857
4858 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4859 (depth-i),depth);
4860 if (proceed == MagickFalse)
4861 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004862 }
4863 }
cristyb0d3bb92010-09-22 14:37:58 +00004864 stegano_view=DestroyCacheView(stegano_view);
4865 watermark_view=DestroyCacheView(watermark_view);
cristy3ed852e2009-09-05 21:47:34 +00004866 if (stegano_image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00004867 (void) SyncImage(stegano_image,exception);
cristyda16f162011-02-19 23:52:17 +00004868 if (status == MagickFalse)
4869 {
4870 stegano_image=DestroyImage(stegano_image);
4871 return((Image *) NULL);
4872 }
cristy3ed852e2009-09-05 21:47:34 +00004873 return(stegano_image);
4874}
4875
4876/*
4877%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4878% %
4879% %
4880% %
4881% S t e r e o A n a g l y p h I m a g e %
4882% %
4883% %
4884% %
4885%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4886%
4887% StereoAnaglyphImage() combines two images and produces a single image that
4888% is the composite of a left and right image of a stereo pair. Special
4889% red-green stereo glasses are required to view this effect.
4890%
4891% The format of the StereoAnaglyphImage method is:
4892%
4893% Image *StereoImage(const Image *left_image,const Image *right_image,
4894% ExceptionInfo *exception)
4895% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004896% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004897% ExceptionInfo *exception)
4898%
4899% A description of each parameter follows:
4900%
4901% o left_image: the left image.
4902%
4903% o right_image: the right image.
4904%
4905% o exception: return any errors or warnings in this structure.
4906%
4907% o x_offset: amount, in pixels, by which the left image is offset to the
4908% right of the right image.
4909%
4910% o y_offset: amount, in pixels, by which the left image is offset to the
4911% bottom of the right image.
4912%
4913%
4914*/
4915MagickExport Image *StereoImage(const Image *left_image,
4916 const Image *right_image,ExceptionInfo *exception)
4917{
4918 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4919}
4920
4921MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004922 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004923 ExceptionInfo *exception)
4924{
4925#define StereoImageTag "Stereo/Image"
4926
4927 const Image
4928 *image;
4929
4930 Image
4931 *stereo_image;
4932
cristy3ed852e2009-09-05 21:47:34 +00004933 MagickBooleanType
4934 status;
4935
cristy9d314ff2011-03-09 01:30:28 +00004936 ssize_t
4937 y;
4938
cristy3ed852e2009-09-05 21:47:34 +00004939 assert(left_image != (const Image *) NULL);
4940 assert(left_image->signature == MagickSignature);
4941 if (left_image->debug != MagickFalse)
4942 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4943 left_image->filename);
4944 assert(right_image != (const Image *) NULL);
4945 assert(right_image->signature == MagickSignature);
4946 assert(exception != (ExceptionInfo *) NULL);
4947 assert(exception->signature == MagickSignature);
4948 assert(right_image != (const Image *) NULL);
4949 image=left_image;
4950 if ((left_image->columns != right_image->columns) ||
4951 (left_image->rows != right_image->rows))
4952 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4953 /*
4954 Initialize stereo image attributes.
4955 */
4956 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4957 MagickTrue,exception);
4958 if (stereo_image == (Image *) NULL)
4959 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004960 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004961 {
cristy3ed852e2009-09-05 21:47:34 +00004962 stereo_image=DestroyImage(stereo_image);
4963 return((Image *) NULL);
4964 }
4965 /*
4966 Copy left image to red channel and right image to blue channel.
4967 */
cristyda16f162011-02-19 23:52:17 +00004968 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004969 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004970 {
cristy4c08aed2011-07-01 19:47:50 +00004971 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004972 *restrict p,
4973 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004974
cristybb503372010-05-27 20:51:26 +00004975 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004976 x;
4977
cristy4c08aed2011-07-01 19:47:50 +00004978 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004979 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004980
4981 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4982 exception);
4983 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4984 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy76f512e2011-09-12 01:26:56 +00004985 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
4986 (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004987 break;
cristybb503372010-05-27 20:51:26 +00004988 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004989 {
cristy4c08aed2011-07-01 19:47:50 +00004990 SetPixelRed(image,GetPixelRed(left_image,p),r);
cristy76f512e2011-09-12 01:26:56 +00004991 SetPixelGreen(image,GetPixelGreen(right_image,q),r);
4992 SetPixelBlue(image,GetPixelBlue(right_image,q),r);
4993 if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
4994 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4995 GetPixelAlpha(right_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00004996 p+=GetPixelChannels(left_image);
cristy76f512e2011-09-12 01:26:56 +00004997 q+=GetPixelChannels(right_image);
4998 r+=GetPixelChannels(stereo_image);
cristy3ed852e2009-09-05 21:47:34 +00004999 }
5000 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
5001 break;
cristy8b27a6d2010-02-14 03:31:15 +00005002 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005003 {
cristy8b27a6d2010-02-14 03:31:15 +00005004 MagickBooleanType
5005 proceed;
5006
cristybb503372010-05-27 20:51:26 +00005007 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
5008 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00005009 if (proceed == MagickFalse)
5010 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005011 }
5012 }
cristyda16f162011-02-19 23:52:17 +00005013 if (status == MagickFalse)
5014 {
5015 stereo_image=DestroyImage(stereo_image);
5016 return((Image *) NULL);
5017 }
cristy3ed852e2009-09-05 21:47:34 +00005018 return(stereo_image);
5019}
5020
5021/*
5022%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5023% %
5024% %
5025% %
5026% S w i r l I m a g e %
5027% %
5028% %
5029% %
5030%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5031%
5032% SwirlImage() swirls the pixels about the center of the image, where
5033% degrees indicates the sweep of the arc through which each pixel is moved.
5034% You get a more dramatic effect as the degrees move from 1 to 360.
5035%
5036% The format of the SwirlImage method is:
5037%
5038% Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005039% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005040%
5041% A description of each parameter follows:
5042%
5043% o image: the image.
5044%
5045% o degrees: Define the tightness of the swirling effect.
5046%
cristy76f512e2011-09-12 01:26:56 +00005047% o method: the pixel interpolation method.
5048%
cristy3ed852e2009-09-05 21:47:34 +00005049% o exception: return any errors or warnings in this structure.
5050%
5051*/
5052MagickExport Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005053 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005054{
5055#define SwirlImageTag "Swirl/Image"
5056
cristyfa112112010-01-04 17:48:07 +00005057 CacheView
5058 *image_view,
5059 *swirl_view;
5060
cristy3ed852e2009-09-05 21:47:34 +00005061 Image
5062 *swirl_image;
5063
cristy3ed852e2009-09-05 21:47:34 +00005064 MagickBooleanType
5065 status;
5066
cristybb503372010-05-27 20:51:26 +00005067 MagickOffsetType
5068 progress;
5069
cristy3ed852e2009-09-05 21:47:34 +00005070 MagickRealType
5071 radius;
5072
5073 PointInfo
5074 center,
5075 scale;
5076
cristybb503372010-05-27 20:51:26 +00005077 ssize_t
5078 y;
5079
cristy3ed852e2009-09-05 21:47:34 +00005080 /*
5081 Initialize swirl image attributes.
5082 */
5083 assert(image != (const Image *) NULL);
5084 assert(image->signature == MagickSignature);
5085 if (image->debug != MagickFalse)
5086 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5087 assert(exception != (ExceptionInfo *) NULL);
5088 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00005089 swirl_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005090 if (swirl_image == (Image *) NULL)
5091 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005092 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005093 {
cristy3ed852e2009-09-05 21:47:34 +00005094 swirl_image=DestroyImage(swirl_image);
5095 return((Image *) NULL);
5096 }
cristy4c08aed2011-07-01 19:47:50 +00005097 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005098 swirl_image->matte=MagickTrue;
5099 /*
5100 Compute scaling factor.
5101 */
5102 center.x=(double) image->columns/2.0;
5103 center.y=(double) image->rows/2.0;
5104 radius=MagickMax(center.x,center.y);
5105 scale.x=1.0;
5106 scale.y=1.0;
5107 if (image->columns > image->rows)
5108 scale.y=(double) image->columns/(double) image->rows;
5109 else
5110 if (image->columns < image->rows)
5111 scale.x=(double) image->rows/(double) image->columns;
5112 degrees=(double) DegreesToRadians(degrees);
5113 /*
5114 Swirl image.
5115 */
5116 status=MagickTrue;
5117 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00005118 image_view=AcquireCacheView(image);
5119 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005120#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005121 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005122#endif
cristybb503372010-05-27 20:51:26 +00005123 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005124 {
cristy3ed852e2009-09-05 21:47:34 +00005125 MagickRealType
5126 distance;
5127
5128 PointInfo
5129 delta;
5130
cristy6d188022011-09-12 13:23:33 +00005131 register const Quantum
5132 *restrict p;
5133
cristybb503372010-05-27 20:51:26 +00005134 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005135 x;
5136
cristy4c08aed2011-07-01 19:47:50 +00005137 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005138 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005139
5140 if (status == MagickFalse)
5141 continue;
cristy6d188022011-09-12 13:23:33 +00005142 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00005143 q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00005144 exception);
cristy6d188022011-09-12 13:23:33 +00005145 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005146 {
5147 status=MagickFalse;
5148 continue;
5149 }
cristy3ed852e2009-09-05 21:47:34 +00005150 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00005151 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005152 {
5153 /*
5154 Determine if the pixel is within an ellipse.
5155 */
cristy10a6c612012-01-29 21:41:05 +00005156 if (GetPixelMask(image,p) != 0)
5157 {
5158 p+=GetPixelChannels(image);
5159 q+=GetPixelChannels(swirl_image);
5160 continue;
5161 }
cristy3ed852e2009-09-05 21:47:34 +00005162 delta.x=scale.x*(double) (x-center.x);
5163 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00005164 if (distance >= (radius*radius))
5165 {
cristy1707c6c2012-01-18 23:30:54 +00005166 register ssize_t
5167 i;
5168
cristy6d188022011-09-12 13:23:33 +00005169 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy1707c6c2012-01-18 23:30:54 +00005170 {
5171 PixelChannel
5172 channel;
5173
5174 PixelTrait
5175 swirl_traits,
5176 traits;
5177
5178 channel=GetPixelChannelMapChannel(image,i);
5179 traits=GetPixelChannelMapTraits(image,channel);
5180 swirl_traits=GetPixelChannelMapTraits(swirl_image,channel);
5181 if ((traits == UndefinedPixelTrait) ||
5182 (swirl_traits == UndefinedPixelTrait))
5183 continue;
5184 SetPixelChannel(swirl_image,channel,p[i],q);
5185 }
cristy6d188022011-09-12 13:23:33 +00005186 }
5187 else
cristy3ed852e2009-09-05 21:47:34 +00005188 {
5189 MagickRealType
5190 cosine,
5191 factor,
5192 sine;
5193
5194 /*
5195 Swirl the pixel.
5196 */
5197 factor=1.0-sqrt((double) distance)/radius;
5198 sine=sin((double) (degrees*factor*factor));
5199 cosine=cos((double) (degrees*factor*factor));
cristy76f512e2011-09-12 01:26:56 +00005200 status=InterpolatePixelChannels(image,image_view,swirl_image,method,
5201 ((cosine*delta.x-sine*delta.y)/scale.x+center.x),(double)
5202 ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00005203 }
cristy6d188022011-09-12 13:23:33 +00005204 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00005205 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00005206 }
5207 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5208 status=MagickFalse;
5209 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5210 {
5211 MagickBooleanType
5212 proceed;
5213
cristyb5d5f722009-11-04 03:03:49 +00005214#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005215 #pragma omp critical (MagickCore_SwirlImage)
cristy3ed852e2009-09-05 21:47:34 +00005216#endif
5217 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5218 if (proceed == MagickFalse)
5219 status=MagickFalse;
5220 }
5221 }
5222 swirl_view=DestroyCacheView(swirl_view);
5223 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005224 if (status == MagickFalse)
5225 swirl_image=DestroyImage(swirl_image);
5226 return(swirl_image);
5227}
5228
5229/*
5230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5231% %
5232% %
5233% %
5234% T i n t I m a g e %
5235% %
5236% %
5237% %
5238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5239%
5240% TintImage() applies a color vector to each pixel in the image. The length
5241% of the vector is 0 for black and white and at its maximum for the midtones.
5242% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5243%
5244% The format of the TintImage method is:
5245%
cristyb817c3f2011-10-03 14:00:35 +00005246% Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005247% const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005248%
5249% A description of each parameter follows:
5250%
5251% o image: the image.
5252%
cristyb817c3f2011-10-03 14:00:35 +00005253% o blend: A color value used for tinting.
cristy3ed852e2009-09-05 21:47:34 +00005254%
5255% o tint: A color value used for tinting.
5256%
5257% o exception: return any errors or warnings in this structure.
5258%
5259*/
cristyb817c3f2011-10-03 14:00:35 +00005260MagickExport Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005261 const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005262{
5263#define TintImageTag "Tint/Image"
5264
cristyc4c8d132010-01-07 01:58:38 +00005265 CacheView
5266 *image_view,
5267 *tint_view;
5268
cristy3ed852e2009-09-05 21:47:34 +00005269 GeometryInfo
5270 geometry_info;
5271
5272 Image
5273 *tint_image;
5274
cristy3ed852e2009-09-05 21:47:34 +00005275 MagickBooleanType
5276 status;
5277
cristybb503372010-05-27 20:51:26 +00005278 MagickOffsetType
5279 progress;
cristy3ed852e2009-09-05 21:47:34 +00005280
cristy28474bf2011-09-11 23:32:52 +00005281 MagickRealType
5282 intensity;
5283
cristy4c08aed2011-07-01 19:47:50 +00005284 PixelInfo
cristy1707c6c2012-01-18 23:30:54 +00005285 color_vector;
cristy3ed852e2009-09-05 21:47:34 +00005286
cristybb503372010-05-27 20:51:26 +00005287 MagickStatusType
5288 flags;
5289
5290 ssize_t
5291 y;
5292
cristy3ed852e2009-09-05 21:47:34 +00005293 /*
5294 Allocate tint image.
5295 */
5296 assert(image != (const Image *) NULL);
5297 assert(image->signature == MagickSignature);
5298 if (image->debug != MagickFalse)
5299 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5300 assert(exception != (ExceptionInfo *) NULL);
5301 assert(exception->signature == MagickSignature);
5302 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5303 if (tint_image == (Image *) NULL)
5304 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005305 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005306 {
cristy3ed852e2009-09-05 21:47:34 +00005307 tint_image=DestroyImage(tint_image);
5308 return((Image *) NULL);
5309 }
cristyaed9c382011-10-03 17:54:21 +00005310 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005311 return(tint_image);
5312 /*
5313 Determine RGB values of the color.
5314 */
cristy1707c6c2012-01-18 23:30:54 +00005315 GetPixelInfo(image,&color_vector);
cristyb817c3f2011-10-03 14:00:35 +00005316 flags=ParseGeometry(blend,&geometry_info);
cristy1707c6c2012-01-18 23:30:54 +00005317 color_vector.red=geometry_info.rho;
5318 color_vector.green=geometry_info.rho;
5319 color_vector.blue=geometry_info.rho;
5320 color_vector.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005321 if ((flags & SigmaValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005322 color_vector.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00005323 if ((flags & XiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005324 color_vector.blue=geometry_info.xi;
cristyb817c3f2011-10-03 14:00:35 +00005325 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005326 color_vector.alpha=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005327 if (image->colorspace == CMYKColorspace)
5328 {
cristy1707c6c2012-01-18 23:30:54 +00005329 color_vector.black=geometry_info.rho;
cristy76f512e2011-09-12 01:26:56 +00005330 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005331 color_vector.black=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005332 if ((flags & ChiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005333 color_vector.alpha=geometry_info.chi;
cristy76f512e2011-09-12 01:26:56 +00005334 }
cristy28474bf2011-09-11 23:32:52 +00005335 intensity=(MagickRealType) GetPixelInfoIntensity(tint);
cristy1707c6c2012-01-18 23:30:54 +00005336 color_vector.red=(MagickRealType) (color_vector.red*tint->red/100.0-
5337 intensity);
5338 color_vector.green=(MagickRealType) (color_vector.green*tint->green/100.0-
5339 intensity);
5340 color_vector.blue=(MagickRealType) (color_vector.blue*tint->blue/100.0-
5341 intensity);
5342 color_vector.black=(MagickRealType) (color_vector.black*tint->black/100.0-
5343 intensity);
5344 color_vector.alpha=(MagickRealType) (color_vector.alpha*tint->alpha/100.0-
5345 intensity);
cristy3ed852e2009-09-05 21:47:34 +00005346 /*
5347 Tint image.
5348 */
5349 status=MagickTrue;
5350 progress=0;
5351 image_view=AcquireCacheView(image);
5352 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005353#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005354 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005355#endif
cristybb503372010-05-27 20:51:26 +00005356 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005357 {
cristy4c08aed2011-07-01 19:47:50 +00005358 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005359 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005360
cristy4c08aed2011-07-01 19:47:50 +00005361 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005362 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005363
cristy6b91acb2011-04-19 12:23:54 +00005364 register ssize_t
5365 x;
5366
cristy3ed852e2009-09-05 21:47:34 +00005367 if (status == MagickFalse)
5368 continue;
5369 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5370 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5371 exception);
cristy4c08aed2011-07-01 19:47:50 +00005372 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005373 {
5374 status=MagickFalse;
5375 continue;
5376 }
cristybb503372010-05-27 20:51:26 +00005377 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005378 {
cristy4c08aed2011-07-01 19:47:50 +00005379 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005380 pixel;
5381
5382 MagickRealType
5383 weight;
5384
cristy1707c6c2012-01-18 23:30:54 +00005385 register ssize_t
5386 i;
5387
cristy10a6c612012-01-29 21:41:05 +00005388 if (GetPixelMask(image,p) != 0)
5389 {
5390 p+=GetPixelChannels(image);
5391 q+=GetPixelChannels(tint_image);
5392 continue;
5393 }
cristy1707c6c2012-01-18 23:30:54 +00005394 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
5395 {
5396 PixelChannel
5397 channel;
5398
5399 PixelTrait
5400 tint_traits,
5401 traits;
5402
5403 channel=GetPixelChannelMapChannel(image,i);
5404 traits=GetPixelChannelMapTraits(image,channel);
5405 tint_traits=GetPixelChannelMapTraits(tint_image,channel);
5406 if ((traits == UndefinedPixelTrait) ||
5407 (tint_traits == UndefinedPixelTrait))
5408 continue;
5409 if ((tint_traits & CopyPixelTrait) != 0)
5410 {
5411 SetPixelChannel(tint_image,channel,p[i],q);
5412 continue;
5413 }
5414 }
5415 GetPixelInfo(image,&pixel);
5416 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5417 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5418 (1.0-(4.0*(weight*weight)));
5419 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5420 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5421 (1.0-(4.0*(weight*weight)));
5422 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5423 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5424 (1.0-(4.0*(weight*weight)));
5425 weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5426 pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5427 (1.0-(4.0*(weight*weight)));
5428 SetPixelInfoPixel(tint_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005429 p+=GetPixelChannels(image);
5430 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005431 }
5432 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5433 status=MagickFalse;
5434 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5435 {
5436 MagickBooleanType
5437 proceed;
5438
cristyb5d5f722009-11-04 03:03:49 +00005439#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005440 #pragma omp critical (MagickCore_TintImage)
cristy3ed852e2009-09-05 21:47:34 +00005441#endif
5442 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5443 if (proceed == MagickFalse)
5444 status=MagickFalse;
5445 }
5446 }
5447 tint_view=DestroyCacheView(tint_view);
5448 image_view=DestroyCacheView(image_view);
5449 if (status == MagickFalse)
5450 tint_image=DestroyImage(tint_image);
5451 return(tint_image);
5452}
5453
5454/*
5455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5456% %
5457% %
5458% %
5459% V i g n e t t e I m a g e %
5460% %
5461% %
5462% %
5463%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5464%
5465% VignetteImage() softens the edges of the image in vignette style.
5466%
5467% The format of the VignetteImage method is:
5468%
5469% Image *VignetteImage(const Image *image,const double radius,
cristyeb6e6582011-12-09 09:14:23 +00005470% const double sigma,const double bias,const ssize_t x,const ssize_t y,
cristy05c0c9a2011-09-05 23:16:13 +00005471% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005472%
5473% A description of each parameter follows:
5474%
5475% o image: the image.
5476%
5477% o radius: the radius of the pixel neighborhood.
5478%
5479% o sigma: the standard deviation of the Gaussian, in pixels.
5480%
cristyeb6e6582011-12-09 09:14:23 +00005481% o bias: the bias.
5482%
cristy3ed852e2009-09-05 21:47:34 +00005483% o x, y: Define the x and y ellipse offset.
5484%
5485% o exception: return any errors or warnings in this structure.
5486%
5487*/
5488MagickExport Image *VignetteImage(const Image *image,const double radius,
cristyeb6e6582011-12-09 09:14:23 +00005489 const double sigma,const double bias,const ssize_t x,const ssize_t y,
5490 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005491{
5492 char
5493 ellipse[MaxTextExtent];
5494
5495 DrawInfo
5496 *draw_info;
5497
5498 Image
5499 *canvas_image,
5500 *blur_image,
5501 *oval_image,
5502 *vignette_image;
5503
5504 assert(image != (Image *) NULL);
5505 assert(image->signature == MagickSignature);
5506 if (image->debug != MagickFalse)
5507 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5508 assert(exception != (ExceptionInfo *) NULL);
5509 assert(exception->signature == MagickSignature);
5510 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5511 if (canvas_image == (Image *) NULL)
5512 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005513 if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005514 {
cristy3ed852e2009-09-05 21:47:34 +00005515 canvas_image=DestroyImage(canvas_image);
5516 return((Image *) NULL);
5517 }
5518 canvas_image->matte=MagickTrue;
cristy98621462011-12-31 22:31:11 +00005519 oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
5520 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005521 if (oval_image == (Image *) NULL)
5522 {
5523 canvas_image=DestroyImage(canvas_image);
5524 return((Image *) NULL);
5525 }
cristy9950d572011-10-01 18:22:35 +00005526 (void) QueryColorCompliance("#000000",AllCompliance,
5527 &oval_image->background_color,exception);
cristyea1a8aa2011-10-20 13:24:06 +00005528 (void) SetImageBackgroundColor(oval_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005529 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00005530 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5531 exception);
5532 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5533 exception);
cristy1707c6c2012-01-18 23:30:54 +00005534 (void) FormatLocaleString(ellipse,MaxTextExtent,"ellipse %g,%g,%g,%g,"
5535 "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5536 image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005537 draw_info->primitive=AcquireString(ellipse);
cristy018f07f2011-09-04 21:15:19 +00005538 (void) DrawImage(oval_image,draw_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005539 draw_info=DestroyDrawInfo(draw_info);
cristyeb6e6582011-12-09 09:14:23 +00005540 blur_image=BlurImage(oval_image,radius,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00005541 oval_image=DestroyImage(oval_image);
5542 if (blur_image == (Image *) NULL)
5543 {
5544 canvas_image=DestroyImage(canvas_image);
5545 return((Image *) NULL);
5546 }
5547 blur_image->matte=MagickFalse;
cristy98621462011-12-31 22:31:11 +00005548 (void) CompositeImage(canvas_image,IntensityCompositeOp,blur_image,0,0,
cristye941a752011-10-15 01:52:48 +00005549 exception);
cristy3ed852e2009-09-05 21:47:34 +00005550 blur_image=DestroyImage(blur_image);
5551 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5552 canvas_image=DestroyImage(canvas_image);
5553 return(vignette_image);
5554}
5555
5556/*
5557%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5558% %
5559% %
5560% %
5561% W a v e I m a g e %
5562% %
5563% %
5564% %
5565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5566%
5567% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005568% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005569% by the given parameters.
5570%
5571% The format of the WaveImage method is:
5572%
5573% Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005574% const double wave_length,const PixelInterpolateMethod method,
5575% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005576%
5577% A description of each parameter follows:
5578%
5579% o image: the image.
5580%
5581% o amplitude, wave_length: Define the amplitude and wave length of the
5582% sine wave.
5583%
cristy5c4e2582011-09-11 19:21:03 +00005584% o interpolate: the pixel interpolation method.
5585%
cristy3ed852e2009-09-05 21:47:34 +00005586% o exception: return any errors or warnings in this structure.
5587%
5588*/
5589MagickExport Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005590 const double wave_length,const PixelInterpolateMethod method,
5591 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005592{
5593#define WaveImageTag "Wave/Image"
5594
cristyfa112112010-01-04 17:48:07 +00005595 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005596 *image_view,
cristyfa112112010-01-04 17:48:07 +00005597 *wave_view;
5598
cristy3ed852e2009-09-05 21:47:34 +00005599 Image
5600 *wave_image;
5601
cristy3ed852e2009-09-05 21:47:34 +00005602 MagickBooleanType
5603 status;
5604
cristybb503372010-05-27 20:51:26 +00005605 MagickOffsetType
5606 progress;
5607
cristy3ed852e2009-09-05 21:47:34 +00005608 MagickRealType
5609 *sine_map;
5610
cristybb503372010-05-27 20:51:26 +00005611 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005612 i;
5613
cristybb503372010-05-27 20:51:26 +00005614 ssize_t
5615 y;
5616
cristy3ed852e2009-09-05 21:47:34 +00005617 /*
5618 Initialize wave image attributes.
5619 */
5620 assert(image != (Image *) NULL);
5621 assert(image->signature == MagickSignature);
5622 if (image->debug != MagickFalse)
5623 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5624 assert(exception != (ExceptionInfo *) NULL);
5625 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005626 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005627 fabs(amplitude)),MagickTrue,exception);
5628 if (wave_image == (Image *) NULL)
5629 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005630 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005631 {
cristy3ed852e2009-09-05 21:47:34 +00005632 wave_image=DestroyImage(wave_image);
5633 return((Image *) NULL);
5634 }
cristy4c08aed2011-07-01 19:47:50 +00005635 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005636 wave_image->matte=MagickTrue;
5637 /*
5638 Allocate sine map.
5639 */
5640 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5641 sizeof(*sine_map));
5642 if (sine_map == (MagickRealType *) NULL)
5643 {
5644 wave_image=DestroyImage(wave_image);
5645 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5646 }
cristybb503372010-05-27 20:51:26 +00005647 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005648 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5649 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005650 /*
5651 Wave image.
5652 */
5653 status=MagickTrue;
5654 progress=0;
cristyd76c51e2011-03-26 00:21:26 +00005655 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005656 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005657 (void) SetCacheViewVirtualPixelMethod(image_view,
5658 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005659#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005660 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005661#endif
cristybb503372010-05-27 20:51:26 +00005662 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005663 {
cristy4c08aed2011-07-01 19:47:50 +00005664 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005665 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005666
cristye97bb922011-04-03 01:36:52 +00005667 register ssize_t
5668 x;
5669
cristy3ed852e2009-09-05 21:47:34 +00005670 if (status == MagickFalse)
5671 continue;
5672 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5673 exception);
cristyacd2ed22011-08-30 01:44:23 +00005674 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005675 {
5676 status=MagickFalse;
5677 continue;
5678 }
cristybb503372010-05-27 20:51:26 +00005679 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005680 {
cristy5c4e2582011-09-11 19:21:03 +00005681 status=InterpolatePixelChannels(image,image_view,wave_image,method,
5682 (double) x,(double) (y-sine_map[x]),q,exception);
cristyed231572011-07-14 02:18:59 +00005683 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005684 }
5685 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5686 status=MagickFalse;
5687 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5688 {
5689 MagickBooleanType
5690 proceed;
5691
cristyb5d5f722009-11-04 03:03:49 +00005692#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005693 #pragma omp critical (MagickCore_WaveImage)
cristy3ed852e2009-09-05 21:47:34 +00005694#endif
5695 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5696 if (proceed == MagickFalse)
5697 status=MagickFalse;
5698 }
5699 }
5700 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005701 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005702 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5703 if (status == MagickFalse)
5704 wave_image=DestroyImage(wave_image);
5705 return(wave_image);
5706}