blob: 860900813f8b7a8d8b464953ef3ca9275cb958c3 [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 }
cristyc7e6ff62011-10-03 13:46:11 +0000691 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000692 return(colorize_image);
693 /*
anthonyfd706f92012-01-19 04:22:02 +0000694 Determine RGB values of the fill color for pixel
cristy3ed852e2009-09-05 21:47:34 +0000695 */
cristyc7e6ff62011-10-03 13:46:11 +0000696 GetPixelInfo(image,&pixel);
cristyb817c3f2011-10-03 14:00:35 +0000697 flags=ParseGeometry(blend,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000698 pixel.red=geometry_info.rho;
699 pixel.green=geometry_info.rho;
700 pixel.blue=geometry_info.rho;
cristyc7e6ff62011-10-03 13:46:11 +0000701 pixel.alpha=100.0;
cristy3ed852e2009-09-05 21:47:34 +0000702 if ((flags & SigmaValue) != 0)
703 pixel.green=geometry_info.sigma;
704 if ((flags & XiValue) != 0)
705 pixel.blue=geometry_info.xi;
706 if ((flags & PsiValue) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000707 pixel.alpha=geometry_info.psi;
cristyc7e6ff62011-10-03 13:46:11 +0000708 if (pixel.colorspace == CMYKColorspace)
709 {
710 pixel.black=geometry_info.rho;
711 if ((flags & PsiValue) != 0)
712 pixel.black=geometry_info.psi;
713 if ((flags & ChiValue) != 0)
714 pixel.alpha=geometry_info.chi;
715 }
cristy3ed852e2009-09-05 21:47:34 +0000716 /*
717 Colorize DirectClass image.
718 */
719 status=MagickTrue;
720 progress=0;
721 image_view=AcquireCacheView(image);
722 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000723#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000724 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000725#endif
cristybb503372010-05-27 20:51:26 +0000726 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000727 {
728 MagickBooleanType
729 sync;
730
cristy4c08aed2011-07-01 19:47:50 +0000731 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000732 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000733
cristybb503372010-05-27 20:51:26 +0000734 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000735 x;
736
cristy4c08aed2011-07-01 19:47:50 +0000737 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000738 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000739
740 if (status == MagickFalse)
741 continue;
742 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
743 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
744 exception);
cristy4c08aed2011-07-01 19:47:50 +0000745 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000746 {
747 status=MagickFalse;
748 continue;
749 }
cristybb503372010-05-27 20:51:26 +0000750 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000751 {
cristyc7e6ff62011-10-03 13:46:11 +0000752 register ssize_t
753 i;
754
cristy10a6c612012-01-29 21:41:05 +0000755 if (GetPixelMask(image,p) != 0)
756 {
757 p+=GetPixelChannels(image);
758 q+=GetPixelChannels(colorize_image);
759 continue;
760 }
cristyc7e6ff62011-10-03 13:46:11 +0000761 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
762 {
763 PixelChannel
764 channel;
765
766 PixelTrait
767 colorize_traits,
768 traits;
769
cristye2a912b2011-12-05 20:02:07 +0000770 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000771 traits=GetPixelChannelMapTraits(image,channel);
cristyc7e6ff62011-10-03 13:46:11 +0000772 colorize_traits=GetPixelChannelMapTraits(colorize_image,channel);
773 if ((traits == UndefinedPixelTrait) ||
774 (colorize_traits == UndefinedPixelTrait))
775 continue;
776 if ((colorize_traits & CopyPixelTrait) != 0)
777 {
778 SetPixelChannel(colorize_image,channel,p[i],q);
779 continue;
780 }
781 switch (channel)
782 {
783 case RedPixelChannel:
784 {
785 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
786 (100.0-pixel.red)+colorize->red*pixel.red)/100.0),q);
787 break;
788 }
789 case GreenPixelChannel:
790 {
791 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
792 (100.0-pixel.green)+colorize->green*pixel.green)/100.0),q);
793 break;
794 }
795 case BluePixelChannel:
796 {
797 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
798 (100.0-pixel.blue)+colorize->blue*pixel.blue)/100.0),q);
799 break;
800 }
801 case BlackPixelChannel:
802 {
803 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
804 (100.0-pixel.black)+colorize->black*pixel.black)/100.0),q);
805 break;
806 }
807 case AlphaPixelChannel:
808 {
809 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
810 (100.0-pixel.alpha)+colorize->alpha*pixel.alpha)/100.0),q);
811 break;
812 }
813 default:
814 {
815 SetPixelChannel(colorize_image,channel,p[i],q);
816 break;
817 }
818 }
819 }
cristyed231572011-07-14 02:18:59 +0000820 p+=GetPixelChannels(image);
821 q+=GetPixelChannels(colorize_image);
cristy3ed852e2009-09-05 21:47:34 +0000822 }
823 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
824 if (sync == MagickFalse)
825 status=MagickFalse;
826 if (image->progress_monitor != (MagickProgressMonitor) NULL)
827 {
828 MagickBooleanType
829 proceed;
830
cristy319a1e72010-02-21 15:13:11 +0000831#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000832 #pragma omp critical (MagickCore_ColorizeImage)
cristy3ed852e2009-09-05 21:47:34 +0000833#endif
834 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
835 if (proceed == MagickFalse)
836 status=MagickFalse;
837 }
838 }
839 image_view=DestroyCacheView(image_view);
840 colorize_view=DestroyCacheView(colorize_view);
841 if (status == MagickFalse)
842 colorize_image=DestroyImage(colorize_image);
843 return(colorize_image);
844}
845
846/*
847%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
848% %
849% %
850% %
cristye6365592010-04-02 17:31:23 +0000851% C o l o r M a t r i x I m a g e %
852% %
853% %
854% %
855%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
856%
857% ColorMatrixImage() applies color transformation to an image. This method
858% permits saturation changes, hue rotation, luminance to alpha, and various
859% other effects. Although variable-sized transformation matrices can be used,
860% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
861% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
862% except offsets are in column 6 rather than 5 (in support of CMYKA images)
863% and offsets are normalized (divide Flash offset by 255).
864%
865% The format of the ColorMatrixImage method is:
866%
867% Image *ColorMatrixImage(const Image *image,
868% const KernelInfo *color_matrix,ExceptionInfo *exception)
869%
870% A description of each parameter follows:
871%
872% o image: the image.
873%
874% o color_matrix: the color matrix.
875%
876% o exception: return any errors or warnings in this structure.
877%
878*/
anthonyfd706f92012-01-19 04:22:02 +0000879/* FUTURE: modify to make use of a MagickMatrix Mutliply function
880 That should be provided in "matrix.c"
881 (ASIDE: actually distorts should do this too but currently doesn't)
882*/
883
cristye6365592010-04-02 17:31:23 +0000884MagickExport Image *ColorMatrixImage(const Image *image,
885 const KernelInfo *color_matrix,ExceptionInfo *exception)
886{
887#define ColorMatrixImageTag "ColorMatrix/Image"
888
889 CacheView
890 *color_view,
891 *image_view;
892
893 double
894 ColorMatrix[6][6] =
895 {
896 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
897 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
898 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
899 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
900 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
901 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
902 };
903
904 Image
905 *color_image;
906
cristye6365592010-04-02 17:31:23 +0000907 MagickBooleanType
908 status;
909
cristybb503372010-05-27 20:51:26 +0000910 MagickOffsetType
911 progress;
912
913 register ssize_t
cristye6365592010-04-02 17:31:23 +0000914 i;
915
cristybb503372010-05-27 20:51:26 +0000916 ssize_t
917 u,
918 v,
919 y;
920
cristye6365592010-04-02 17:31:23 +0000921 /*
anthonyfd706f92012-01-19 04:22:02 +0000922 Map given color_matrix, into a 6x6 matrix RGBKA and a constant
cristye6365592010-04-02 17:31:23 +0000923 */
924 assert(image != (Image *) NULL);
925 assert(image->signature == MagickSignature);
926 if (image->debug != MagickFalse)
927 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
928 assert(exception != (ExceptionInfo *) NULL);
929 assert(exception->signature == MagickSignature);
930 i=0;
cristybb503372010-05-27 20:51:26 +0000931 for (v=0; v < (ssize_t) color_matrix->height; v++)
932 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000933 {
934 if ((v < 6) && (u < 6))
935 ColorMatrix[v][u]=color_matrix->values[i];
936 i++;
937 }
938 /*
939 Initialize color image.
940 */
cristy12550e62010-06-07 12:46:40 +0000941 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000942 if (color_image == (Image *) NULL)
943 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000944 if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
cristye6365592010-04-02 17:31:23 +0000945 {
cristye6365592010-04-02 17:31:23 +0000946 color_image=DestroyImage(color_image);
947 return((Image *) NULL);
948 }
949 if (image->debug != MagickFalse)
950 {
951 char
952 format[MaxTextExtent],
953 *message;
954
955 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
956 " ColorMatrix image with color matrix:");
957 message=AcquireString("");
958 for (v=0; v < 6; v++)
959 {
960 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000961 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000962 (void) ConcatenateString(&message,format);
963 for (u=0; u < 6; u++)
964 {
cristyb51dff52011-05-19 16:55:47 +0000965 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
cristye6365592010-04-02 17:31:23 +0000966 ColorMatrix[v][u]);
967 (void) ConcatenateString(&message,format);
968 }
969 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
970 }
971 message=DestroyString(message);
972 }
973 /*
anthonyfd706f92012-01-19 04:22:02 +0000974 Apply the ColorMatrix to image.
cristye6365592010-04-02 17:31:23 +0000975 */
976 status=MagickTrue;
977 progress=0;
978 image_view=AcquireCacheView(image);
979 color_view=AcquireCacheView(color_image);
980#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000981 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye6365592010-04-02 17:31:23 +0000982#endif
cristybb503372010-05-27 20:51:26 +0000983 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000984 {
985 MagickRealType
986 pixel;
987
cristy4c08aed2011-07-01 19:47:50 +0000988 register const Quantum
cristye6365592010-04-02 17:31:23 +0000989 *restrict p;
990
cristy4c08aed2011-07-01 19:47:50 +0000991 register Quantum
992 *restrict q;
993
cristybb503372010-05-27 20:51:26 +0000994 register ssize_t
cristye6365592010-04-02 17:31:23 +0000995 x;
996
cristye6365592010-04-02 17:31:23 +0000997 if (status == MagickFalse)
998 continue;
999 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1000 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
1001 exception);
cristy4c08aed2011-07-01 19:47:50 +00001002 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristye6365592010-04-02 17:31:23 +00001003 {
1004 status=MagickFalse;
1005 continue;
1006 }
cristybb503372010-05-27 20:51:26 +00001007 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +00001008 {
cristybb503372010-05-27 20:51:26 +00001009 register ssize_t
cristye6365592010-04-02 17:31:23 +00001010 v;
1011
cristybb503372010-05-27 20:51:26 +00001012 size_t
cristye6365592010-04-02 17:31:23 +00001013 height;
1014
1015 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +00001016 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +00001017 {
cristy4c08aed2011-07-01 19:47:50 +00001018 pixel=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
1019 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +00001020 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001021 pixel+=ColorMatrix[v][3]*GetPixelBlack(image,p);
1022 if (image->matte != MagickFalse)
1023 pixel+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
cristye6365592010-04-02 17:31:23 +00001024 pixel+=QuantumRange*ColorMatrix[v][5];
1025 switch (v)
1026 {
cristy4c08aed2011-07-01 19:47:50 +00001027 case 0: SetPixelRed(color_image,ClampToQuantum(pixel),q); break;
1028 case 1: SetPixelGreen(color_image,ClampToQuantum(pixel),q); break;
1029 case 2: SetPixelBlue(color_image,ClampToQuantum(pixel),q); break;
cristye6365592010-04-02 17:31:23 +00001030 case 3:
1031 {
cristy4c08aed2011-07-01 19:47:50 +00001032 if (image->colorspace == CMYKColorspace)
1033 SetPixelBlack(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001034 break;
1035 }
1036 case 4:
1037 {
cristy4c08aed2011-07-01 19:47:50 +00001038 if (image->matte != MagickFalse)
1039 SetPixelAlpha(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001040 break;
1041 }
1042 }
1043 }
cristyed231572011-07-14 02:18:59 +00001044 p+=GetPixelChannels(image);
1045 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +00001046 }
1047 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1048 status=MagickFalse;
1049 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1050 {
1051 MagickBooleanType
1052 proceed;
1053
1054#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00001055 #pragma omp critical (MagickCore_ColorMatrixImage)
cristye6365592010-04-02 17:31:23 +00001056#endif
1057 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1058 image->rows);
1059 if (proceed == MagickFalse)
1060 status=MagickFalse;
1061 }
1062 }
1063 color_view=DestroyCacheView(color_view);
1064 image_view=DestroyCacheView(image_view);
1065 if (status == MagickFalse)
1066 color_image=DestroyImage(color_image);
1067 return(color_image);
1068}
1069
1070/*
1071%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1072% %
1073% %
1074% %
cristy3ed852e2009-09-05 21:47:34 +00001075+ D e s t r o y F x I n f o %
1076% %
1077% %
1078% %
1079%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1080%
1081% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1082%
1083% The format of the DestroyFxInfo method is:
1084%
1085% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1086%
1087% A description of each parameter follows:
1088%
1089% o fx_info: the fx info.
1090%
1091*/
cristy7832dc22011-09-05 01:21:53 +00001092MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
cristy3ed852e2009-09-05 21:47:34 +00001093{
cristybb503372010-05-27 20:51:26 +00001094 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001095 i;
1096
1097 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1098 fx_info->expression=DestroyString(fx_info->expression);
1099 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1100 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001101 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001102 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1103 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001104 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1105 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1106 return(fx_info);
1107}
1108
1109/*
1110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1111% %
1112% %
1113% %
cristy3ed852e2009-09-05 21:47:34 +00001114+ 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 %
1115% %
1116% %
1117% %
1118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1119%
1120% FxEvaluateChannelExpression() evaluates an expression and returns the
1121% results.
1122%
1123% The format of the FxEvaluateExpression method is:
1124%
1125% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001126% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001127% MagickRealType *alpha,Exceptioninfo *exception)
1128% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1129% MagickRealType *alpha,Exceptioninfo *exception)
1130%
1131% A description of each parameter follows:
1132%
1133% o fx_info: the fx info.
1134%
1135% o channel: the channel.
1136%
1137% o x,y: the pixel position.
1138%
1139% o alpha: the result.
1140%
1141% o exception: return any errors or warnings in this structure.
1142%
1143*/
1144
cristy351842f2010-03-07 15:27:38 +00001145static inline double MagickMax(const double x,const double y)
1146{
1147 if (x > y)
1148 return(x);
1149 return(y);
1150}
1151
1152static inline double MagickMin(const double x,const double y)
1153{
1154 if (x < y)
1155 return(x);
1156 return(y);
1157}
1158
cristy3ed852e2009-09-05 21:47:34 +00001159static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001160 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001161{
1162 char
1163 key[MaxTextExtent],
1164 statistic[MaxTextExtent];
1165
1166 const char
1167 *value;
1168
1169 register const char
1170 *p;
1171
1172 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1173 if (*p == '.')
1174 switch (*++p) /* e.g. depth.r */
1175 {
cristy541ae572011-08-05 19:08:59 +00001176 case 'r': channel=RedPixelChannel; break;
1177 case 'g': channel=GreenPixelChannel; break;
1178 case 'b': channel=BluePixelChannel; break;
1179 case 'c': channel=CyanPixelChannel; break;
1180 case 'm': channel=MagentaPixelChannel; break;
1181 case 'y': channel=YellowPixelChannel; break;
1182 case 'k': channel=BlackPixelChannel; break;
cristy3ed852e2009-09-05 21:47:34 +00001183 default: break;
1184 }
cristyb51dff52011-05-19 16:55:47 +00001185 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001186 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001187 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1188 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001189 return(QuantumScale*StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001190 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1191 if (LocaleNCompare(symbol,"depth",5) == 0)
1192 {
cristybb503372010-05-27 20:51:26 +00001193 size_t
cristy3ed852e2009-09-05 21:47:34 +00001194 depth;
1195
cristyfefab1b2011-07-05 00:33:22 +00001196 depth=GetImageDepth(image,exception);
1197 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001198 }
1199 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1200 {
1201 double
1202 kurtosis,
1203 skewness;
1204
cristyd42d9952011-07-08 14:21:50 +00001205 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001206 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001207 }
1208 if (LocaleNCompare(symbol,"maxima",6) == 0)
1209 {
1210 double
1211 maxima,
1212 minima;
1213
cristyd42d9952011-07-08 14:21:50 +00001214 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001215 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001216 }
1217 if (LocaleNCompare(symbol,"mean",4) == 0)
1218 {
1219 double
1220 mean,
1221 standard_deviation;
1222
cristyd42d9952011-07-08 14:21:50 +00001223 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001224 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001225 }
1226 if (LocaleNCompare(symbol,"minima",6) == 0)
1227 {
1228 double
1229 maxima,
1230 minima;
1231
cristyd42d9952011-07-08 14:21:50 +00001232 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001233 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001234 }
1235 if (LocaleNCompare(symbol,"skewness",8) == 0)
1236 {
1237 double
1238 kurtosis,
1239 skewness;
1240
cristyd42d9952011-07-08 14:21:50 +00001241 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001242 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001243 }
1244 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1245 {
1246 double
1247 mean,
1248 standard_deviation;
1249
cristyd42d9952011-07-08 14:21:50 +00001250 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001251 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001252 standard_deviation);
1253 }
1254 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1255 ConstantString(statistic));
cristydbdd0e32011-11-04 23:29:40 +00001256 return(QuantumScale*StringToDouble(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001257}
1258
1259static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001260 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001261 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001262
cristyb0aad4c2011-11-02 19:30:35 +00001263static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
1264{
1265 if (beta != 0)
1266 return(FxGCD(beta,alpha % beta));
1267 return(alpha);
1268}
1269
cristy3ed852e2009-09-05 21:47:34 +00001270static inline const char *FxSubexpression(const char *expression,
1271 ExceptionInfo *exception)
1272{
1273 const char
1274 *subexpression;
1275
cristybb503372010-05-27 20:51:26 +00001276 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001277 level;
1278
1279 level=0;
1280 subexpression=expression;
1281 while ((*subexpression != '\0') &&
1282 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1283 {
1284 if (strchr("(",(int) *subexpression) != (char *) NULL)
1285 level++;
1286 else
1287 if (strchr(")",(int) *subexpression) != (char *) NULL)
1288 level--;
1289 subexpression++;
1290 }
1291 if (*subexpression == '\0')
1292 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1293 "UnbalancedParenthesis","`%s'",expression);
1294 return(subexpression);
1295}
1296
cristy0568ffc2011-07-25 16:54:14 +00001297static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001298 const ssize_t x,const ssize_t y,const char *expression,
1299 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001300{
1301 char
1302 *q,
1303 subexpression[MaxTextExtent],
1304 symbol[MaxTextExtent];
1305
1306 const char
1307 *p,
1308 *value;
1309
1310 Image
1311 *image;
1312
cristy4c08aed2011-07-01 19:47:50 +00001313 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001314 pixel;
1315
1316 MagickRealType
1317 alpha,
1318 beta;
1319
1320 PointInfo
1321 point;
1322
cristybb503372010-05-27 20:51:26 +00001323 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001324 i;
1325
1326 size_t
cristy1707c6c2012-01-18 23:30:54 +00001327 length,
cristy3ed852e2009-09-05 21:47:34 +00001328 level;
1329
1330 p=expression;
1331 i=GetImageIndexInList(fx_info->images);
1332 level=0;
1333 point.x=(double) x;
1334 point.y=(double) y;
1335 if (isalpha((int) *(p+1)) == 0)
1336 {
1337 if (strchr("suv",(int) *p) != (char *) NULL)
1338 {
1339 switch (*p)
1340 {
1341 case 's':
1342 default:
1343 {
1344 i=GetImageIndexInList(fx_info->images);
1345 break;
1346 }
1347 case 'u': i=0; break;
1348 case 'v': i=1; break;
1349 }
1350 p++;
1351 if (*p == '[')
1352 {
1353 level++;
1354 q=subexpression;
1355 for (p++; *p != '\0'; )
1356 {
1357 if (*p == '[')
1358 level++;
1359 else
1360 if (*p == ']')
1361 {
1362 level--;
1363 if (level == 0)
1364 break;
1365 }
1366 *q++=(*p++);
1367 }
1368 *q='\0';
1369 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1370 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001371 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001372 p++;
1373 }
1374 if (*p == '.')
1375 p++;
1376 }
1377 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1378 {
1379 p++;
1380 if (*p == '{')
1381 {
1382 level++;
1383 q=subexpression;
1384 for (p++; *p != '\0'; )
1385 {
1386 if (*p == '{')
1387 level++;
1388 else
1389 if (*p == '}')
1390 {
1391 level--;
1392 if (level == 0)
1393 break;
1394 }
1395 *q++=(*p++);
1396 }
1397 *q='\0';
1398 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1399 &beta,exception);
1400 point.x=alpha;
1401 point.y=beta;
1402 p++;
1403 }
1404 else
1405 if (*p == '[')
1406 {
1407 level++;
1408 q=subexpression;
1409 for (p++; *p != '\0'; )
1410 {
1411 if (*p == '[')
1412 level++;
1413 else
1414 if (*p == ']')
1415 {
1416 level--;
1417 if (level == 0)
1418 break;
1419 }
1420 *q++=(*p++);
1421 }
1422 *q='\0';
1423 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1424 &beta,exception);
1425 point.x+=alpha;
1426 point.y+=beta;
1427 p++;
1428 }
1429 if (*p == '.')
1430 p++;
1431 }
1432 }
1433 length=GetImageListLength(fx_info->images);
1434 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001435 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001436 i%=length;
1437 image=GetImageFromList(fx_info->images,i);
1438 if (image == (Image *) NULL)
1439 {
1440 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1441 "NoSuchImage","`%s'",expression);
1442 return(0.0);
1443 }
cristy4c08aed2011-07-01 19:47:50 +00001444 GetPixelInfo(image,&pixel);
1445 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001446 point.x,point.y,&pixel,exception);
cristy1707c6c2012-01-18 23:30:54 +00001447 if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1448 (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001449 (LocaleCompare(p,"saturation") != 0) &&
1450 (LocaleCompare(p,"lightness") != 0))
1451 {
1452 char
1453 name[MaxTextExtent];
1454
1455 (void) CopyMagickString(name,p,MaxTextExtent);
1456 for (q=name+(strlen(name)-1); q > name; q--)
1457 {
1458 if (*q == ')')
1459 break;
1460 if (*q == '.')
1461 {
1462 *q='\0';
1463 break;
1464 }
1465 }
1466 if ((strlen(name) > 2) &&
1467 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1468 {
cristy4c08aed2011-07-01 19:47:50 +00001469 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001470 *color;
1471
cristy4c08aed2011-07-01 19:47:50 +00001472 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1473 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001474 {
1475 pixel=(*color);
1476 p+=strlen(name);
1477 }
1478 else
cristy1707c6c2012-01-18 23:30:54 +00001479 {
1480 MagickBooleanType
1481 status;
1482
1483 status=QueryColorCompliance(name,AllCompliance,&pixel,
1484 fx_info->exception);
1485 if (status != MagickFalse)
1486 {
1487 (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1488 name),ClonePixelInfo(&pixel));
1489 p+=strlen(name);
1490 }
1491 }
cristy3ed852e2009-09-05 21:47:34 +00001492 }
1493 }
1494 (void) CopyMagickString(symbol,p,MaxTextExtent);
1495 StripString(symbol);
1496 if (*symbol == '\0')
1497 {
1498 switch (channel)
1499 {
cristy0568ffc2011-07-25 16:54:14 +00001500 case RedPixelChannel: return(QuantumScale*pixel.red);
1501 case GreenPixelChannel: return(QuantumScale*pixel.green);
1502 case BluePixelChannel: return(QuantumScale*pixel.blue);
1503 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001504 {
1505 if (image->colorspace != CMYKColorspace)
1506 {
1507 (void) ThrowMagickException(exception,GetMagickModule(),
cristy1a020e42011-12-06 18:13:23 +00001508 ImageError,"ColorSeparatedImageRequired","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00001509 image->filename);
1510 return(0.0);
1511 }
cristy4c08aed2011-07-01 19:47:50 +00001512 return(QuantumScale*pixel.black);
1513 }
cristy0568ffc2011-07-25 16:54:14 +00001514 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001515 {
1516 MagickRealType
1517 alpha;
1518
1519 if (pixel.matte == MagickFalse)
1520 return(1.0);
1521 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1522 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001523 }
cristya382aca2011-12-06 18:22:48 +00001524 case IndexPixelChannel:
1525 return(0.0);
cristyb3a73b52011-07-26 01:34:43 +00001526 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001527 {
cristy4c08aed2011-07-01 19:47:50 +00001528 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001529 }
cristy3ed852e2009-09-05 21:47:34 +00001530 default:
1531 break;
1532 }
1533 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1534 "UnableToParseExpression","`%s'",p);
1535 return(0.0);
1536 }
1537 switch (*symbol)
1538 {
1539 case 'A':
1540 case 'a':
1541 {
1542 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001543 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001544 break;
1545 }
1546 case 'B':
1547 case 'b':
1548 {
1549 if (LocaleCompare(symbol,"b") == 0)
1550 return(QuantumScale*pixel.blue);
1551 break;
1552 }
1553 case 'C':
1554 case 'c':
1555 {
1556 if (LocaleNCompare(symbol,"channel",7) == 0)
1557 {
1558 GeometryInfo
1559 channel_info;
1560
1561 MagickStatusType
1562 flags;
1563
1564 flags=ParseGeometry(symbol+7,&channel_info);
1565 if (image->colorspace == CMYKColorspace)
1566 switch (channel)
1567 {
cristy0568ffc2011-07-25 16:54:14 +00001568 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001569 {
1570 if ((flags & RhoValue) == 0)
1571 return(0.0);
1572 return(channel_info.rho);
1573 }
cristy0568ffc2011-07-25 16:54:14 +00001574 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001575 {
1576 if ((flags & SigmaValue) == 0)
1577 return(0.0);
1578 return(channel_info.sigma);
1579 }
cristy0568ffc2011-07-25 16:54:14 +00001580 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001581 {
1582 if ((flags & XiValue) == 0)
1583 return(0.0);
1584 return(channel_info.xi);
1585 }
cristy0568ffc2011-07-25 16:54:14 +00001586 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001587 {
1588 if ((flags & PsiValue) == 0)
1589 return(0.0);
1590 return(channel_info.psi);
1591 }
cristy0568ffc2011-07-25 16:54:14 +00001592 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001593 {
1594 if ((flags & ChiValue) == 0)
1595 return(0.0);
1596 return(channel_info.chi);
1597 }
1598 default:
1599 return(0.0);
1600 }
1601 switch (channel)
1602 {
cristy0568ffc2011-07-25 16:54:14 +00001603 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001604 {
1605 if ((flags & RhoValue) == 0)
1606 return(0.0);
1607 return(channel_info.rho);
1608 }
cristy0568ffc2011-07-25 16:54:14 +00001609 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001610 {
1611 if ((flags & SigmaValue) == 0)
1612 return(0.0);
1613 return(channel_info.sigma);
1614 }
cristy0568ffc2011-07-25 16:54:14 +00001615 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001616 {
1617 if ((flags & XiValue) == 0)
1618 return(0.0);
1619 return(channel_info.xi);
1620 }
cristy0568ffc2011-07-25 16:54:14 +00001621 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001622 {
1623 if ((flags & ChiValue) == 0)
1624 return(0.0);
1625 return(channel_info.chi);
1626 }
cristy0568ffc2011-07-25 16:54:14 +00001627 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001628 {
1629 if ((flags & PsiValue) == 0)
1630 return(0.0);
1631 return(channel_info.psi);
1632 }
cristy3ed852e2009-09-05 21:47:34 +00001633 default:
1634 return(0.0);
1635 }
1636 return(0.0);
1637 }
1638 if (LocaleCompare(symbol,"c") == 0)
1639 return(QuantumScale*pixel.red);
1640 break;
1641 }
1642 case 'D':
1643 case 'd':
1644 {
1645 if (LocaleNCompare(symbol,"depth",5) == 0)
1646 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1647 break;
1648 }
1649 case 'G':
1650 case 'g':
1651 {
1652 if (LocaleCompare(symbol,"g") == 0)
1653 return(QuantumScale*pixel.green);
1654 break;
1655 }
1656 case 'K':
1657 case 'k':
1658 {
1659 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1660 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1661 if (LocaleCompare(symbol,"k") == 0)
1662 {
1663 if (image->colorspace != CMYKColorspace)
1664 {
1665 (void) ThrowMagickException(exception,GetMagickModule(),
1666 OptionError,"ColorSeparatedImageRequired","`%s'",
1667 image->filename);
1668 return(0.0);
1669 }
cristy4c08aed2011-07-01 19:47:50 +00001670 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001671 }
1672 break;
1673 }
1674 case 'H':
1675 case 'h':
1676 {
1677 if (LocaleCompare(symbol,"h") == 0)
1678 return((MagickRealType) image->rows);
1679 if (LocaleCompare(symbol,"hue") == 0)
1680 {
1681 double
1682 hue,
1683 lightness,
1684 saturation;
1685
cristyda1f9c12011-10-02 21:39:49 +00001686 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1687 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001688 return(hue);
1689 }
1690 break;
1691 }
1692 case 'I':
1693 case 'i':
1694 {
1695 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1696 (LocaleCompare(symbol,"image.minima") == 0) ||
1697 (LocaleCompare(symbol,"image.maxima") == 0) ||
1698 (LocaleCompare(symbol,"image.mean") == 0) ||
1699 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1700 (LocaleCompare(symbol,"image.skewness") == 0) ||
1701 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1702 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1703 if (LocaleCompare(symbol,"image.resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001704 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001705 if (LocaleCompare(symbol,"image.resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001706 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001707 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001708 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001709 if (LocaleCompare(symbol,"i") == 0)
1710 return((MagickRealType) x);
1711 break;
1712 }
1713 case 'J':
1714 case 'j':
1715 {
1716 if (LocaleCompare(symbol,"j") == 0)
1717 return((MagickRealType) y);
1718 break;
1719 }
1720 case 'L':
1721 case 'l':
1722 {
1723 if (LocaleCompare(symbol,"lightness") == 0)
1724 {
1725 double
1726 hue,
1727 lightness,
1728 saturation;
1729
cristyda1f9c12011-10-02 21:39:49 +00001730 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1731 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001732 return(lightness);
1733 }
1734 if (LocaleCompare(symbol,"luminance") == 0)
1735 {
1736 double
1737 luminence;
1738
1739 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1740 return(QuantumScale*luminence);
1741 }
1742 break;
1743 }
1744 case 'M':
1745 case 'm':
1746 {
1747 if (LocaleNCompare(symbol,"maxima",6) == 0)
1748 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1749 if (LocaleNCompare(symbol,"mean",4) == 0)
1750 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1751 if (LocaleNCompare(symbol,"minima",6) == 0)
1752 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1753 if (LocaleCompare(symbol,"m") == 0)
1754 return(QuantumScale*pixel.blue);
1755 break;
1756 }
1757 case 'N':
1758 case 'n':
1759 {
1760 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001761 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001762 break;
1763 }
1764 case 'O':
1765 case 'o':
1766 {
1767 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001768 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001769 break;
1770 }
1771 case 'P':
1772 case 'p':
1773 {
1774 if (LocaleCompare(symbol,"page.height") == 0)
1775 return((MagickRealType) image->page.height);
1776 if (LocaleCompare(symbol,"page.width") == 0)
1777 return((MagickRealType) image->page.width);
1778 if (LocaleCompare(symbol,"page.x") == 0)
1779 return((MagickRealType) image->page.x);
1780 if (LocaleCompare(symbol,"page.y") == 0)
1781 return((MagickRealType) image->page.y);
1782 break;
1783 }
1784 case 'R':
1785 case 'r':
1786 {
1787 if (LocaleCompare(symbol,"resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001788 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001789 if (LocaleCompare(symbol,"resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001790 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001791 if (LocaleCompare(symbol,"r") == 0)
1792 return(QuantumScale*pixel.red);
1793 break;
1794 }
1795 case 'S':
1796 case 's':
1797 {
1798 if (LocaleCompare(symbol,"saturation") == 0)
1799 {
1800 double
1801 hue,
1802 lightness,
1803 saturation;
1804
cristyda1f9c12011-10-02 21:39:49 +00001805 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1806 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001807 return(saturation);
1808 }
1809 if (LocaleNCompare(symbol,"skewness",8) == 0)
1810 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1811 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1812 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1813 break;
1814 }
1815 case 'T':
1816 case 't':
1817 {
1818 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001819 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001820 break;
1821 }
1822 case 'W':
1823 case 'w':
1824 {
1825 if (LocaleCompare(symbol,"w") == 0)
1826 return((MagickRealType) image->columns);
1827 break;
1828 }
1829 case 'Y':
1830 case 'y':
1831 {
1832 if (LocaleCompare(symbol,"y") == 0)
1833 return(QuantumScale*pixel.green);
1834 break;
1835 }
1836 case 'Z':
1837 case 'z':
1838 {
1839 if (LocaleCompare(symbol,"z") == 0)
1840 {
1841 MagickRealType
1842 depth;
1843
cristyfefab1b2011-07-05 00:33:22 +00001844 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001845 return(depth);
1846 }
1847 break;
1848 }
1849 default:
1850 break;
1851 }
1852 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1853 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001854 return((MagickRealType) StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001855 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1856 "UnableToParseExpression","`%s'",symbol);
1857 return(0.0);
1858}
1859
1860static const char *FxOperatorPrecedence(const char *expression,
1861 ExceptionInfo *exception)
1862{
1863 typedef enum
1864 {
1865 UndefinedPrecedence,
1866 NullPrecedence,
1867 BitwiseComplementPrecedence,
1868 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001869 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001870 MultiplyPrecedence,
1871 AdditionPrecedence,
1872 ShiftPrecedence,
1873 RelationalPrecedence,
1874 EquivalencyPrecedence,
1875 BitwiseAndPrecedence,
1876 BitwiseOrPrecedence,
1877 LogicalAndPrecedence,
1878 LogicalOrPrecedence,
1879 TernaryPrecedence,
1880 AssignmentPrecedence,
1881 CommaPrecedence,
1882 SeparatorPrecedence
1883 } FxPrecedence;
1884
1885 FxPrecedence
1886 precedence,
1887 target;
1888
1889 register const char
1890 *subexpression;
1891
1892 register int
1893 c;
1894
cristybb503372010-05-27 20:51:26 +00001895 size_t
cristy3ed852e2009-09-05 21:47:34 +00001896 level;
1897
1898 c=0;
1899 level=0;
1900 subexpression=(const char *) NULL;
1901 target=NullPrecedence;
1902 while (*expression != '\0')
1903 {
1904 precedence=UndefinedPrecedence;
1905 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1906 {
1907 expression++;
1908 continue;
1909 }
cristy488fa882010-03-01 22:34:24 +00001910 switch (*expression)
1911 {
1912 case 'A':
1913 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001914 {
cristyb33454f2011-08-03 02:10:45 +00001915#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001916 if (LocaleNCompare(expression,"acosh",5) == 0)
1917 {
1918 expression+=5;
1919 break;
1920 }
cristyb33454f2011-08-03 02:10:45 +00001921#endif
1922#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001923 if (LocaleNCompare(expression,"asinh",5) == 0)
1924 {
1925 expression+=5;
1926 break;
1927 }
cristyb33454f2011-08-03 02:10:45 +00001928#endif
1929#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001930 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001931 {
1932 expression+=5;
1933 break;
1934 }
cristyb33454f2011-08-03 02:10:45 +00001935#endif
cristy488fa882010-03-01 22:34:24 +00001936 break;
cristy3ed852e2009-09-05 21:47:34 +00001937 }
cristy62d455f2011-11-03 11:42:28 +00001938 case 'E':
1939 case 'e':
1940 {
1941 if ((LocaleNCompare(expression,"E+",2) == 0) ||
1942 (LocaleNCompare(expression,"E-",2) == 0))
1943 {
1944 expression+=2; /* scientific notation */
1945 break;
1946 }
1947 }
cristy488fa882010-03-01 22:34:24 +00001948 case 'J':
1949 case 'j':
1950 {
1951 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1952 (LocaleNCompare(expression,"j1",2) == 0))
1953 {
1954 expression+=2;
1955 break;
1956 }
1957 break;
1958 }
cristy2def9322010-06-18 23:59:37 +00001959 case '#':
1960 {
1961 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1962 expression++;
1963 break;
1964 }
cristy488fa882010-03-01 22:34:24 +00001965 default:
1966 break;
1967 }
cristy3ed852e2009-09-05 21:47:34 +00001968 if ((c == (int) '{') || (c == (int) '['))
1969 level++;
1970 else
1971 if ((c == (int) '}') || (c == (int) ']'))
1972 level--;
1973 if (level == 0)
1974 switch ((unsigned char) *expression)
1975 {
1976 case '~':
1977 case '!':
1978 {
1979 precedence=BitwiseComplementPrecedence;
1980 break;
1981 }
1982 case '^':
cristy6621e252010-08-13 00:42:57 +00001983 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001984 {
1985 precedence=ExponentPrecedence;
1986 break;
1987 }
1988 default:
1989 {
1990 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1991 (strchr(")",c) != (char *) NULL))) &&
1992 (((islower((int) ((char) *expression)) != 0) ||
1993 (strchr("(",(int) *expression) != (char *) NULL)) ||
1994 ((isdigit((int) ((char) c)) == 0) &&
1995 (isdigit((int) ((char) *expression)) != 0))) &&
1996 (strchr("xy",(int) *expression) == (char *) NULL))
1997 precedence=MultiplyPrecedence;
1998 break;
1999 }
2000 case '*':
2001 case '/':
2002 case '%':
2003 {
2004 precedence=MultiplyPrecedence;
2005 break;
2006 }
2007 case '+':
2008 case '-':
2009 {
2010 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
2011 (isalpha(c) != 0))
2012 precedence=AdditionPrecedence;
2013 break;
2014 }
2015 case LeftShiftOperator:
2016 case RightShiftOperator:
2017 {
2018 precedence=ShiftPrecedence;
2019 break;
2020 }
2021 case '<':
2022 case LessThanEqualOperator:
2023 case GreaterThanEqualOperator:
2024 case '>':
2025 {
2026 precedence=RelationalPrecedence;
2027 break;
2028 }
2029 case EqualOperator:
2030 case NotEqualOperator:
2031 {
2032 precedence=EquivalencyPrecedence;
2033 break;
2034 }
2035 case '&':
2036 {
2037 precedence=BitwiseAndPrecedence;
2038 break;
2039 }
2040 case '|':
2041 {
2042 precedence=BitwiseOrPrecedence;
2043 break;
2044 }
2045 case LogicalAndOperator:
2046 {
2047 precedence=LogicalAndPrecedence;
2048 break;
2049 }
2050 case LogicalOrOperator:
2051 {
2052 precedence=LogicalOrPrecedence;
2053 break;
2054 }
cristy116af162010-08-13 01:25:47 +00002055 case ExponentialNotation:
2056 {
2057 precedence=ExponentialNotationPrecedence;
2058 break;
2059 }
cristy3ed852e2009-09-05 21:47:34 +00002060 case ':':
2061 case '?':
2062 {
2063 precedence=TernaryPrecedence;
2064 break;
2065 }
2066 case '=':
2067 {
2068 precedence=AssignmentPrecedence;
2069 break;
2070 }
2071 case ',':
2072 {
2073 precedence=CommaPrecedence;
2074 break;
2075 }
2076 case ';':
2077 {
2078 precedence=SeparatorPrecedence;
2079 break;
2080 }
2081 }
2082 if ((precedence == BitwiseComplementPrecedence) ||
2083 (precedence == TernaryPrecedence) ||
2084 (precedence == AssignmentPrecedence))
2085 {
2086 if (precedence > target)
2087 {
2088 /*
2089 Right-to-left associativity.
2090 */
2091 target=precedence;
2092 subexpression=expression;
2093 }
2094 }
2095 else
2096 if (precedence >= target)
2097 {
2098 /*
2099 Left-to-right associativity.
2100 */
2101 target=precedence;
2102 subexpression=expression;
2103 }
2104 if (strchr("(",(int) *expression) != (char *) NULL)
2105 expression=FxSubexpression(expression,exception);
2106 c=(int) (*expression++);
2107 }
2108 return(subexpression);
2109}
2110
2111static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002112 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002113 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002114{
2115 char
2116 *q,
2117 subexpression[MaxTextExtent];
2118
2119 MagickRealType
2120 alpha,
2121 gamma;
2122
2123 register const char
2124 *p;
2125
2126 *beta=0.0;
2127 if (exception->severity != UndefinedException)
2128 return(0.0);
2129 while (isspace((int) *expression) != 0)
2130 expression++;
2131 if (*expression == '\0')
2132 {
2133 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2134 "MissingExpression","`%s'",expression);
2135 return(0.0);
2136 }
cristy66322f02010-05-17 11:40:48 +00002137 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002138 p=FxOperatorPrecedence(expression,exception);
2139 if (p != (const char *) NULL)
2140 {
2141 (void) CopyMagickString(subexpression,expression,(size_t)
2142 (p-expression+1));
2143 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2144 exception);
2145 switch ((unsigned char) *p)
2146 {
2147 case '~':
2148 {
2149 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002150 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002151 return(*beta);
2152 }
2153 case '!':
2154 {
2155 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2156 return(*beta == 0.0 ? 1.0 : 0.0);
2157 }
2158 case '^':
2159 {
2160 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2161 channel,x,y,++p,beta,exception));
2162 return(*beta);
2163 }
2164 case '*':
cristy116af162010-08-13 01:25:47 +00002165 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002166 {
2167 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2168 return(alpha*(*beta));
2169 }
2170 case '/':
2171 {
2172 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2173 if (*beta == 0.0)
2174 {
2175 if (exception->severity == UndefinedException)
2176 (void) ThrowMagickException(exception,GetMagickModule(),
2177 OptionError,"DivideByZero","`%s'",expression);
2178 return(0.0);
2179 }
2180 return(alpha/(*beta));
2181 }
2182 case '%':
2183 {
2184 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2185 *beta=fabs(floor(((double) *beta)+0.5));
2186 if (*beta == 0.0)
2187 {
2188 (void) ThrowMagickException(exception,GetMagickModule(),
2189 OptionError,"DivideByZero","`%s'",expression);
2190 return(0.0);
2191 }
2192 return(fmod((double) alpha,(double) *beta));
2193 }
2194 case '+':
2195 {
2196 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2197 return(alpha+(*beta));
2198 }
2199 case '-':
2200 {
2201 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2202 return(alpha-(*beta));
2203 }
2204 case LeftShiftOperator:
2205 {
2206 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002207 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002208 return(*beta);
2209 }
2210 case RightShiftOperator:
2211 {
2212 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002213 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002214 return(*beta);
2215 }
2216 case '<':
2217 {
2218 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2219 return(alpha < *beta ? 1.0 : 0.0);
2220 }
2221 case LessThanEqualOperator:
2222 {
2223 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2224 return(alpha <= *beta ? 1.0 : 0.0);
2225 }
2226 case '>':
2227 {
2228 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2229 return(alpha > *beta ? 1.0 : 0.0);
2230 }
2231 case GreaterThanEqualOperator:
2232 {
2233 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2234 return(alpha >= *beta ? 1.0 : 0.0);
2235 }
2236 case EqualOperator:
2237 {
2238 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2239 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2240 }
2241 case NotEqualOperator:
2242 {
2243 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2244 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2245 }
2246 case '&':
2247 {
2248 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002249 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002250 return(*beta);
2251 }
2252 case '|':
2253 {
2254 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002255 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002256 return(*beta);
2257 }
2258 case LogicalAndOperator:
2259 {
2260 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2261 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2262 return(*beta);
2263 }
2264 case LogicalOrOperator:
2265 {
2266 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2267 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2268 return(*beta);
2269 }
2270 case '?':
2271 {
2272 MagickRealType
2273 gamma;
2274
2275 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2276 q=subexpression;
2277 p=StringToken(":",&q);
2278 if (q == (char *) NULL)
2279 {
2280 (void) ThrowMagickException(exception,GetMagickModule(),
2281 OptionError,"UnableToParseExpression","`%s'",subexpression);
2282 return(0.0);
2283 }
2284 if (fabs((double) alpha) > MagickEpsilon)
2285 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2286 else
2287 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2288 return(gamma);
2289 }
2290 case '=':
2291 {
2292 char
2293 numeric[MaxTextExtent];
2294
2295 q=subexpression;
2296 while (isalpha((int) ((unsigned char) *q)) != 0)
2297 q++;
2298 if (*q != '\0')
2299 {
2300 (void) ThrowMagickException(exception,GetMagickModule(),
2301 OptionError,"UnableToParseExpression","`%s'",subexpression);
2302 return(0.0);
2303 }
2304 ClearMagickException(exception);
2305 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002306 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002307 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002308 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2309 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2310 subexpression),ConstantString(numeric));
2311 return(*beta);
2312 }
2313 case ',':
2314 {
2315 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2316 return(alpha);
2317 }
2318 case ';':
2319 {
2320 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2321 return(*beta);
2322 }
2323 default:
2324 {
2325 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2326 exception);
2327 return(gamma);
2328 }
2329 }
2330 }
2331 if (strchr("(",(int) *expression) != (char *) NULL)
2332 {
2333 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2334 subexpression[strlen(subexpression)-1]='\0';
2335 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2336 exception);
2337 return(gamma);
2338 }
cristy8b8a3ae2010-10-23 18:49:46 +00002339 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002340 {
2341 case '+':
2342 {
2343 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2344 exception);
2345 return(1.0*gamma);
2346 }
2347 case '-':
2348 {
2349 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2350 exception);
2351 return(-1.0*gamma);
2352 }
2353 case '~':
2354 {
2355 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2356 exception);
cristybb503372010-05-27 20:51:26 +00002357 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002358 }
2359 case 'A':
2360 case 'a':
2361 {
2362 if (LocaleNCompare(expression,"abs",3) == 0)
2363 {
2364 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2365 exception);
2366 return((MagickRealType) fabs((double) alpha));
2367 }
cristyb33454f2011-08-03 02:10:45 +00002368#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002369 if (LocaleNCompare(expression,"acosh",5) == 0)
2370 {
2371 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2372 exception);
2373 return((MagickRealType) acosh((double) alpha));
2374 }
cristyb33454f2011-08-03 02:10:45 +00002375#endif
cristy3ed852e2009-09-05 21:47:34 +00002376 if (LocaleNCompare(expression,"acos",4) == 0)
2377 {
2378 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2379 exception);
2380 return((MagickRealType) acos((double) alpha));
2381 }
cristy43c22f42010-03-30 12:34:07 +00002382#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002383 if (LocaleNCompare(expression,"airy",4) == 0)
2384 {
2385 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2386 exception);
2387 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002388 return(1.0);
2389 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002390 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002391 }
cristy43c22f42010-03-30 12:34:07 +00002392#endif
cristyb33454f2011-08-03 02:10:45 +00002393#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002394 if (LocaleNCompare(expression,"asinh",5) == 0)
2395 {
2396 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2397 exception);
2398 return((MagickRealType) asinh((double) alpha));
2399 }
cristyb33454f2011-08-03 02:10:45 +00002400#endif
cristy3ed852e2009-09-05 21:47:34 +00002401 if (LocaleNCompare(expression,"asin",4) == 0)
2402 {
2403 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2404 exception);
2405 return((MagickRealType) asin((double) alpha));
2406 }
2407 if (LocaleNCompare(expression,"alt",3) == 0)
2408 {
2409 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2410 exception);
cristybb503372010-05-27 20:51:26 +00002411 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002412 }
2413 if (LocaleNCompare(expression,"atan2",5) == 0)
2414 {
2415 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2416 exception);
2417 return((MagickRealType) atan2((double) alpha,(double) *beta));
2418 }
cristyb33454f2011-08-03 02:10:45 +00002419#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002420 if (LocaleNCompare(expression,"atanh",5) == 0)
2421 {
2422 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2423 exception);
2424 return((MagickRealType) atanh((double) alpha));
2425 }
cristyb33454f2011-08-03 02:10:45 +00002426#endif
cristy3ed852e2009-09-05 21:47:34 +00002427 if (LocaleNCompare(expression,"atan",4) == 0)
2428 {
2429 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2430 exception);
2431 return((MagickRealType) atan((double) alpha));
2432 }
2433 if (LocaleCompare(expression,"a") == 0)
2434 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2435 break;
2436 }
2437 case 'B':
2438 case 'b':
2439 {
2440 if (LocaleCompare(expression,"b") == 0)
2441 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2442 break;
2443 }
2444 case 'C':
2445 case 'c':
2446 {
2447 if (LocaleNCompare(expression,"ceil",4) == 0)
2448 {
2449 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2450 exception);
2451 return((MagickRealType) ceil((double) alpha));
2452 }
2453 if (LocaleNCompare(expression,"cosh",4) == 0)
2454 {
2455 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2456 exception);
2457 return((MagickRealType) cosh((double) alpha));
2458 }
2459 if (LocaleNCompare(expression,"cos",3) == 0)
2460 {
2461 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2462 exception);
2463 return((MagickRealType) cos((double) alpha));
2464 }
2465 if (LocaleCompare(expression,"c") == 0)
2466 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2467 break;
2468 }
2469 case 'D':
2470 case 'd':
2471 {
2472 if (LocaleNCompare(expression,"debug",5) == 0)
2473 {
2474 const char
2475 *type;
2476
2477 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2478 exception);
2479 if (fx_info->images->colorspace == CMYKColorspace)
2480 switch (channel)
2481 {
cristy0568ffc2011-07-25 16:54:14 +00002482 case CyanPixelChannel: type="cyan"; break;
2483 case MagentaPixelChannel: type="magenta"; break;
2484 case YellowPixelChannel: type="yellow"; break;
2485 case AlphaPixelChannel: type="opacity"; break;
2486 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002487 default: type="unknown"; break;
2488 }
2489 else
2490 switch (channel)
2491 {
cristy0568ffc2011-07-25 16:54:14 +00002492 case RedPixelChannel: type="red"; break;
2493 case GreenPixelChannel: type="green"; break;
2494 case BluePixelChannel: type="blue"; break;
2495 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002496 default: type="unknown"; break;
2497 }
2498 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2499 if (strlen(subexpression) > 1)
2500 subexpression[strlen(subexpression)-1]='\0';
2501 if (fx_info->file != (FILE *) NULL)
cristy1707c6c2012-01-18 23:30:54 +00002502 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2503 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2504 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002505 return(0.0);
2506 }
cristy5597a8d2011-11-04 00:25:32 +00002507 if (LocaleNCompare(expression,"drc",3) == 0)
2508 {
2509 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2510 exception);
2511 return((MagickRealType) (alpha/(*beta*(alpha-1.0)+1.0)));
2512 }
cristy3ed852e2009-09-05 21:47:34 +00002513 break;
2514 }
2515 case 'E':
2516 case 'e':
2517 {
2518 if (LocaleCompare(expression,"epsilon") == 0)
2519 return((MagickRealType) MagickEpsilon);
2520 if (LocaleNCompare(expression,"exp",3) == 0)
2521 {
2522 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2523 exception);
2524 return((MagickRealType) exp((double) alpha));
2525 }
2526 if (LocaleCompare(expression,"e") == 0)
2527 return((MagickRealType) 2.7182818284590452354);
2528 break;
2529 }
2530 case 'F':
2531 case 'f':
2532 {
2533 if (LocaleNCompare(expression,"floor",5) == 0)
2534 {
2535 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2536 exception);
2537 return((MagickRealType) floor((double) alpha));
2538 }
2539 break;
2540 }
2541 case 'G':
2542 case 'g':
2543 {
cristy9eeedea2011-11-02 19:04:05 +00002544 if (LocaleNCompare(expression,"gauss",5) == 0)
2545 {
2546 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2547 exception);
2548 gamma=exp((double) (-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2549 return((MagickRealType) gamma);
2550 }
cristyb0aad4c2011-11-02 19:30:35 +00002551 if (LocaleNCompare(expression,"gcd",3) == 0)
2552 {
2553 MagickOffsetType
2554 gcd;
2555
2556 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2557 exception);
cristy1707c6c2012-01-18 23:30:54 +00002558 gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2559 0.5));
cristyb0aad4c2011-11-02 19:30:35 +00002560 return((MagickRealType) gcd);
2561 }
cristy3ed852e2009-09-05 21:47:34 +00002562 if (LocaleCompare(expression,"g") == 0)
2563 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2564 break;
2565 }
2566 case 'H':
2567 case 'h':
2568 {
2569 if (LocaleCompare(expression,"h") == 0)
2570 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2571 if (LocaleCompare(expression,"hue") == 0)
2572 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2573 if (LocaleNCompare(expression,"hypot",5) == 0)
2574 {
2575 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2576 exception);
2577 return((MagickRealType) hypot((double) alpha,(double) *beta));
2578 }
2579 break;
2580 }
2581 case 'K':
2582 case 'k':
2583 {
2584 if (LocaleCompare(expression,"k") == 0)
2585 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2586 break;
2587 }
2588 case 'I':
2589 case 'i':
2590 {
2591 if (LocaleCompare(expression,"intensity") == 0)
2592 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2593 if (LocaleNCompare(expression,"int",3) == 0)
2594 {
2595 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2596 exception);
cristy16788e42010-08-13 13:44:26 +00002597 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002598 }
cristy82b20722011-11-05 21:52:36 +00002599#if defined(MAGICKCORE_HAVE_ISNAN)
cristy639399c2011-11-02 19:16:15 +00002600 if (LocaleNCompare(expression,"isnan",5) == 0)
2601 {
2602 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2603 exception);
cristy17a10202011-11-02 19:17:04 +00002604 return((MagickRealType) !!isnan((double) alpha));
cristy639399c2011-11-02 19:16:15 +00002605 }
cristy82b20722011-11-05 21:52:36 +00002606#endif
cristy3ed852e2009-09-05 21:47:34 +00002607 if (LocaleCompare(expression,"i") == 0)
2608 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2609 break;
2610 }
2611 case 'J':
2612 case 'j':
2613 {
2614 if (LocaleCompare(expression,"j") == 0)
2615 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002616#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002617 if (LocaleNCompare(expression,"j0",2) == 0)
2618 {
2619 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2620 exception);
2621 return((MagickRealType) j0((double) alpha));
2622 }
cristy161b9262010-03-20 19:34:32 +00002623#endif
2624#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002625 if (LocaleNCompare(expression,"j1",2) == 0)
2626 {
2627 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2628 exception);
2629 return((MagickRealType) j1((double) alpha));
2630 }
cristy161b9262010-03-20 19:34:32 +00002631#endif
cristyaa018fa2010-04-08 23:03:54 +00002632#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002633 if (LocaleNCompare(expression,"jinc",4) == 0)
2634 {
2635 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2636 exception);
cristy0946a822010-03-12 17:14:58 +00002637 if (alpha == 0.0)
2638 return(1.0);
cristy1707c6c2012-01-18 23:30:54 +00002639 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/(MagickPI*
2640 alpha));
cristyfce2f7b2010-03-12 00:29:49 +00002641 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002642 }
cristyaa018fa2010-04-08 23:03:54 +00002643#endif
cristy3ed852e2009-09-05 21:47:34 +00002644 break;
2645 }
2646 case 'L':
2647 case 'l':
2648 {
2649 if (LocaleNCompare(expression,"ln",2) == 0)
2650 {
2651 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2652 exception);
2653 return((MagickRealType) log((double) alpha));
2654 }
cristyc8ed5322010-08-31 12:07:59 +00002655 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002656 {
cristyc8ed5322010-08-31 12:07:59 +00002657 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002658 exception);
2659 return((MagickRealType) log10((double) alpha))/log10(2.0);
2660 }
2661 if (LocaleNCompare(expression,"log",3) == 0)
2662 {
2663 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2664 exception);
2665 return((MagickRealType) log10((double) alpha));
2666 }
2667 if (LocaleCompare(expression,"lightness") == 0)
2668 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2669 break;
2670 }
2671 case 'M':
2672 case 'm':
2673 {
2674 if (LocaleCompare(expression,"MaxRGB") == 0)
2675 return((MagickRealType) QuantumRange);
2676 if (LocaleNCompare(expression,"maxima",6) == 0)
2677 break;
2678 if (LocaleNCompare(expression,"max",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002679 {
2680 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2681 exception);
2682 return(alpha > *beta ? alpha : *beta);
2683 }
cristy3ed852e2009-09-05 21:47:34 +00002684 if (LocaleNCompare(expression,"minima",6) == 0)
2685 break;
2686 if (LocaleNCompare(expression,"min",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002687 {
2688 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2689 exception);
2690 return(alpha < *beta ? alpha : *beta);
2691 }
cristy3ed852e2009-09-05 21:47:34 +00002692 if (LocaleNCompare(expression,"mod",3) == 0)
2693 {
2694 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2695 exception);
cristy984049c2011-11-03 18:34:58 +00002696 gamma=alpha-floor((double) (alpha/(*beta)))*(*beta);
2697 return(gamma);
cristy3ed852e2009-09-05 21:47:34 +00002698 }
2699 if (LocaleCompare(expression,"m") == 0)
2700 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2701 break;
2702 }
2703 case 'N':
2704 case 'n':
2705 {
cristyad3502e2011-11-02 19:10:45 +00002706 if (LocaleNCompare(expression,"not",3) == 0)
2707 {
2708 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2709 exception);
2710 return((MagickRealType) (alpha < MagickEpsilon));
2711 }
cristy3ed852e2009-09-05 21:47:34 +00002712 if (LocaleCompare(expression,"n") == 0)
2713 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2714 break;
2715 }
2716 case 'O':
2717 case 'o':
2718 {
2719 if (LocaleCompare(expression,"Opaque") == 0)
2720 return(1.0);
2721 if (LocaleCompare(expression,"o") == 0)
2722 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2723 break;
2724 }
2725 case 'P':
2726 case 'p':
2727 {
cristy670aa3c2011-11-03 00:54:00 +00002728 if (LocaleCompare(expression,"phi") == 0)
2729 return((MagickRealType) MagickPHI);
cristy3ed852e2009-09-05 21:47:34 +00002730 if (LocaleCompare(expression,"pi") == 0)
2731 return((MagickRealType) MagickPI);
2732 if (LocaleNCompare(expression,"pow",3) == 0)
2733 {
2734 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2735 exception);
2736 return((MagickRealType) pow((double) alpha,(double) *beta));
2737 }
2738 if (LocaleCompare(expression,"p") == 0)
2739 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2740 break;
2741 }
2742 case 'Q':
2743 case 'q':
2744 {
2745 if (LocaleCompare(expression,"QuantumRange") == 0)
2746 return((MagickRealType) QuantumRange);
2747 if (LocaleCompare(expression,"QuantumScale") == 0)
2748 return((MagickRealType) QuantumScale);
2749 break;
2750 }
2751 case 'R':
2752 case 'r':
2753 {
2754 if (LocaleNCompare(expression,"rand",4) == 0)
2755 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2756 if (LocaleNCompare(expression,"round",5) == 0)
2757 {
2758 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2759 exception);
cristy16788e42010-08-13 13:44:26 +00002760 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002761 }
2762 if (LocaleCompare(expression,"r") == 0)
2763 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2764 break;
2765 }
2766 case 'S':
2767 case 's':
2768 {
2769 if (LocaleCompare(expression,"saturation") == 0)
2770 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2771 if (LocaleNCompare(expression,"sign",4) == 0)
2772 {
2773 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2774 exception);
2775 return(alpha < 0.0 ? -1.0 : 1.0);
2776 }
cristya6a09e72010-03-02 14:51:02 +00002777 if (LocaleNCompare(expression,"sinc",4) == 0)
2778 {
2779 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2780 exception);
2781 if (alpha == 0)
2782 return(1.0);
2783 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2784 (MagickPI*alpha));
2785 return(gamma);
2786 }
cristy3ed852e2009-09-05 21:47:34 +00002787 if (LocaleNCompare(expression,"sinh",4) == 0)
2788 {
2789 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2790 exception);
2791 return((MagickRealType) sinh((double) alpha));
2792 }
2793 if (LocaleNCompare(expression,"sin",3) == 0)
2794 {
2795 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2796 exception);
2797 return((MagickRealType) sin((double) alpha));
2798 }
2799 if (LocaleNCompare(expression,"sqrt",4) == 0)
2800 {
2801 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2802 exception);
2803 return((MagickRealType) sqrt((double) alpha));
2804 }
cristy9eeedea2011-11-02 19:04:05 +00002805 if (LocaleNCompare(expression,"squish",6) == 0)
2806 {
2807 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2808 exception);
2809 return((MagickRealType) (1.0/(1.0+exp((double) (4.0*alpha)))));
2810 }
cristy3ed852e2009-09-05 21:47:34 +00002811 if (LocaleCompare(expression,"s") == 0)
2812 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2813 break;
2814 }
2815 case 'T':
2816 case 't':
2817 {
2818 if (LocaleNCompare(expression,"tanh",4) == 0)
2819 {
2820 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2821 exception);
2822 return((MagickRealType) tanh((double) alpha));
2823 }
2824 if (LocaleNCompare(expression,"tan",3) == 0)
2825 {
2826 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2827 exception);
2828 return((MagickRealType) tan((double) alpha));
2829 }
2830 if (LocaleCompare(expression,"Transparent") == 0)
2831 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002832 if (LocaleNCompare(expression,"trunc",5) == 0)
2833 {
2834 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2835 exception);
2836 if (alpha >= 0.0)
2837 return((MagickRealType) floor((double) alpha));
2838 return((MagickRealType) ceil((double) alpha));
2839 }
cristy3ed852e2009-09-05 21:47:34 +00002840 if (LocaleCompare(expression,"t") == 0)
2841 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2842 break;
2843 }
2844 case 'U':
2845 case 'u':
2846 {
2847 if (LocaleCompare(expression,"u") == 0)
2848 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2849 break;
2850 }
2851 case 'V':
2852 case 'v':
2853 {
2854 if (LocaleCompare(expression,"v") == 0)
2855 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2856 break;
2857 }
2858 case 'W':
2859 case 'w':
2860 {
cristy9eeedea2011-11-02 19:04:05 +00002861 if (LocaleNCompare(expression,"while",5) == 0)
2862 {
2863 do
2864 {
2865 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2866 exception);
2867 } while (fabs((double) alpha) >= MagickEpsilon);
2868 return((MagickRealType) *beta);
2869 }
cristy3ed852e2009-09-05 21:47:34 +00002870 if (LocaleCompare(expression,"w") == 0)
2871 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2872 break;
2873 }
2874 case 'Y':
2875 case 'y':
2876 {
2877 if (LocaleCompare(expression,"y") == 0)
2878 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2879 break;
2880 }
2881 case 'Z':
2882 case 'z':
2883 {
2884 if (LocaleCompare(expression,"z") == 0)
2885 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2886 break;
2887 }
2888 default:
2889 break;
2890 }
2891 q=(char *) expression;
cristydbdd0e32011-11-04 23:29:40 +00002892 alpha=InterpretSiPrefixValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002893 if (q == expression)
2894 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2895 return(alpha);
2896}
2897
cristy7832dc22011-09-05 01:21:53 +00002898MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
cristy3ed852e2009-09-05 21:47:34 +00002899 MagickRealType *alpha,ExceptionInfo *exception)
2900{
2901 MagickBooleanType
2902 status;
2903
cristy541ae572011-08-05 19:08:59 +00002904 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2905 exception);
cristy3ed852e2009-09-05 21:47:34 +00002906 return(status);
2907}
2908
2909MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2910 MagickRealType *alpha,ExceptionInfo *exception)
2911{
2912 FILE
2913 *file;
2914
2915 MagickBooleanType
2916 status;
2917
2918 file=fx_info->file;
2919 fx_info->file=(FILE *) NULL;
cristy541ae572011-08-05 19:08:59 +00002920 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2921 exception);
cristy3ed852e2009-09-05 21:47:34 +00002922 fx_info->file=file;
2923 return(status);
2924}
2925
cristy7832dc22011-09-05 01:21:53 +00002926MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002927 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002928 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002929{
2930 MagickRealType
2931 beta;
2932
2933 beta=0.0;
2934 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2935 exception);
2936 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2937}
2938
2939/*
2940%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2941% %
2942% %
2943% %
2944% F x I m a g e %
2945% %
2946% %
2947% %
2948%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2949%
2950% FxImage() applies a mathematical expression to the specified image.
2951%
2952% The format of the FxImage method is:
2953%
2954% Image *FxImage(const Image *image,const char *expression,
2955% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002956%
2957% A description of each parameter follows:
2958%
2959% o image: the image.
2960%
cristy3ed852e2009-09-05 21:47:34 +00002961% o expression: A mathematical expression.
2962%
2963% o exception: return any errors or warnings in this structure.
2964%
2965*/
2966
2967static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2968{
cristybb503372010-05-27 20:51:26 +00002969 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002970 i;
2971
2972 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002973 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002974 if (fx_info[i] != (FxInfo *) NULL)
2975 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002976 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002977 return(fx_info);
2978}
2979
2980static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2981 ExceptionInfo *exception)
2982{
2983 char
2984 *fx_expression;
2985
2986 FxInfo
2987 **fx_info;
2988
2989 MagickRealType
2990 alpha;
2991
cristybb503372010-05-27 20:51:26 +00002992 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002993 i;
2994
cristybb503372010-05-27 20:51:26 +00002995 size_t
cristy3ed852e2009-09-05 21:47:34 +00002996 number_threads;
2997
2998 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002999 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00003000 if (fx_info == (FxInfo **) NULL)
3001 return((FxInfo **) NULL);
3002 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
3003 if (*expression != '@')
3004 fx_expression=ConstantString(expression);
3005 else
3006 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00003007 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00003008 {
3009 fx_info[i]=AcquireFxInfo(image,fx_expression);
3010 if (fx_info[i] == (FxInfo *) NULL)
3011 return(DestroyFxThreadSet(fx_info));
3012 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
3013 }
3014 fx_expression=DestroyString(fx_expression);
3015 return(fx_info);
3016}
3017
3018MagickExport Image *FxImage(const Image *image,const char *expression,
3019 ExceptionInfo *exception)
3020{
cristy3ed852e2009-09-05 21:47:34 +00003021#define FxImageTag "Fx/Image"
3022
cristyfa112112010-01-04 17:48:07 +00003023 CacheView
cristy79cedc72011-07-25 00:41:15 +00003024 *fx_view,
3025 *image_view;
cristyfa112112010-01-04 17:48:07 +00003026
cristy3ed852e2009-09-05 21:47:34 +00003027 FxInfo
cristyfa112112010-01-04 17:48:07 +00003028 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00003029
3030 Image
3031 *fx_image;
3032
cristy3ed852e2009-09-05 21:47:34 +00003033 MagickBooleanType
3034 status;
3035
cristybb503372010-05-27 20:51:26 +00003036 MagickOffsetType
3037 progress;
3038
cristy3ed852e2009-09-05 21:47:34 +00003039 MagickRealType
3040 alpha;
3041
cristybb503372010-05-27 20:51:26 +00003042 ssize_t
3043 y;
3044
cristy3ed852e2009-09-05 21:47:34 +00003045 assert(image != (Image *) NULL);
3046 assert(image->signature == MagickSignature);
3047 if (image->debug != MagickFalse)
3048 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00003049 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003050 if (fx_image == (Image *) NULL)
3051 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003052 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003053 {
cristy3ed852e2009-09-05 21:47:34 +00003054 fx_image=DestroyImage(fx_image);
3055 return((Image *) NULL);
3056 }
3057 fx_info=AcquireFxThreadSet(image,expression,exception);
3058 if (fx_info == (FxInfo **) NULL)
3059 {
3060 fx_image=DestroyImage(fx_image);
3061 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3062 }
3063 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3064 if (status == MagickFalse)
3065 {
3066 fx_image=DestroyImage(fx_image);
3067 fx_info=DestroyFxThreadSet(fx_info);
3068 return((Image *) NULL);
3069 }
3070 /*
3071 Fx image.
3072 */
3073 status=MagickTrue;
3074 progress=0;
cristy79cedc72011-07-25 00:41:15 +00003075 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003076 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003077#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003078 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003079#endif
cristybb503372010-05-27 20:51:26 +00003080 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003081 {
cristy5c9e6f22010-09-17 17:31:01 +00003082 const int
3083 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003084
cristy79cedc72011-07-25 00:41:15 +00003085 register const Quantum
3086 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003087
cristy4c08aed2011-07-01 19:47:50 +00003088 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003089 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003090
cristy79cedc72011-07-25 00:41:15 +00003091 register ssize_t
3092 x;
3093
cristy3ed852e2009-09-05 21:47:34 +00003094 if (status == MagickFalse)
3095 continue;
cristy79cedc72011-07-25 00:41:15 +00003096 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003097 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003098 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003099 {
3100 status=MagickFalse;
3101 continue;
3102 }
cristybb503372010-05-27 20:51:26 +00003103 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003104 {
cristy79cedc72011-07-25 00:41:15 +00003105 register ssize_t
3106 i;
3107
cristy10a6c612012-01-29 21:41:05 +00003108 if (GetPixelMask(image,p) != 0)
3109 {
3110 p+=GetPixelChannels(image);
3111 q+=GetPixelChannels(fx_image);
3112 continue;
3113 }
cristy79cedc72011-07-25 00:41:15 +00003114 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3115 {
3116 MagickRealType
3117 alpha;
3118
3119 PixelChannel
3120 channel;
3121
3122 PixelTrait
3123 fx_traits,
3124 traits;
3125
cristye2a912b2011-12-05 20:02:07 +00003126 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003127 traits=GetPixelChannelMapTraits(image,channel);
cristy79cedc72011-07-25 00:41:15 +00003128 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
cristy010d7d12011-08-31 01:02:48 +00003129 if ((traits == UndefinedPixelTrait) ||
3130 (fx_traits == UndefinedPixelTrait))
cristy79cedc72011-07-25 00:41:15 +00003131 continue;
3132 if ((fx_traits & CopyPixelTrait) != 0)
3133 {
cristy0beccfa2011-09-25 20:47:53 +00003134 SetPixelChannel(fx_image,channel,p[i],q);
cristy79cedc72011-07-25 00:41:15 +00003135 continue;
3136 }
3137 alpha=0.0;
cristya382aca2011-12-06 18:22:48 +00003138 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3139 exception);
cristyb3a73b52011-07-26 01:34:43 +00003140 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003141 }
3142 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003143 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003144 }
3145 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3146 status=MagickFalse;
3147 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3148 {
3149 MagickBooleanType
3150 proceed;
3151
cristyb5d5f722009-11-04 03:03:49 +00003152#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003153 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003154#endif
3155 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3156 if (proceed == MagickFalse)
3157 status=MagickFalse;
3158 }
3159 }
cristy3ed852e2009-09-05 21:47:34 +00003160 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003161 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003162 fx_info=DestroyFxThreadSet(fx_info);
3163 if (status == MagickFalse)
3164 fx_image=DestroyImage(fx_image);
3165 return(fx_image);
3166}
3167
3168/*
3169%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3170% %
3171% %
3172% %
3173% I m p l o d e I m a g e %
3174% %
3175% %
3176% %
3177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3178%
3179% ImplodeImage() creates a new image that is a copy of an existing
3180% one with the image pixels "implode" by the specified percentage. It
3181% allocates the memory necessary for the new Image structure and returns a
3182% pointer to the new image.
3183%
3184% The format of the ImplodeImage method is:
3185%
3186% Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003187% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003188%
3189% A description of each parameter follows:
3190%
3191% o implode_image: Method ImplodeImage returns a pointer to the image
3192% after it is implode. A null image is returned if there is a memory
3193% shortage.
3194%
3195% o image: the image.
3196%
3197% o amount: Define the extent of the implosion.
3198%
cristy76f512e2011-09-12 01:26:56 +00003199% o method: the pixel interpolation method.
3200%
cristy3ed852e2009-09-05 21:47:34 +00003201% o exception: return any errors or warnings in this structure.
3202%
3203*/
3204MagickExport Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003205 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003206{
3207#define ImplodeImageTag "Implode/Image"
3208
cristyfa112112010-01-04 17:48:07 +00003209 CacheView
3210 *image_view,
3211 *implode_view;
3212
cristy3ed852e2009-09-05 21:47:34 +00003213 Image
3214 *implode_image;
3215
cristy3ed852e2009-09-05 21:47:34 +00003216 MagickBooleanType
3217 status;
3218
cristybb503372010-05-27 20:51:26 +00003219 MagickOffsetType
3220 progress;
3221
cristy3ed852e2009-09-05 21:47:34 +00003222 MagickRealType
3223 radius;
3224
3225 PointInfo
3226 center,
3227 scale;
3228
cristybb503372010-05-27 20:51:26 +00003229 ssize_t
3230 y;
3231
cristy3ed852e2009-09-05 21:47:34 +00003232 /*
3233 Initialize implode image attributes.
3234 */
3235 assert(image != (Image *) NULL);
3236 assert(image->signature == MagickSignature);
3237 if (image->debug != MagickFalse)
3238 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3239 assert(exception != (ExceptionInfo *) NULL);
3240 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00003241 implode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3242 exception);
cristy3ed852e2009-09-05 21:47:34 +00003243 if (implode_image == (Image *) NULL)
3244 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003245 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003246 {
cristy3ed852e2009-09-05 21:47:34 +00003247 implode_image=DestroyImage(implode_image);
3248 return((Image *) NULL);
3249 }
cristy4c08aed2011-07-01 19:47:50 +00003250 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003251 implode_image->matte=MagickTrue;
3252 /*
3253 Compute scaling factor.
3254 */
3255 scale.x=1.0;
3256 scale.y=1.0;
3257 center.x=0.5*image->columns;
3258 center.y=0.5*image->rows;
3259 radius=center.x;
3260 if (image->columns > image->rows)
3261 scale.y=(double) image->columns/(double) image->rows;
3262 else
3263 if (image->columns < image->rows)
3264 {
3265 scale.x=(double) image->rows/(double) image->columns;
3266 radius=center.y;
3267 }
3268 /*
3269 Implode image.
3270 */
3271 status=MagickTrue;
3272 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003273 image_view=AcquireCacheView(image);
3274 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003275#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003276 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003277#endif
cristybb503372010-05-27 20:51:26 +00003278 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003279 {
cristy3ed852e2009-09-05 21:47:34 +00003280 MagickRealType
3281 distance;
3282
3283 PointInfo
3284 delta;
3285
cristy6d188022011-09-12 13:23:33 +00003286 register const Quantum
3287 *restrict p;
3288
cristybb503372010-05-27 20:51:26 +00003289 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003290 x;
3291
cristy4c08aed2011-07-01 19:47:50 +00003292 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003293 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003294
3295 if (status == MagickFalse)
3296 continue;
cristy6d188022011-09-12 13:23:33 +00003297 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003298 q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003299 exception);
cristy6d188022011-09-12 13:23:33 +00003300 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003301 {
3302 status=MagickFalse;
3303 continue;
3304 }
cristy3ed852e2009-09-05 21:47:34 +00003305 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00003306 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003307 {
cristy6d188022011-09-12 13:23:33 +00003308 register ssize_t
3309 i;
3310
cristy3ed852e2009-09-05 21:47:34 +00003311 /*
3312 Determine if the pixel is within an ellipse.
3313 */
cristy10a6c612012-01-29 21:41:05 +00003314 if (GetPixelMask(image,p) != 0)
3315 {
3316 p+=GetPixelChannels(image);
3317 q+=GetPixelChannels(implode_image);
3318 continue;
3319 }
cristy3ed852e2009-09-05 21:47:34 +00003320 delta.x=scale.x*(double) (x-center.x);
3321 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00003322 if (distance >= (radius*radius))
cristya6d7a9b2012-01-18 20:04:48 +00003323 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy6d188022011-09-12 13:23:33 +00003324 {
cristya6d7a9b2012-01-18 20:04:48 +00003325 PixelChannel
3326 channel;
3327
cristy1707c6c2012-01-18 23:30:54 +00003328 PixelTrait
3329 implode_traits,
3330 traits;
3331
cristya6d7a9b2012-01-18 20:04:48 +00003332 channel=GetPixelChannelMapChannel(image,i);
cristy1707c6c2012-01-18 23:30:54 +00003333 traits=GetPixelChannelMapTraits(image,channel);
3334 implode_traits=GetPixelChannelMapTraits(implode_image,channel);
3335 if ((traits == UndefinedPixelTrait) ||
3336 (implode_traits == UndefinedPixelTrait))
3337 continue;
cristya6d7a9b2012-01-18 20:04:48 +00003338 SetPixelChannel(implode_image,channel,p[i],q);
cristy6d188022011-09-12 13:23:33 +00003339 }
3340 else
cristy3ed852e2009-09-05 21:47:34 +00003341 {
3342 double
3343 factor;
3344
3345 /*
3346 Implode the pixel.
3347 */
3348 factor=1.0;
3349 if (distance > 0.0)
cristy1707c6c2012-01-18 23:30:54 +00003350 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/radius/
3351 2)),-amount);
cristy76f512e2011-09-12 01:26:56 +00003352 status=InterpolatePixelChannels(image,image_view,implode_image,method,
3353 (double) (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3354 scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00003355 }
cristy6d188022011-09-12 13:23:33 +00003356 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003357 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003358 }
3359 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3360 status=MagickFalse;
3361 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3362 {
3363 MagickBooleanType
3364 proceed;
3365
cristyb5d5f722009-11-04 03:03:49 +00003366#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003367 #pragma omp critical (MagickCore_ImplodeImage)
cristy3ed852e2009-09-05 21:47:34 +00003368#endif
3369 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3370 if (proceed == MagickFalse)
3371 status=MagickFalse;
3372 }
3373 }
3374 implode_view=DestroyCacheView(implode_view);
3375 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003376 if (status == MagickFalse)
3377 implode_image=DestroyImage(implode_image);
3378 return(implode_image);
3379}
3380
3381/*
3382%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3383% %
3384% %
3385% %
3386% M o r p h I m a g e s %
3387% %
3388% %
3389% %
3390%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3391%
3392% The MorphImages() method requires a minimum of two images. The first
3393% image is transformed into the second by a number of intervening images
3394% as specified by frames.
3395%
3396% The format of the MorphImage method is:
3397%
cristybb503372010-05-27 20:51:26 +00003398% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003399% ExceptionInfo *exception)
3400%
3401% A description of each parameter follows:
3402%
3403% o image: the image.
3404%
3405% o number_frames: Define the number of in-between image to generate.
3406% The more in-between frames, the smoother the morph.
3407%
3408% o exception: return any errors or warnings in this structure.
3409%
3410*/
3411MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003412 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003413{
3414#define MorphImageTag "Morph/Image"
3415
3416 Image
3417 *morph_image,
3418 *morph_images;
3419
cristy9d314ff2011-03-09 01:30:28 +00003420 MagickBooleanType
3421 status;
cristy3ed852e2009-09-05 21:47:34 +00003422
3423 MagickOffsetType
3424 scene;
3425
3426 MagickRealType
3427 alpha,
3428 beta;
3429
3430 register const Image
3431 *next;
3432
cristybb503372010-05-27 20:51:26 +00003433 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003434 i;
3435
cristy9d314ff2011-03-09 01:30:28 +00003436 ssize_t
3437 y;
cristy3ed852e2009-09-05 21:47:34 +00003438
3439 /*
3440 Clone first frame in sequence.
3441 */
3442 assert(image != (Image *) NULL);
3443 assert(image->signature == MagickSignature);
3444 if (image->debug != MagickFalse)
3445 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3446 assert(exception != (ExceptionInfo *) NULL);
3447 assert(exception->signature == MagickSignature);
3448 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3449 if (morph_images == (Image *) NULL)
3450 return((Image *) NULL);
3451 if (GetNextImageInList(image) == (Image *) NULL)
3452 {
3453 /*
3454 Morph single image.
3455 */
cristybb503372010-05-27 20:51:26 +00003456 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003457 {
3458 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3459 if (morph_image == (Image *) NULL)
3460 {
3461 morph_images=DestroyImageList(morph_images);
3462 return((Image *) NULL);
3463 }
3464 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003465 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003466 {
cristy8b27a6d2010-02-14 03:31:15 +00003467 MagickBooleanType
3468 proceed;
3469
cristybb503372010-05-27 20:51:26 +00003470 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3471 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003472 if (proceed == MagickFalse)
3473 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003474 }
3475 }
3476 return(GetFirstImageInList(morph_images));
3477 }
3478 /*
3479 Morph image sequence.
3480 */
3481 status=MagickTrue;
3482 scene=0;
3483 next=image;
3484 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3485 {
cristybb503372010-05-27 20:51:26 +00003486 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003487 {
3488 CacheView
3489 *image_view,
3490 *morph_view;
3491
3492 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3493 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003494 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristybb503372010-05-27 20:51:26 +00003495 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
cristy15b98cd2010-09-12 19:42:50 +00003496 next->rows+beta*GetNextImageInList(next)->rows+0.5),
3497 next->filter,next->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003498 if (morph_image == (Image *) NULL)
3499 {
3500 morph_images=DestroyImageList(morph_images);
3501 return((Image *) NULL);
3502 }
cristy1707c6c2012-01-18 23:30:54 +00003503 status=SetImageStorageClass(morph_image,DirectClass,exception);
3504 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003505 {
cristy3ed852e2009-09-05 21:47:34 +00003506 morph_image=DestroyImage(morph_image);
3507 return((Image *) NULL);
3508 }
3509 AppendImageToList(&morph_images,morph_image);
3510 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003511 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3512 morph_images->rows,GetNextImageInList(next)->filter,
3513 GetNextImageInList(next)->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003514 if (morph_image == (Image *) NULL)
3515 {
3516 morph_images=DestroyImageList(morph_images);
3517 return((Image *) NULL);
3518 }
3519 image_view=AcquireCacheView(morph_image);
3520 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003521#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003522 #pragma omp parallel for schedule(static,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003523#endif
cristybb503372010-05-27 20:51:26 +00003524 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003525 {
3526 MagickBooleanType
3527 sync;
3528
cristy4c08aed2011-07-01 19:47:50 +00003529 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003530 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003531
cristybb503372010-05-27 20:51:26 +00003532 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003533 x;
3534
cristy4c08aed2011-07-01 19:47:50 +00003535 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003536 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003537
3538 if (status == MagickFalse)
3539 continue;
3540 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3541 exception);
3542 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3543 exception);
cristy4c08aed2011-07-01 19:47:50 +00003544 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003545 {
3546 status=MagickFalse;
3547 continue;
3548 }
cristybb503372010-05-27 20:51:26 +00003549 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003550 {
cristy1707c6c2012-01-18 23:30:54 +00003551 register ssize_t
3552 i;
3553
cristy10a6c612012-01-29 21:41:05 +00003554 if (GetPixelMask(morph_image,p) != 0)
3555 {
3556 p+=GetPixelChannels(morph_image);
3557 continue;
3558 }
3559 for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
cristy1707c6c2012-01-18 23:30:54 +00003560 {
3561 PixelChannel
3562 channel;
3563
3564 PixelTrait
3565 morph_traits,
3566 traits;
3567
3568 channel=GetPixelChannelMapChannel(image,i);
3569 traits=GetPixelChannelMapTraits(image,channel);
3570 morph_traits=GetPixelChannelMapTraits(morph_image,channel);
3571 if ((traits == UndefinedPixelTrait) ||
3572 (morph_traits == UndefinedPixelTrait))
3573 continue;
3574 if ((morph_traits & CopyPixelTrait) != 0)
3575 {
3576 SetPixelChannel(morph_image,channel,p[i],q);
3577 continue;
3578 }
3579 SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3580 GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3581 }
cristyed231572011-07-14 02:18:59 +00003582 p+=GetPixelChannels(morph_image);
3583 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003584 }
3585 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3586 if (sync == MagickFalse)
3587 status=MagickFalse;
3588 }
3589 morph_view=DestroyCacheView(morph_view);
3590 image_view=DestroyCacheView(image_view);
3591 morph_image=DestroyImage(morph_image);
3592 }
cristybb503372010-05-27 20:51:26 +00003593 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003594 break;
3595 /*
3596 Clone last frame in sequence.
3597 */
3598 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3599 if (morph_image == (Image *) NULL)
3600 {
3601 morph_images=DestroyImageList(morph_images);
3602 return((Image *) NULL);
3603 }
3604 AppendImageToList(&morph_images,morph_image);
3605 morph_images=GetLastImageInList(morph_images);
3606 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3607 {
3608 MagickBooleanType
3609 proceed;
3610
cristyb5d5f722009-11-04 03:03:49 +00003611#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003612 #pragma omp critical (MagickCore_MorphImages)
cristy3ed852e2009-09-05 21:47:34 +00003613#endif
3614 proceed=SetImageProgress(image,MorphImageTag,scene,
3615 GetImageListLength(image));
3616 if (proceed == MagickFalse)
3617 status=MagickFalse;
3618 }
3619 scene++;
3620 }
3621 if (GetNextImageInList(next) != (Image *) NULL)
3622 {
3623 morph_images=DestroyImageList(morph_images);
3624 return((Image *) NULL);
3625 }
3626 return(GetFirstImageInList(morph_images));
3627}
3628
3629/*
3630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3631% %
3632% %
3633% %
3634% P l a s m a I m a g e %
3635% %
3636% %
3637% %
3638%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3639%
3640% PlasmaImage() initializes an image with plasma fractal values. The image
3641% must be initialized with a base color and the random number generator
3642% seeded before this method is called.
3643%
3644% The format of the PlasmaImage method is:
3645%
3646% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristy5cbc0162011-08-29 00:36:28 +00003647% size_t attenuate,size_t depth,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003648%
3649% A description of each parameter follows:
3650%
3651% o image: the image.
3652%
3653% o segment: Define the region to apply plasma fractals values.
3654%
glennrp7dae1ca2010-09-16 12:17:35 +00003655% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003656%
3657% o depth: Limit the plasma recursion depth.
3658%
cristy5cbc0162011-08-29 00:36:28 +00003659% o exception: return any errors or warnings in this structure.
3660%
cristy3ed852e2009-09-05 21:47:34 +00003661*/
3662
3663static inline Quantum PlasmaPixel(RandomInfo *random_info,
3664 const MagickRealType pixel,const MagickRealType noise)
3665{
3666 Quantum
3667 plasma;
3668
cristyce70c172010-01-07 17:15:30 +00003669 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003670 noise/2.0);
3671 return(plasma);
3672}
3673
cristyda1f9c12011-10-02 21:39:49 +00003674static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
3675 CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3676 const SegmentInfo *segment,size_t attenuate,size_t depth,
3677 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003678{
cristy3ed852e2009-09-05 21:47:34 +00003679 MagickRealType
3680 plasma;
3681
cristyda1f9c12011-10-02 21:39:49 +00003682 PixelChannel
3683 channel;
3684
3685 PixelTrait
3686 traits;
3687
3688 register const Quantum
3689 *restrict u,
3690 *restrict v;
3691
3692 register Quantum
3693 *restrict q;
3694
3695 register ssize_t
3696 i;
cristy3ed852e2009-09-05 21:47:34 +00003697
cristy9d314ff2011-03-09 01:30:28 +00003698 ssize_t
3699 x,
3700 x_mid,
3701 y,
3702 y_mid;
3703
cristy3ed852e2009-09-05 21:47:34 +00003704 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3705 return(MagickTrue);
3706 if (depth != 0)
3707 {
3708 SegmentInfo
3709 local_info;
3710
3711 /*
3712 Divide the area into quadrants and recurse.
3713 */
3714 depth--;
3715 attenuate++;
cristybb503372010-05-27 20:51:26 +00003716 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3717 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003718 local_info=(*segment);
3719 local_info.x2=(double) x_mid;
3720 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003721 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3722 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003723 local_info=(*segment);
3724 local_info.y1=(double) y_mid;
3725 local_info.x2=(double) x_mid;
cristyda1f9c12011-10-02 21:39:49 +00003726 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3727 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003728 local_info=(*segment);
3729 local_info.x1=(double) x_mid;
3730 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003731 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3732 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003733 local_info=(*segment);
3734 local_info.x1=(double) x_mid;
3735 local_info.y1=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003736 return(PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3737 &local_info,attenuate,depth,exception));
cristy3ed852e2009-09-05 21:47:34 +00003738 }
cristybb503372010-05-27 20:51:26 +00003739 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3740 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003741 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3742 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3743 return(MagickFalse);
3744 /*
3745 Average pixels and apply plasma.
3746 */
cristy3ed852e2009-09-05 21:47:34 +00003747 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3748 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3749 {
cristy3ed852e2009-09-05 21:47:34 +00003750 /*
3751 Left pixel.
3752 */
cristybb503372010-05-27 20:51:26 +00003753 x=(ssize_t) ceil(segment->x1-0.5);
cristy1707c6c2012-01-18 23:30:54 +00003754 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3755 exception);
3756 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3757 exception);
cristyc5c6f662010-09-22 14:23:02 +00003758 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003759 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3760 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003761 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003762 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3763 {
cristye2a912b2011-12-05 20:02:07 +00003764 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003765 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003766 if (traits == UndefinedPixelTrait)
3767 continue;
3768 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3769 }
cristyc5c6f662010-09-22 14:23:02 +00003770 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003771 if (segment->x1 != segment->x2)
3772 {
3773 /*
3774 Right pixel.
3775 */
cristybb503372010-05-27 20:51:26 +00003776 x=(ssize_t) ceil(segment->x2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003777 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3778 1,1,exception);
3779 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3780 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003781 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003782 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3783 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003784 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003785 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3786 {
cristye2a912b2011-12-05 20:02:07 +00003787 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003788 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003789 if (traits == UndefinedPixelTrait)
3790 continue;
3791 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3792 }
cristyc5c6f662010-09-22 14:23:02 +00003793 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003794 }
3795 }
3796 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3797 {
3798 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3799 {
cristy3ed852e2009-09-05 21:47:34 +00003800 /*
3801 Bottom pixel.
3802 */
cristybb503372010-05-27 20:51:26 +00003803 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003804 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3805 1,1,exception);
3806 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3807 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003808 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003809 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3810 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003811 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003812 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3813 {
cristye2a912b2011-12-05 20:02:07 +00003814 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003815 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003816 if (traits == UndefinedPixelTrait)
3817 continue;
3818 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3819 }
cristyc5c6f662010-09-22 14:23:02 +00003820 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003821 }
3822 if (segment->y1 != segment->y2)
3823 {
cristy3ed852e2009-09-05 21:47:34 +00003824 /*
3825 Top pixel.
3826 */
cristybb503372010-05-27 20:51:26 +00003827 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003828 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3829 1,1,exception);
3830 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3831 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003832 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003833 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3834 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003835 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003836 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3837 {
cristye2a912b2011-12-05 20:02:07 +00003838 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003839 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003840 if (traits == UndefinedPixelTrait)
3841 continue;
3842 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3843 }
cristyc5c6f662010-09-22 14:23:02 +00003844 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003845 }
3846 }
3847 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3848 {
cristy3ed852e2009-09-05 21:47:34 +00003849 /*
3850 Middle pixel.
3851 */
cristybb503372010-05-27 20:51:26 +00003852 x=(ssize_t) ceil(segment->x1-0.5);
3853 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003854 u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
cristybb503372010-05-27 20:51:26 +00003855 x=(ssize_t) ceil(segment->x2-0.5);
3856 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003857 v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003858 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003859 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3860 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003861 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003862 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3863 {
cristye2a912b2011-12-05 20:02:07 +00003864 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003865 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003866 if (traits == UndefinedPixelTrait)
3867 continue;
3868 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3869 }
cristyc5c6f662010-09-22 14:23:02 +00003870 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003871 }
3872 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3873 return(MagickTrue);
3874 return(MagickFalse);
3875}
cristyda1f9c12011-10-02 21:39:49 +00003876
cristy3ed852e2009-09-05 21:47:34 +00003877MagickExport MagickBooleanType PlasmaImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00003878 const SegmentInfo *segment,size_t attenuate,size_t depth,
3879 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003880{
cristyc5c6f662010-09-22 14:23:02 +00003881 CacheView
cristyda1f9c12011-10-02 21:39:49 +00003882 *image_view,
3883 *u_view,
3884 *v_view;
cristyc5c6f662010-09-22 14:23:02 +00003885
cristy3ed852e2009-09-05 21:47:34 +00003886 MagickBooleanType
3887 status;
3888
3889 RandomInfo
3890 *random_info;
3891
3892 if (image->debug != MagickFalse)
3893 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3894 assert(image != (Image *) NULL);
3895 assert(image->signature == MagickSignature);
3896 if (image->debug != MagickFalse)
3897 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy5cbc0162011-08-29 00:36:28 +00003898 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristyc5c6f662010-09-22 14:23:02 +00003899 return(MagickFalse);
3900 image_view=AcquireCacheView(image);
cristyda1f9c12011-10-02 21:39:49 +00003901 u_view=AcquireCacheView(image);
3902 v_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003903 random_info=AcquireRandomInfo();
cristyda1f9c12011-10-02 21:39:49 +00003904 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
3905 attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003906 random_info=DestroyRandomInfo(random_info);
cristyda1f9c12011-10-02 21:39:49 +00003907 v_view=DestroyCacheView(v_view);
3908 u_view=DestroyCacheView(u_view);
cristyc5c6f662010-09-22 14:23:02 +00003909 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003910 return(status);
3911}
3912
3913/*
3914%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3915% %
3916% %
3917% %
3918% P o l a r o i d I m a g e %
3919% %
3920% %
3921% %
3922%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3923%
3924% PolaroidImage() simulates a Polaroid picture.
3925%
3926% The format of the AnnotateImage method is:
3927%
3928% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003929% const char *caption,const double angle,
3930% const PixelInterpolateMethod method,ExceptionInfo exception)
cristy3ed852e2009-09-05 21:47:34 +00003931%
3932% A description of each parameter follows:
3933%
3934% o image: the image.
3935%
3936% o draw_info: the draw info.
3937%
cristye9e3d382011-12-14 01:50:13 +00003938% o caption: the Polaroid caption.
3939%
cristycee97112010-05-28 00:44:52 +00003940% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003941%
cristy5c4e2582011-09-11 19:21:03 +00003942% o method: the pixel interpolation method.
3943%
cristy3ed852e2009-09-05 21:47:34 +00003944% o exception: return any errors or warnings in this structure.
3945%
3946*/
3947MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003948 const char *caption,const double angle,const PixelInterpolateMethod method,
cristy5c4e2582011-09-11 19:21:03 +00003949 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003950{
cristy3ed852e2009-09-05 21:47:34 +00003951 Image
3952 *bend_image,
3953 *caption_image,
3954 *flop_image,
3955 *picture_image,
3956 *polaroid_image,
3957 *rotate_image,
3958 *trim_image;
3959
cristybb503372010-05-27 20:51:26 +00003960 size_t
cristy3ed852e2009-09-05 21:47:34 +00003961 height;
3962
cristy9d314ff2011-03-09 01:30:28 +00003963 ssize_t
3964 quantum;
3965
cristy3ed852e2009-09-05 21:47:34 +00003966 /*
3967 Simulate a Polaroid picture.
3968 */
3969 assert(image != (Image *) NULL);
3970 assert(image->signature == MagickSignature);
3971 if (image->debug != MagickFalse)
3972 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3973 assert(exception != (ExceptionInfo *) NULL);
3974 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003975 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003976 image->rows)/25.0,10.0);
3977 height=image->rows+2*quantum;
3978 caption_image=(Image *) NULL;
cristye9e3d382011-12-14 01:50:13 +00003979 if (caption != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003980 {
3981 char
cristye9e3d382011-12-14 01:50:13 +00003982 geometry[MaxTextExtent],
3983 *text;
cristy3ed852e2009-09-05 21:47:34 +00003984
3985 DrawInfo
3986 *annotate_info;
3987
cristy3ed852e2009-09-05 21:47:34 +00003988 MagickBooleanType
3989 status;
3990
cristy9d314ff2011-03-09 01:30:28 +00003991 ssize_t
3992 count;
3993
cristy3ed852e2009-09-05 21:47:34 +00003994 TypeMetric
3995 metrics;
3996
3997 /*
3998 Generate caption image.
3999 */
4000 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
4001 if (caption_image == (Image *) NULL)
4002 return((Image *) NULL);
4003 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
cristye9e3d382011-12-14 01:50:13 +00004004 text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
4005 exception);
4006 (void) CloneString(&annotate_info->text,text);
cristy6b1d05e2010-09-22 19:17:27 +00004007 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
cristye9e3d382011-12-14 01:50:13 +00004008 &text,exception);
4009 status=SetImageExtent(caption_image,image->columns,(size_t) ((count+1)*
4010 (metrics.ascent-metrics.descent)+0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00004011 if (status == MagickFalse)
4012 caption_image=DestroyImage(caption_image);
4013 else
4014 {
4015 caption_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004016 (void) SetImageBackgroundColor(caption_image,exception);
cristye9e3d382011-12-14 01:50:13 +00004017 (void) CloneString(&annotate_info->text,text);
cristyb51dff52011-05-19 16:55:47 +00004018 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00004019 metrics.ascent);
4020 if (annotate_info->gravity == UndefinedGravity)
4021 (void) CloneString(&annotate_info->geometry,AcquireString(
4022 geometry));
cristy5cbc0162011-08-29 00:36:28 +00004023 (void) AnnotateImage(caption_image,annotate_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004024 height+=caption_image->rows;
4025 }
4026 annotate_info=DestroyDrawInfo(annotate_info);
cristye9e3d382011-12-14 01:50:13 +00004027 text=DestroyString(text);
cristy3ed852e2009-09-05 21:47:34 +00004028 }
4029 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4030 exception);
4031 if (picture_image == (Image *) NULL)
4032 {
4033 if (caption_image != (Image *) NULL)
4034 caption_image=DestroyImage(caption_image);
4035 return((Image *) NULL);
4036 }
4037 picture_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004038 (void) SetImageBackgroundColor(picture_image,exception);
cristye941a752011-10-15 01:52:48 +00004039 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum,
4040 exception);
cristy3ed852e2009-09-05 21:47:34 +00004041 if (caption_image != (Image *) NULL)
4042 {
cristy1707c6c2012-01-18 23:30:54 +00004043 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,quantum,
4044 (ssize_t) (image->rows+3*quantum/2),exception);
cristy3ed852e2009-09-05 21:47:34 +00004045 caption_image=DestroyImage(caption_image);
4046 }
cristy9950d572011-10-01 18:22:35 +00004047 (void) QueryColorCompliance("none",AllCompliance,
4048 &picture_image->background_color,exception);
cristy63240882011-08-05 19:05:27 +00004049 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004050 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 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
cristy5c4e2582011-09-11 19:21:03 +00004056 picture_image->columns,method,exception);
cristy3ed852e2009-09-05 21:47:34 +00004057 picture_image=DestroyImage(picture_image);
4058 if (bend_image == (Image *) NULL)
4059 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004060 picture_image=bend_image;
4061 rotate_image=RotateImage(picture_image,-90.0,exception);
4062 picture_image=DestroyImage(picture_image);
4063 if (rotate_image == (Image *) NULL)
4064 return((Image *) NULL);
4065 picture_image=rotate_image;
4066 picture_image->background_color=image->background_color;
cristy78ec1a92011-12-09 09:25:34 +00004067 polaroid_image=ShadowImage(picture_image,80.0,2.0,0.0,quantum/3,quantum/3,
cristy3ed852e2009-09-05 21:47:34 +00004068 exception);
4069 if (polaroid_image == (Image *) NULL)
4070 {
4071 picture_image=DestroyImage(picture_image);
4072 return(picture_image);
4073 }
4074 flop_image=FlopImage(polaroid_image,exception);
4075 polaroid_image=DestroyImage(polaroid_image);
4076 if (flop_image == (Image *) NULL)
4077 {
4078 picture_image=DestroyImage(picture_image);
4079 return(picture_image);
4080 }
4081 polaroid_image=flop_image;
cristy1707c6c2012-01-18 23:30:54 +00004082 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,(ssize_t)
4083 (-0.01*picture_image->columns/2.0),0L,exception);
cristy3ed852e2009-09-05 21:47:34 +00004084 picture_image=DestroyImage(picture_image);
cristy9950d572011-10-01 18:22:35 +00004085 (void) QueryColorCompliance("none",AllCompliance,
4086 &polaroid_image->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00004087 rotate_image=RotateImage(polaroid_image,angle,exception);
4088 polaroid_image=DestroyImage(polaroid_image);
4089 if (rotate_image == (Image *) NULL)
4090 return((Image *) NULL);
4091 polaroid_image=rotate_image;
4092 trim_image=TrimImage(polaroid_image,exception);
4093 polaroid_image=DestroyImage(polaroid_image);
4094 if (trim_image == (Image *) NULL)
4095 return((Image *) NULL);
4096 polaroid_image=trim_image;
4097 return(polaroid_image);
4098}
4099
4100/*
4101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4102% %
4103% %
4104% %
cristy3ed852e2009-09-05 21:47:34 +00004105% S e p i a T o n e I m a g e %
4106% %
4107% %
4108% %
4109%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4110%
4111% MagickSepiaToneImage() applies a special effect to the image, similar to the
4112% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4113% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4114% threshold of 80% is a good starting point for a reasonable tone.
4115%
4116% The format of the SepiaToneImage method is:
4117%
4118% Image *SepiaToneImage(const Image *image,const double threshold,
4119% ExceptionInfo *exception)
4120%
4121% A description of each parameter follows:
4122%
4123% o image: the image.
4124%
4125% o threshold: the tone threshold.
4126%
4127% o exception: return any errors or warnings in this structure.
4128%
4129*/
4130MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4131 ExceptionInfo *exception)
4132{
4133#define SepiaToneImageTag "SepiaTone/Image"
4134
cristyc4c8d132010-01-07 01:58:38 +00004135 CacheView
4136 *image_view,
4137 *sepia_view;
4138
cristy3ed852e2009-09-05 21:47:34 +00004139 Image
4140 *sepia_image;
4141
cristy3ed852e2009-09-05 21:47:34 +00004142 MagickBooleanType
4143 status;
4144
cristybb503372010-05-27 20:51:26 +00004145 MagickOffsetType
4146 progress;
4147
4148 ssize_t
4149 y;
4150
cristy3ed852e2009-09-05 21:47:34 +00004151 /*
4152 Initialize sepia-toned image attributes.
4153 */
4154 assert(image != (const Image *) NULL);
4155 assert(image->signature == MagickSignature);
4156 if (image->debug != MagickFalse)
4157 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4158 assert(exception != (ExceptionInfo *) NULL);
4159 assert(exception->signature == MagickSignature);
cristy1707c6c2012-01-18 23:30:54 +00004160 sepia_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004161 if (sepia_image == (Image *) NULL)
4162 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004163 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004164 {
cristy3ed852e2009-09-05 21:47:34 +00004165 sepia_image=DestroyImage(sepia_image);
4166 return((Image *) NULL);
4167 }
4168 /*
4169 Tone each row of the image.
4170 */
4171 status=MagickTrue;
4172 progress=0;
4173 image_view=AcquireCacheView(image);
4174 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004175#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004176 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004177#endif
cristybb503372010-05-27 20:51:26 +00004178 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004179 {
cristy4c08aed2011-07-01 19:47:50 +00004180 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004181 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004182
cristybb503372010-05-27 20:51:26 +00004183 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004184 x;
4185
cristy4c08aed2011-07-01 19:47:50 +00004186 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004187 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004188
4189 if (status == MagickFalse)
4190 continue;
4191 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00004192 q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004193 exception);
cristy4c08aed2011-07-01 19:47:50 +00004194 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004195 {
4196 status=MagickFalse;
4197 continue;
4198 }
cristybb503372010-05-27 20:51:26 +00004199 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004200 {
4201 MagickRealType
4202 intensity,
4203 tone;
4204
cristy4c08aed2011-07-01 19:47:50 +00004205 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004206 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4207 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004208 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004209 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4210 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004211 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004212 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004213 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004214 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004215 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4216 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4217 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4218 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004219 p+=GetPixelChannels(image);
4220 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004221 }
4222 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4223 status=MagickFalse;
4224 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4225 {
4226 MagickBooleanType
4227 proceed;
4228
cristyb5d5f722009-11-04 03:03:49 +00004229#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004230 #pragma omp critical (MagickCore_SepiaToneImage)
cristy3ed852e2009-09-05 21:47:34 +00004231#endif
4232 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4233 image->rows);
4234 if (proceed == MagickFalse)
4235 status=MagickFalse;
4236 }
4237 }
4238 sepia_view=DestroyCacheView(sepia_view);
4239 image_view=DestroyCacheView(image_view);
cristye23ec9d2011-08-16 18:15:40 +00004240 (void) NormalizeImage(sepia_image,exception);
4241 (void) ContrastImage(sepia_image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004242 if (status == MagickFalse)
4243 sepia_image=DestroyImage(sepia_image);
4244 return(sepia_image);
4245}
4246
4247/*
4248%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4249% %
4250% %
4251% %
4252% S h a d o w I m a g e %
4253% %
4254% %
4255% %
4256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4257%
4258% ShadowImage() simulates a shadow from the specified image and returns it.
4259%
4260% The format of the ShadowImage method is:
4261%
cristy70cddf72011-12-10 22:42:42 +00004262% Image *ShadowImage(const Image *image,const double alpha,
cristyeb6e6582011-12-09 09:14:23 +00004263% const double sigma,const double bias,const ssize_t x_offset,
4264% const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004265%
4266% A description of each parameter follows:
4267%
4268% o image: the image.
4269%
cristy70cddf72011-12-10 22:42:42 +00004270% o alpha: percentage transparency.
cristy3ed852e2009-09-05 21:47:34 +00004271%
4272% o sigma: the standard deviation of the Gaussian, in pixels.
4273%
cristyeb6e6582011-12-09 09:14:23 +00004274% o bias: the bias.
4275%
cristy3ed852e2009-09-05 21:47:34 +00004276% o x_offset: the shadow x-offset.
4277%
4278% o y_offset: the shadow y-offset.
4279%
4280% o exception: return any errors or warnings in this structure.
4281%
4282*/
cristy70cddf72011-12-10 22:42:42 +00004283MagickExport Image *ShadowImage(const Image *image,const double alpha,
cristyeb6e6582011-12-09 09:14:23 +00004284 const double sigma,const double bias,const ssize_t x_offset,
4285 const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004286{
4287#define ShadowImageTag "Shadow/Image"
4288
cristy70cddf72011-12-10 22:42:42 +00004289 CacheView
4290 *image_view;
4291
cristybd5a96c2011-08-21 00:04:26 +00004292 ChannelType
4293 channel_mask;
4294
cristy3ed852e2009-09-05 21:47:34 +00004295 Image
4296 *border_image,
4297 *clone_image,
4298 *shadow_image;
4299
cristy70cddf72011-12-10 22:42:42 +00004300 MagickBooleanType
4301 status;
4302
cristy3ed852e2009-09-05 21:47:34 +00004303 RectangleInfo
4304 border_info;
4305
cristy70cddf72011-12-10 22:42:42 +00004306 ssize_t
4307 y;
4308
cristy3ed852e2009-09-05 21:47:34 +00004309 assert(image != (Image *) NULL);
4310 assert(image->signature == MagickSignature);
4311 if (image->debug != MagickFalse)
4312 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4313 assert(exception != (ExceptionInfo *) NULL);
4314 assert(exception->signature == MagickSignature);
4315 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4316 if (clone_image == (Image *) NULL)
4317 return((Image *) NULL);
4318 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
cristybb503372010-05-27 20:51:26 +00004319 border_info.width=(size_t) floor(2.0*sigma+0.5);
4320 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004321 border_info.x=0;
4322 border_info.y=0;
cristy9950d572011-10-01 18:22:35 +00004323 (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4324 exception);
cristy70cddf72011-12-10 22:42:42 +00004325 clone_image->matte=MagickTrue;
4326 border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
cristy3ed852e2009-09-05 21:47:34 +00004327 clone_image=DestroyImage(clone_image);
4328 if (border_image == (Image *) NULL)
4329 return((Image *) NULL);
4330 if (border_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004331 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004332 /*
4333 Shadow image.
4334 */
cristy70cddf72011-12-10 22:42:42 +00004335 status=MagickTrue;
4336 image_view=AcquireCacheView(border_image);
4337 for (y=0; y < (ssize_t) border_image->rows; y++)
4338 {
4339 PixelInfo
4340 background_color;
4341
4342 register Quantum
4343 *restrict q;
4344
4345 register ssize_t
4346 x;
4347
4348 if (status == MagickFalse)
4349 continue;
4350 q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4351 exception);
4352 if (q == (Quantum *) NULL)
4353 {
4354 status=MagickFalse;
4355 continue;
4356 }
4357 background_color=border_image->background_color;
4358 background_color.matte=MagickTrue;
4359 for (x=0; x < (ssize_t) border_image->columns; x++)
4360 {
4361 if (border_image->matte != MagickFalse)
4362 background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4363 SetPixelInfoPixel(border_image,&background_color,q);
4364 q+=GetPixelChannels(border_image);
4365 }
4366 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4367 status=MagickFalse;
4368 }
4369 image_view=DestroyCacheView(image_view);
4370 if (status == MagickFalse)
4371 {
4372 border_image=DestroyImage(border_image);
4373 return((Image *) NULL);
4374 }
cristybd5a96c2011-08-21 00:04:26 +00004375 channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
cristyb27bb772011-12-11 16:12:50 +00004376 shadow_image=BlurImage(border_image,0.0,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00004377 border_image=DestroyImage(border_image);
4378 if (shadow_image == (Image *) NULL)
4379 return((Image *) NULL);
cristyae1969f2011-12-10 03:07:36 +00004380 (void) SetPixelChannelMapMask(shadow_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004381 if (shadow_image->page.width == 0)
4382 shadow_image->page.width=shadow_image->columns;
4383 if (shadow_image->page.height == 0)
4384 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004385 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4386 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4387 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4388 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004389 return(shadow_image);
4390}
4391
4392/*
4393%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4394% %
4395% %
4396% %
4397% S k e t c h I m a g e %
4398% %
4399% %
4400% %
4401%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4402%
4403% SketchImage() simulates a pencil sketch. We convolve the image with a
4404% Gaussian operator of the given radius and standard deviation (sigma). For
4405% reasonable results, radius should be larger than sigma. Use a radius of 0
4406% and SketchImage() selects a suitable radius for you. Angle gives the angle
4407% of the sketch.
4408%
4409% The format of the SketchImage method is:
4410%
4411% Image *SketchImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00004412% const double sigma,const double angle,const double bias,
4413% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004414%
4415% A description of each parameter follows:
4416%
4417% o image: the image.
4418%
cristy574cc262011-08-05 01:23:58 +00004419% o radius: the radius of the Gaussian, in pixels, not counting the
4420% center pixel.
cristy3ed852e2009-09-05 21:47:34 +00004421%
4422% o sigma: the standard deviation of the Gaussian, in pixels.
4423%
cristyf7ef0252011-09-09 14:50:06 +00004424% o angle: apply the effect along this angle.
4425%
4426% o bias: the bias.
cristy3ed852e2009-09-05 21:47:34 +00004427%
4428% o exception: return any errors or warnings in this structure.
4429%
4430*/
4431MagickExport Image *SketchImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00004432 const double sigma,const double angle,const double bias,
4433 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004434{
cristyfa112112010-01-04 17:48:07 +00004435 CacheView
4436 *random_view;
4437
cristy3ed852e2009-09-05 21:47:34 +00004438 Image
4439 *blend_image,
4440 *blur_image,
4441 *dodge_image,
4442 *random_image,
4443 *sketch_image;
4444
cristy3ed852e2009-09-05 21:47:34 +00004445 MagickBooleanType
4446 status;
4447
cristy3ed852e2009-09-05 21:47:34 +00004448 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004449 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004450
cristy9d314ff2011-03-09 01:30:28 +00004451 ssize_t
4452 y;
4453
cristy3ed852e2009-09-05 21:47:34 +00004454 /*
4455 Sketch image.
4456 */
4457 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4458 MagickTrue,exception);
4459 if (random_image == (Image *) NULL)
4460 return((Image *) NULL);
4461 status=MagickTrue;
cristy1b784432009-12-19 02:20:40 +00004462 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004463 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004464#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004465 #pragma omp parallel for schedule(static,4) shared(status)
cristy1b784432009-12-19 02:20:40 +00004466#endif
cristybb503372010-05-27 20:51:26 +00004467 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004468 {
cristy5c9e6f22010-09-17 17:31:01 +00004469 const int
4470 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004471
cristybb503372010-05-27 20:51:26 +00004472 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004473 x;
4474
cristy4c08aed2011-07-01 19:47:50 +00004475 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004476 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004477
cristy1b784432009-12-19 02:20:40 +00004478 if (status == MagickFalse)
4479 continue;
cristy3ed852e2009-09-05 21:47:34 +00004480 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4481 exception);
cristyacd2ed22011-08-30 01:44:23 +00004482 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004483 {
4484 status=MagickFalse;
4485 continue;
4486 }
cristybb503372010-05-27 20:51:26 +00004487 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004488 {
cristy76f512e2011-09-12 01:26:56 +00004489 MagickRealType
4490 value;
4491
4492 register ssize_t
4493 i;
4494
cristy10a6c612012-01-29 21:41:05 +00004495 if (GetPixelMask(random_image,q) != 0)
4496 {
4497 q+=GetPixelChannels(random_image);
4498 continue;
4499 }
cristy76f512e2011-09-12 01:26:56 +00004500 value=GetPseudoRandomValue(random_info[id]);
4501 for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4502 {
cristyabace412011-12-11 15:56:53 +00004503 PixelChannel
4504 channel;
4505
cristy76f512e2011-09-12 01:26:56 +00004506 PixelTrait
4507 traits;
4508
cristyabace412011-12-11 15:56:53 +00004509 channel=GetPixelChannelMapChannel(image,i);
4510 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004511 if (traits == UndefinedPixelTrait)
4512 continue;
4513 q[i]=ClampToQuantum(QuantumRange*value);
4514 }
cristyed231572011-07-14 02:18:59 +00004515 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004516 }
4517 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4518 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004519 }
4520 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004521 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004522 if (status == MagickFalse)
4523 {
4524 random_image=DestroyImage(random_image);
4525 return(random_image);
4526 }
cristyf7ef0252011-09-09 14:50:06 +00004527 blur_image=MotionBlurImage(random_image,radius,sigma,angle,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00004528 random_image=DestroyImage(random_image);
4529 if (blur_image == (Image *) NULL)
4530 return((Image *) NULL);
cristy6bfd6902011-12-09 01:33:45 +00004531 dodge_image=EdgeImage(blur_image,radius,1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004532 blur_image=DestroyImage(blur_image);
4533 if (dodge_image == (Image *) NULL)
4534 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +00004535 (void) NormalizeImage(dodge_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00004536 (void) NegateImage(dodge_image,MagickFalse,exception);
cristye941a752011-10-15 01:52:48 +00004537 (void) TransformImage(&dodge_image,(char *) NULL,"50%",exception);
cristy3ed852e2009-09-05 21:47:34 +00004538 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4539 if (sketch_image == (Image *) NULL)
4540 {
4541 dodge_image=DestroyImage(dodge_image);
4542 return((Image *) NULL);
4543 }
cristye941a752011-10-15 01:52:48 +00004544 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0,
4545 exception);
cristy3ed852e2009-09-05 21:47:34 +00004546 dodge_image=DestroyImage(dodge_image);
4547 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4548 if (blend_image == (Image *) NULL)
4549 {
4550 sketch_image=DestroyImage(sketch_image);
4551 return((Image *) NULL);
4552 }
4553 (void) SetImageArtifact(blend_image,"compose:args","20x80");
cristye941a752011-10-15 01:52:48 +00004554 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0,
4555 exception);
cristy3ed852e2009-09-05 21:47:34 +00004556 blend_image=DestroyImage(blend_image);
4557 return(sketch_image);
4558}
4559
4560/*
4561%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4562% %
4563% %
4564% %
4565% S o l a r i z e I m a g e %
4566% %
4567% %
4568% %
4569%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4570%
4571% SolarizeImage() applies a special effect to the image, similar to the effect
4572% achieved in a photo darkroom by selectively exposing areas of photo
4573% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4574% measure of the extent of the solarization.
4575%
4576% The format of the SolarizeImage method is:
4577%
cristy5cbc0162011-08-29 00:36:28 +00004578% MagickBooleanType SolarizeImage(Image *image,const double threshold,
4579% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004580%
4581% A description of each parameter follows:
4582%
4583% o image: the image.
4584%
4585% o threshold: Define the extent of the solarization.
4586%
cristy5cbc0162011-08-29 00:36:28 +00004587% o exception: return any errors or warnings in this structure.
4588%
cristy3ed852e2009-09-05 21:47:34 +00004589*/
4590MagickExport MagickBooleanType SolarizeImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00004591 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004592{
4593#define SolarizeImageTag "Solarize/Image"
4594
cristyc4c8d132010-01-07 01:58:38 +00004595 CacheView
4596 *image_view;
4597
cristy3ed852e2009-09-05 21:47:34 +00004598 MagickBooleanType
4599 status;
4600
cristybb503372010-05-27 20:51:26 +00004601 MagickOffsetType
4602 progress;
4603
4604 ssize_t
4605 y;
4606
cristy3ed852e2009-09-05 21:47:34 +00004607 assert(image != (Image *) NULL);
4608 assert(image->signature == MagickSignature);
4609 if (image->debug != MagickFalse)
4610 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4611 if (image->storage_class == PseudoClass)
4612 {
cristybb503372010-05-27 20:51:26 +00004613 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004614 i;
4615
4616 /*
4617 Solarize colormap.
4618 */
cristybb503372010-05-27 20:51:26 +00004619 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004620 {
4621 if ((MagickRealType) image->colormap[i].red > threshold)
4622 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4623 if ((MagickRealType) image->colormap[i].green > threshold)
4624 image->colormap[i].green=(Quantum) QuantumRange-
4625 image->colormap[i].green;
4626 if ((MagickRealType) image->colormap[i].blue > threshold)
4627 image->colormap[i].blue=(Quantum) QuantumRange-
4628 image->colormap[i].blue;
4629 }
4630 }
4631 /*
4632 Solarize image.
4633 */
4634 status=MagickTrue;
4635 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00004636 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004637#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004638 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004639#endif
cristybb503372010-05-27 20:51:26 +00004640 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004641 {
cristybb503372010-05-27 20:51:26 +00004642 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004643 x;
4644
cristy4c08aed2011-07-01 19:47:50 +00004645 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004646 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004647
4648 if (status == MagickFalse)
4649 continue;
cristy5cbc0162011-08-29 00:36:28 +00004650 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00004651 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004652 {
4653 status=MagickFalse;
4654 continue;
4655 }
cristybb503372010-05-27 20:51:26 +00004656 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004657 {
cristy76f512e2011-09-12 01:26:56 +00004658 register ssize_t
4659 i;
4660
cristy10a6c612012-01-29 21:41:05 +00004661 if (GetPixelMask(image,q) != 0)
4662 {
4663 q+=GetPixelChannels(image);
4664 continue;
4665 }
cristy76f512e2011-09-12 01:26:56 +00004666 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4667 {
cristyabace412011-12-11 15:56:53 +00004668 PixelChannel
4669 channel;
4670
cristy76f512e2011-09-12 01:26:56 +00004671 PixelTrait
4672 traits;
4673
cristyabace412011-12-11 15:56:53 +00004674 channel=GetPixelChannelMapChannel(image,i);
4675 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004676 if ((traits == UndefinedPixelTrait) ||
4677 ((traits & CopyPixelTrait) != 0))
4678 continue;
4679 if ((MagickRealType) q[i] > threshold)
4680 q[i]=QuantumRange-q[i];
4681 }
cristyed231572011-07-14 02:18:59 +00004682 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004683 }
4684 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4685 status=MagickFalse;
4686 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4687 {
4688 MagickBooleanType
4689 proceed;
4690
cristyb5d5f722009-11-04 03:03:49 +00004691#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004692 #pragma omp critical (MagickCore_SolarizeImage)
cristy3ed852e2009-09-05 21:47:34 +00004693#endif
4694 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4695 if (proceed == MagickFalse)
4696 status=MagickFalse;
4697 }
4698 }
4699 image_view=DestroyCacheView(image_view);
4700 return(status);
4701}
4702
4703/*
4704%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4705% %
4706% %
4707% %
4708% S t e g a n o I m a g e %
4709% %
4710% %
4711% %
4712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4713%
4714% SteganoImage() hides a digital watermark within the image. Recover
4715% the hidden watermark later to prove that the authenticity of an image.
4716% Offset defines the start position within the image to hide the watermark.
4717%
4718% The format of the SteganoImage method is:
4719%
4720% Image *SteganoImage(const Image *image,Image *watermark,
4721% ExceptionInfo *exception)
4722%
4723% A description of each parameter follows:
4724%
4725% o image: the image.
4726%
4727% o watermark: the watermark image.
4728%
4729% o exception: return any errors or warnings in this structure.
4730%
4731*/
4732MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4733 ExceptionInfo *exception)
4734{
cristye1bf8ad2010-09-19 17:07:03 +00004735#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004736#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004737 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004738#define SteganoImageTag "Stegano/Image"
4739
cristyb0d3bb92010-09-22 14:37:58 +00004740 CacheView
4741 *stegano_view,
4742 *watermark_view;
4743
cristy3ed852e2009-09-05 21:47:34 +00004744 Image
4745 *stegano_image;
4746
4747 int
4748 c;
4749
cristy3ed852e2009-09-05 21:47:34 +00004750 MagickBooleanType
4751 status;
4752
cristy101ab702011-10-13 13:06:32 +00004753 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004754 pixel;
4755
cristy4c08aed2011-07-01 19:47:50 +00004756 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004757 *q;
4758
cristye1bf8ad2010-09-19 17:07:03 +00004759 register ssize_t
4760 x;
4761
cristybb503372010-05-27 20:51:26 +00004762 size_t
cristyeaedf062010-05-29 22:36:02 +00004763 depth,
4764 one;
cristy3ed852e2009-09-05 21:47:34 +00004765
cristye1bf8ad2010-09-19 17:07:03 +00004766 ssize_t
4767 i,
4768 j,
4769 k,
4770 y;
4771
cristy3ed852e2009-09-05 21:47:34 +00004772 /*
4773 Initialize steganographic image attributes.
4774 */
4775 assert(image != (const Image *) NULL);
4776 assert(image->signature == MagickSignature);
4777 if (image->debug != MagickFalse)
4778 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4779 assert(watermark != (const Image *) NULL);
4780 assert(watermark->signature == MagickSignature);
4781 assert(exception != (ExceptionInfo *) NULL);
4782 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004783 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004784 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4785 if (stegano_image == (Image *) NULL)
4786 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004787 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004788 {
cristy3ed852e2009-09-05 21:47:34 +00004789 stegano_image=DestroyImage(stegano_image);
4790 return((Image *) NULL);
4791 }
4792 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4793 /*
4794 Hide watermark in low-order bits of image.
4795 */
4796 c=0;
4797 i=0;
4798 j=0;
4799 depth=stegano_image->depth;
4800 k=image->offset;
cristyda16f162011-02-19 23:52:17 +00004801 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004802 watermark_view=AcquireCacheView(watermark);
4803 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004804 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004805 {
cristybb503372010-05-27 20:51:26 +00004806 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004807 {
cristybb503372010-05-27 20:51:26 +00004808 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004809 {
cristyda1f9c12011-10-02 21:39:49 +00004810 Quantum
cristy5f95f4f2011-10-23 01:01:01 +00004811 virtual_pixel[CompositePixelChannel];
cristyda1f9c12011-10-02 21:39:49 +00004812
cristy1707c6c2012-01-18 23:30:54 +00004813 ssize_t
4814 offset;
4815
cristyda1f9c12011-10-02 21:39:49 +00004816 (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,virtual_pixel,
4817 exception);
4818 pixel.red=(double) virtual_pixel[RedPixelChannel];
4819 pixel.green=(double) virtual_pixel[GreenPixelChannel];
4820 pixel.blue=(double) virtual_pixel[BluePixelChannel];
4821 pixel.alpha=(double) virtual_pixel[AlphaPixelChannel];
cristy1707c6c2012-01-18 23:30:54 +00004822 offset=k/(ssize_t) stegano_image->columns;
4823 if (offset >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004824 break;
cristyb0d3bb92010-09-22 14:37:58 +00004825 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4826 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4827 exception);
cristyacd2ed22011-08-30 01:44:23 +00004828 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004829 break;
4830 switch (c)
4831 {
4832 case 0:
4833 {
cristy4c08aed2011-07-01 19:47:50 +00004834 SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004835 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004836 break;
4837 }
4838 case 1:
4839 {
cristy4c08aed2011-07-01 19:47:50 +00004840 SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004841 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004842 break;
4843 }
4844 case 2:
4845 {
cristy4c08aed2011-07-01 19:47:50 +00004846 SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004847 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004848 break;
4849 }
4850 }
cristyb0d3bb92010-09-22 14:37:58 +00004851 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004852 break;
4853 c++;
4854 if (c == 3)
4855 c=0;
4856 k++;
cristybb503372010-05-27 20:51:26 +00004857 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004858 k=0;
4859 if (k == image->offset)
4860 j++;
4861 }
4862 }
cristy8b27a6d2010-02-14 03:31:15 +00004863 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004864 {
cristy8b27a6d2010-02-14 03:31:15 +00004865 MagickBooleanType
4866 proceed;
4867
4868 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4869 (depth-i),depth);
4870 if (proceed == MagickFalse)
4871 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004872 }
4873 }
cristyb0d3bb92010-09-22 14:37:58 +00004874 stegano_view=DestroyCacheView(stegano_view);
4875 watermark_view=DestroyCacheView(watermark_view);
cristy3ed852e2009-09-05 21:47:34 +00004876 if (stegano_image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00004877 (void) SyncImage(stegano_image,exception);
cristyda16f162011-02-19 23:52:17 +00004878 if (status == MagickFalse)
4879 {
4880 stegano_image=DestroyImage(stegano_image);
4881 return((Image *) NULL);
4882 }
cristy3ed852e2009-09-05 21:47:34 +00004883 return(stegano_image);
4884}
4885
4886/*
4887%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4888% %
4889% %
4890% %
4891% S t e r e o A n a g l y p h I m a g e %
4892% %
4893% %
4894% %
4895%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4896%
4897% StereoAnaglyphImage() combines two images and produces a single image that
4898% is the composite of a left and right image of a stereo pair. Special
4899% red-green stereo glasses are required to view this effect.
4900%
4901% The format of the StereoAnaglyphImage method is:
4902%
4903% Image *StereoImage(const Image *left_image,const Image *right_image,
4904% ExceptionInfo *exception)
4905% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004906% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004907% ExceptionInfo *exception)
4908%
4909% A description of each parameter follows:
4910%
4911% o left_image: the left image.
4912%
4913% o right_image: the right image.
4914%
4915% o exception: return any errors or warnings in this structure.
4916%
4917% o x_offset: amount, in pixels, by which the left image is offset to the
4918% right of the right image.
4919%
4920% o y_offset: amount, in pixels, by which the left image is offset to the
4921% bottom of the right image.
4922%
4923%
4924*/
4925MagickExport Image *StereoImage(const Image *left_image,
4926 const Image *right_image,ExceptionInfo *exception)
4927{
4928 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4929}
4930
4931MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004932 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004933 ExceptionInfo *exception)
4934{
4935#define StereoImageTag "Stereo/Image"
4936
4937 const Image
4938 *image;
4939
4940 Image
4941 *stereo_image;
4942
cristy3ed852e2009-09-05 21:47:34 +00004943 MagickBooleanType
4944 status;
4945
cristy9d314ff2011-03-09 01:30:28 +00004946 ssize_t
4947 y;
4948
cristy3ed852e2009-09-05 21:47:34 +00004949 assert(left_image != (const Image *) NULL);
4950 assert(left_image->signature == MagickSignature);
4951 if (left_image->debug != MagickFalse)
4952 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4953 left_image->filename);
4954 assert(right_image != (const Image *) NULL);
4955 assert(right_image->signature == MagickSignature);
4956 assert(exception != (ExceptionInfo *) NULL);
4957 assert(exception->signature == MagickSignature);
4958 assert(right_image != (const Image *) NULL);
4959 image=left_image;
4960 if ((left_image->columns != right_image->columns) ||
4961 (left_image->rows != right_image->rows))
4962 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4963 /*
4964 Initialize stereo image attributes.
4965 */
4966 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4967 MagickTrue,exception);
4968 if (stereo_image == (Image *) NULL)
4969 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004970 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004971 {
cristy3ed852e2009-09-05 21:47:34 +00004972 stereo_image=DestroyImage(stereo_image);
4973 return((Image *) NULL);
4974 }
4975 /*
4976 Copy left image to red channel and right image to blue channel.
4977 */
cristyda16f162011-02-19 23:52:17 +00004978 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004979 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004980 {
cristy4c08aed2011-07-01 19:47:50 +00004981 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004982 *restrict p,
4983 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004984
cristybb503372010-05-27 20:51:26 +00004985 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004986 x;
4987
cristy4c08aed2011-07-01 19:47:50 +00004988 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004989 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004990
4991 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4992 exception);
4993 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4994 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy76f512e2011-09-12 01:26:56 +00004995 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
4996 (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004997 break;
cristybb503372010-05-27 20:51:26 +00004998 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004999 {
cristy4c08aed2011-07-01 19:47:50 +00005000 SetPixelRed(image,GetPixelRed(left_image,p),r);
cristy76f512e2011-09-12 01:26:56 +00005001 SetPixelGreen(image,GetPixelGreen(right_image,q),r);
5002 SetPixelBlue(image,GetPixelBlue(right_image,q),r);
5003 if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
5004 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
5005 GetPixelAlpha(right_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00005006 p+=GetPixelChannels(left_image);
cristy76f512e2011-09-12 01:26:56 +00005007 q+=GetPixelChannels(right_image);
5008 r+=GetPixelChannels(stereo_image);
cristy3ed852e2009-09-05 21:47:34 +00005009 }
5010 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
5011 break;
cristy8b27a6d2010-02-14 03:31:15 +00005012 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005013 {
cristy8b27a6d2010-02-14 03:31:15 +00005014 MagickBooleanType
5015 proceed;
5016
cristybb503372010-05-27 20:51:26 +00005017 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
5018 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00005019 if (proceed == MagickFalse)
5020 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005021 }
5022 }
cristyda16f162011-02-19 23:52:17 +00005023 if (status == MagickFalse)
5024 {
5025 stereo_image=DestroyImage(stereo_image);
5026 return((Image *) NULL);
5027 }
cristy3ed852e2009-09-05 21:47:34 +00005028 return(stereo_image);
5029}
5030
5031/*
5032%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5033% %
5034% %
5035% %
5036% S w i r l I m a g e %
5037% %
5038% %
5039% %
5040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5041%
5042% SwirlImage() swirls the pixels about the center of the image, where
5043% degrees indicates the sweep of the arc through which each pixel is moved.
5044% You get a more dramatic effect as the degrees move from 1 to 360.
5045%
5046% The format of the SwirlImage method is:
5047%
5048% Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005049% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005050%
5051% A description of each parameter follows:
5052%
5053% o image: the image.
5054%
5055% o degrees: Define the tightness of the swirling effect.
5056%
cristy76f512e2011-09-12 01:26:56 +00005057% o method: the pixel interpolation method.
5058%
cristy3ed852e2009-09-05 21:47:34 +00005059% o exception: return any errors or warnings in this structure.
5060%
5061*/
5062MagickExport Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005063 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005064{
5065#define SwirlImageTag "Swirl/Image"
5066
cristyfa112112010-01-04 17:48:07 +00005067 CacheView
5068 *image_view,
5069 *swirl_view;
5070
cristy3ed852e2009-09-05 21:47:34 +00005071 Image
5072 *swirl_image;
5073
cristy3ed852e2009-09-05 21:47:34 +00005074 MagickBooleanType
5075 status;
5076
cristybb503372010-05-27 20:51:26 +00005077 MagickOffsetType
5078 progress;
5079
cristy3ed852e2009-09-05 21:47:34 +00005080 MagickRealType
5081 radius;
5082
5083 PointInfo
5084 center,
5085 scale;
5086
cristybb503372010-05-27 20:51:26 +00005087 ssize_t
5088 y;
5089
cristy3ed852e2009-09-05 21:47:34 +00005090 /*
5091 Initialize swirl image attributes.
5092 */
5093 assert(image != (const Image *) NULL);
5094 assert(image->signature == MagickSignature);
5095 if (image->debug != MagickFalse)
5096 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5097 assert(exception != (ExceptionInfo *) NULL);
5098 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00005099 swirl_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005100 if (swirl_image == (Image *) NULL)
5101 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005102 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005103 {
cristy3ed852e2009-09-05 21:47:34 +00005104 swirl_image=DestroyImage(swirl_image);
5105 return((Image *) NULL);
5106 }
cristy4c08aed2011-07-01 19:47:50 +00005107 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005108 swirl_image->matte=MagickTrue;
5109 /*
5110 Compute scaling factor.
5111 */
5112 center.x=(double) image->columns/2.0;
5113 center.y=(double) image->rows/2.0;
5114 radius=MagickMax(center.x,center.y);
5115 scale.x=1.0;
5116 scale.y=1.0;
5117 if (image->columns > image->rows)
5118 scale.y=(double) image->columns/(double) image->rows;
5119 else
5120 if (image->columns < image->rows)
5121 scale.x=(double) image->rows/(double) image->columns;
5122 degrees=(double) DegreesToRadians(degrees);
5123 /*
5124 Swirl image.
5125 */
5126 status=MagickTrue;
5127 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00005128 image_view=AcquireCacheView(image);
5129 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005130#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005131 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005132#endif
cristybb503372010-05-27 20:51:26 +00005133 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005134 {
cristy3ed852e2009-09-05 21:47:34 +00005135 MagickRealType
5136 distance;
5137
5138 PointInfo
5139 delta;
5140
cristy6d188022011-09-12 13:23:33 +00005141 register const Quantum
5142 *restrict p;
5143
cristybb503372010-05-27 20:51:26 +00005144 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005145 x;
5146
cristy4c08aed2011-07-01 19:47:50 +00005147 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005148 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005149
5150 if (status == MagickFalse)
5151 continue;
cristy6d188022011-09-12 13:23:33 +00005152 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00005153 q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00005154 exception);
cristy6d188022011-09-12 13:23:33 +00005155 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005156 {
5157 status=MagickFalse;
5158 continue;
5159 }
cristy3ed852e2009-09-05 21:47:34 +00005160 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00005161 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005162 {
5163 /*
5164 Determine if the pixel is within an ellipse.
5165 */
cristy10a6c612012-01-29 21:41:05 +00005166 if (GetPixelMask(image,p) != 0)
5167 {
5168 p+=GetPixelChannels(image);
5169 q+=GetPixelChannels(swirl_image);
5170 continue;
5171 }
cristy3ed852e2009-09-05 21:47:34 +00005172 delta.x=scale.x*(double) (x-center.x);
5173 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00005174 if (distance >= (radius*radius))
5175 {
cristy1707c6c2012-01-18 23:30:54 +00005176 register ssize_t
5177 i;
5178
cristy6d188022011-09-12 13:23:33 +00005179 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy1707c6c2012-01-18 23:30:54 +00005180 {
5181 PixelChannel
5182 channel;
5183
5184 PixelTrait
5185 swirl_traits,
5186 traits;
5187
5188 channel=GetPixelChannelMapChannel(image,i);
5189 traits=GetPixelChannelMapTraits(image,channel);
5190 swirl_traits=GetPixelChannelMapTraits(swirl_image,channel);
5191 if ((traits == UndefinedPixelTrait) ||
5192 (swirl_traits == UndefinedPixelTrait))
5193 continue;
5194 SetPixelChannel(swirl_image,channel,p[i],q);
5195 }
cristy6d188022011-09-12 13:23:33 +00005196 }
5197 else
cristy3ed852e2009-09-05 21:47:34 +00005198 {
5199 MagickRealType
5200 cosine,
5201 factor,
5202 sine;
5203
5204 /*
5205 Swirl the pixel.
5206 */
5207 factor=1.0-sqrt((double) distance)/radius;
5208 sine=sin((double) (degrees*factor*factor));
5209 cosine=cos((double) (degrees*factor*factor));
cristy76f512e2011-09-12 01:26:56 +00005210 status=InterpolatePixelChannels(image,image_view,swirl_image,method,
5211 ((cosine*delta.x-sine*delta.y)/scale.x+center.x),(double)
5212 ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00005213 }
cristy6d188022011-09-12 13:23:33 +00005214 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00005215 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00005216 }
5217 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5218 status=MagickFalse;
5219 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5220 {
5221 MagickBooleanType
5222 proceed;
5223
cristyb5d5f722009-11-04 03:03:49 +00005224#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005225 #pragma omp critical (MagickCore_SwirlImage)
cristy3ed852e2009-09-05 21:47:34 +00005226#endif
5227 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5228 if (proceed == MagickFalse)
5229 status=MagickFalse;
5230 }
5231 }
5232 swirl_view=DestroyCacheView(swirl_view);
5233 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005234 if (status == MagickFalse)
5235 swirl_image=DestroyImage(swirl_image);
5236 return(swirl_image);
5237}
5238
5239/*
5240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5241% %
5242% %
5243% %
5244% T i n t I m a g e %
5245% %
5246% %
5247% %
5248%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5249%
5250% TintImage() applies a color vector to each pixel in the image. The length
5251% of the vector is 0 for black and white and at its maximum for the midtones.
5252% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5253%
5254% The format of the TintImage method is:
5255%
cristyb817c3f2011-10-03 14:00:35 +00005256% Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005257% const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005258%
5259% A description of each parameter follows:
5260%
5261% o image: the image.
5262%
cristyb817c3f2011-10-03 14:00:35 +00005263% o blend: A color value used for tinting.
cristy3ed852e2009-09-05 21:47:34 +00005264%
5265% o tint: A color value used for tinting.
5266%
5267% o exception: return any errors or warnings in this structure.
5268%
5269*/
cristyb817c3f2011-10-03 14:00:35 +00005270MagickExport Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005271 const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005272{
5273#define TintImageTag "Tint/Image"
5274
cristyc4c8d132010-01-07 01:58:38 +00005275 CacheView
5276 *image_view,
5277 *tint_view;
5278
cristy3ed852e2009-09-05 21:47:34 +00005279 GeometryInfo
5280 geometry_info;
5281
5282 Image
5283 *tint_image;
5284
cristy3ed852e2009-09-05 21:47:34 +00005285 MagickBooleanType
5286 status;
5287
cristybb503372010-05-27 20:51:26 +00005288 MagickOffsetType
5289 progress;
cristy3ed852e2009-09-05 21:47:34 +00005290
cristy28474bf2011-09-11 23:32:52 +00005291 MagickRealType
5292 intensity;
5293
cristy4c08aed2011-07-01 19:47:50 +00005294 PixelInfo
cristy1707c6c2012-01-18 23:30:54 +00005295 color_vector;
cristy3ed852e2009-09-05 21:47:34 +00005296
cristybb503372010-05-27 20:51:26 +00005297 MagickStatusType
5298 flags;
5299
5300 ssize_t
5301 y;
5302
cristy3ed852e2009-09-05 21:47:34 +00005303 /*
5304 Allocate tint image.
5305 */
5306 assert(image != (const Image *) NULL);
5307 assert(image->signature == MagickSignature);
5308 if (image->debug != MagickFalse)
5309 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5310 assert(exception != (ExceptionInfo *) NULL);
5311 assert(exception->signature == MagickSignature);
5312 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5313 if (tint_image == (Image *) NULL)
5314 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005315 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005316 {
cristy3ed852e2009-09-05 21:47:34 +00005317 tint_image=DestroyImage(tint_image);
5318 return((Image *) NULL);
5319 }
cristyaed9c382011-10-03 17:54:21 +00005320 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005321 return(tint_image);
5322 /*
5323 Determine RGB values of the color.
5324 */
cristy1707c6c2012-01-18 23:30:54 +00005325 GetPixelInfo(image,&color_vector);
cristyb817c3f2011-10-03 14:00:35 +00005326 flags=ParseGeometry(blend,&geometry_info);
cristy1707c6c2012-01-18 23:30:54 +00005327 color_vector.red=geometry_info.rho;
5328 color_vector.green=geometry_info.rho;
5329 color_vector.blue=geometry_info.rho;
5330 color_vector.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005331 if ((flags & SigmaValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005332 color_vector.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00005333 if ((flags & XiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005334 color_vector.blue=geometry_info.xi;
cristyb817c3f2011-10-03 14:00:35 +00005335 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005336 color_vector.alpha=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005337 if (image->colorspace == CMYKColorspace)
5338 {
cristy1707c6c2012-01-18 23:30:54 +00005339 color_vector.black=geometry_info.rho;
cristy76f512e2011-09-12 01:26:56 +00005340 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005341 color_vector.black=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005342 if ((flags & ChiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005343 color_vector.alpha=geometry_info.chi;
cristy76f512e2011-09-12 01:26:56 +00005344 }
cristy28474bf2011-09-11 23:32:52 +00005345 intensity=(MagickRealType) GetPixelInfoIntensity(tint);
cristy1707c6c2012-01-18 23:30:54 +00005346 color_vector.red=(MagickRealType) (color_vector.red*tint->red/100.0-
5347 intensity);
5348 color_vector.green=(MagickRealType) (color_vector.green*tint->green/100.0-
5349 intensity);
5350 color_vector.blue=(MagickRealType) (color_vector.blue*tint->blue/100.0-
5351 intensity);
5352 color_vector.black=(MagickRealType) (color_vector.black*tint->black/100.0-
5353 intensity);
5354 color_vector.alpha=(MagickRealType) (color_vector.alpha*tint->alpha/100.0-
5355 intensity);
cristy3ed852e2009-09-05 21:47:34 +00005356 /*
5357 Tint image.
5358 */
5359 status=MagickTrue;
5360 progress=0;
5361 image_view=AcquireCacheView(image);
5362 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005363#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005364 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005365#endif
cristybb503372010-05-27 20:51:26 +00005366 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005367 {
cristy4c08aed2011-07-01 19:47:50 +00005368 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005369 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005370
cristy4c08aed2011-07-01 19:47:50 +00005371 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005372 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005373
cristy6b91acb2011-04-19 12:23:54 +00005374 register ssize_t
5375 x;
5376
cristy3ed852e2009-09-05 21:47:34 +00005377 if (status == MagickFalse)
5378 continue;
5379 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5380 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5381 exception);
cristy4c08aed2011-07-01 19:47:50 +00005382 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005383 {
5384 status=MagickFalse;
5385 continue;
5386 }
cristybb503372010-05-27 20:51:26 +00005387 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005388 {
cristy4c08aed2011-07-01 19:47:50 +00005389 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005390 pixel;
5391
5392 MagickRealType
5393 weight;
5394
cristy1707c6c2012-01-18 23:30:54 +00005395 register ssize_t
5396 i;
5397
cristy10a6c612012-01-29 21:41:05 +00005398 if (GetPixelMask(image,p) != 0)
5399 {
5400 p+=GetPixelChannels(image);
5401 q+=GetPixelChannels(tint_image);
5402 continue;
5403 }
cristy1707c6c2012-01-18 23:30:54 +00005404 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
5405 {
5406 PixelChannel
5407 channel;
5408
5409 PixelTrait
5410 tint_traits,
5411 traits;
5412
5413 channel=GetPixelChannelMapChannel(image,i);
5414 traits=GetPixelChannelMapTraits(image,channel);
5415 tint_traits=GetPixelChannelMapTraits(tint_image,channel);
5416 if ((traits == UndefinedPixelTrait) ||
5417 (tint_traits == UndefinedPixelTrait))
5418 continue;
5419 if ((tint_traits & CopyPixelTrait) != 0)
5420 {
5421 SetPixelChannel(tint_image,channel,p[i],q);
5422 continue;
5423 }
5424 }
5425 GetPixelInfo(image,&pixel);
5426 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5427 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5428 (1.0-(4.0*(weight*weight)));
5429 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5430 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5431 (1.0-(4.0*(weight*weight)));
5432 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5433 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5434 (1.0-(4.0*(weight*weight)));
5435 weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5436 pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5437 (1.0-(4.0*(weight*weight)));
5438 SetPixelInfoPixel(tint_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005439 p+=GetPixelChannels(image);
5440 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005441 }
5442 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5443 status=MagickFalse;
5444 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5445 {
5446 MagickBooleanType
5447 proceed;
5448
cristyb5d5f722009-11-04 03:03:49 +00005449#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005450 #pragma omp critical (MagickCore_TintImage)
cristy3ed852e2009-09-05 21:47:34 +00005451#endif
5452 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5453 if (proceed == MagickFalse)
5454 status=MagickFalse;
5455 }
5456 }
5457 tint_view=DestroyCacheView(tint_view);
5458 image_view=DestroyCacheView(image_view);
5459 if (status == MagickFalse)
5460 tint_image=DestroyImage(tint_image);
5461 return(tint_image);
5462}
5463
5464/*
5465%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5466% %
5467% %
5468% %
5469% V i g n e t t e I m a g e %
5470% %
5471% %
5472% %
5473%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5474%
5475% VignetteImage() softens the edges of the image in vignette style.
5476%
5477% The format of the VignetteImage method is:
5478%
5479% Image *VignetteImage(const Image *image,const double radius,
cristyeb6e6582011-12-09 09:14:23 +00005480% const double sigma,const double bias,const ssize_t x,const ssize_t y,
cristy05c0c9a2011-09-05 23:16:13 +00005481% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005482%
5483% A description of each parameter follows:
5484%
5485% o image: the image.
5486%
5487% o radius: the radius of the pixel neighborhood.
5488%
5489% o sigma: the standard deviation of the Gaussian, in pixels.
5490%
cristyeb6e6582011-12-09 09:14:23 +00005491% o bias: the bias.
5492%
cristy3ed852e2009-09-05 21:47:34 +00005493% o x, y: Define the x and y ellipse offset.
5494%
5495% o exception: return any errors or warnings in this structure.
5496%
5497*/
5498MagickExport Image *VignetteImage(const Image *image,const double radius,
cristyeb6e6582011-12-09 09:14:23 +00005499 const double sigma,const double bias,const ssize_t x,const ssize_t y,
5500 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005501{
5502 char
5503 ellipse[MaxTextExtent];
5504
5505 DrawInfo
5506 *draw_info;
5507
5508 Image
5509 *canvas_image,
5510 *blur_image,
5511 *oval_image,
5512 *vignette_image;
5513
5514 assert(image != (Image *) NULL);
5515 assert(image->signature == MagickSignature);
5516 if (image->debug != MagickFalse)
5517 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5518 assert(exception != (ExceptionInfo *) NULL);
5519 assert(exception->signature == MagickSignature);
5520 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5521 if (canvas_image == (Image *) NULL)
5522 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005523 if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005524 {
cristy3ed852e2009-09-05 21:47:34 +00005525 canvas_image=DestroyImage(canvas_image);
5526 return((Image *) NULL);
5527 }
5528 canvas_image->matte=MagickTrue;
cristy98621462011-12-31 22:31:11 +00005529 oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
5530 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005531 if (oval_image == (Image *) NULL)
5532 {
5533 canvas_image=DestroyImage(canvas_image);
5534 return((Image *) NULL);
5535 }
cristy9950d572011-10-01 18:22:35 +00005536 (void) QueryColorCompliance("#000000",AllCompliance,
5537 &oval_image->background_color,exception);
cristyea1a8aa2011-10-20 13:24:06 +00005538 (void) SetImageBackgroundColor(oval_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005539 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00005540 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5541 exception);
5542 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5543 exception);
cristy1707c6c2012-01-18 23:30:54 +00005544 (void) FormatLocaleString(ellipse,MaxTextExtent,"ellipse %g,%g,%g,%g,"
5545 "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5546 image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005547 draw_info->primitive=AcquireString(ellipse);
cristy018f07f2011-09-04 21:15:19 +00005548 (void) DrawImage(oval_image,draw_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005549 draw_info=DestroyDrawInfo(draw_info);
cristyeb6e6582011-12-09 09:14:23 +00005550 blur_image=BlurImage(oval_image,radius,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00005551 oval_image=DestroyImage(oval_image);
5552 if (blur_image == (Image *) NULL)
5553 {
5554 canvas_image=DestroyImage(canvas_image);
5555 return((Image *) NULL);
5556 }
5557 blur_image->matte=MagickFalse;
cristy98621462011-12-31 22:31:11 +00005558 (void) CompositeImage(canvas_image,IntensityCompositeOp,blur_image,0,0,
cristye941a752011-10-15 01:52:48 +00005559 exception);
cristy3ed852e2009-09-05 21:47:34 +00005560 blur_image=DestroyImage(blur_image);
5561 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5562 canvas_image=DestroyImage(canvas_image);
5563 return(vignette_image);
5564}
5565
5566/*
5567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5568% %
5569% %
5570% %
5571% W a v e I m a g e %
5572% %
5573% %
5574% %
5575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5576%
5577% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005578% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005579% by the given parameters.
5580%
5581% The format of the WaveImage method is:
5582%
5583% Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005584% const double wave_length,const PixelInterpolateMethod method,
5585% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005586%
5587% A description of each parameter follows:
5588%
5589% o image: the image.
5590%
5591% o amplitude, wave_length: Define the amplitude and wave length of the
5592% sine wave.
5593%
cristy5c4e2582011-09-11 19:21:03 +00005594% o interpolate: the pixel interpolation method.
5595%
cristy3ed852e2009-09-05 21:47:34 +00005596% o exception: return any errors or warnings in this structure.
5597%
5598*/
5599MagickExport Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005600 const double wave_length,const PixelInterpolateMethod method,
5601 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005602{
5603#define WaveImageTag "Wave/Image"
5604
cristyfa112112010-01-04 17:48:07 +00005605 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005606 *image_view,
cristyfa112112010-01-04 17:48:07 +00005607 *wave_view;
5608
cristy3ed852e2009-09-05 21:47:34 +00005609 Image
5610 *wave_image;
5611
cristy3ed852e2009-09-05 21:47:34 +00005612 MagickBooleanType
5613 status;
5614
cristybb503372010-05-27 20:51:26 +00005615 MagickOffsetType
5616 progress;
5617
cristy3ed852e2009-09-05 21:47:34 +00005618 MagickRealType
5619 *sine_map;
5620
cristybb503372010-05-27 20:51:26 +00005621 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005622 i;
5623
cristybb503372010-05-27 20:51:26 +00005624 ssize_t
5625 y;
5626
cristy3ed852e2009-09-05 21:47:34 +00005627 /*
5628 Initialize wave image attributes.
5629 */
5630 assert(image != (Image *) NULL);
5631 assert(image->signature == MagickSignature);
5632 if (image->debug != MagickFalse)
5633 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5634 assert(exception != (ExceptionInfo *) NULL);
5635 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005636 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005637 fabs(amplitude)),MagickTrue,exception);
5638 if (wave_image == (Image *) NULL)
5639 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005640 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005641 {
cristy3ed852e2009-09-05 21:47:34 +00005642 wave_image=DestroyImage(wave_image);
5643 return((Image *) NULL);
5644 }
cristy4c08aed2011-07-01 19:47:50 +00005645 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005646 wave_image->matte=MagickTrue;
5647 /*
5648 Allocate sine map.
5649 */
5650 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5651 sizeof(*sine_map));
5652 if (sine_map == (MagickRealType *) NULL)
5653 {
5654 wave_image=DestroyImage(wave_image);
5655 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5656 }
cristybb503372010-05-27 20:51:26 +00005657 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005658 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5659 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005660 /*
5661 Wave image.
5662 */
5663 status=MagickTrue;
5664 progress=0;
cristyd76c51e2011-03-26 00:21:26 +00005665 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005666 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005667 (void) SetCacheViewVirtualPixelMethod(image_view,
5668 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005669#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005670 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005671#endif
cristybb503372010-05-27 20:51:26 +00005672 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005673 {
cristy4c08aed2011-07-01 19:47:50 +00005674 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005675 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005676
cristye97bb922011-04-03 01:36:52 +00005677 register ssize_t
5678 x;
5679
cristy3ed852e2009-09-05 21:47:34 +00005680 if (status == MagickFalse)
5681 continue;
5682 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5683 exception);
cristyacd2ed22011-08-30 01:44:23 +00005684 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005685 {
5686 status=MagickFalse;
5687 continue;
5688 }
cristybb503372010-05-27 20:51:26 +00005689 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005690 {
cristy5c4e2582011-09-11 19:21:03 +00005691 status=InterpolatePixelChannels(image,image_view,wave_image,method,
5692 (double) x,(double) (y-sine_map[x]),q,exception);
cristyed231572011-07-14 02:18:59 +00005693 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005694 }
5695 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5696 status=MagickFalse;
5697 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5698 {
5699 MagickBooleanType
5700 proceed;
5701
cristyb5d5f722009-11-04 03:03:49 +00005702#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005703 #pragma omp critical (MagickCore_WaveImage)
cristy3ed852e2009-09-05 21:47:34 +00005704#endif
5705 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5706 if (proceed == MagickFalse)
5707 status=MagickFalse;
5708 }
5709 }
5710 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005711 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005712 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5713 if (status == MagickFalse)
5714 wave_image=DestroyImage(wave_image);
5715 return(wave_image);
5716}