blob: b8746f5f85fefc521b052561c0be13e42d8f5521 [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,
cristyaa2c16c2012-03-25 22:21:35 +0000563% const double sigma,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%
573% o exception: return any errors or warnings in this structure.
574%
575*/
576MagickExport Image *CharcoalImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +0000577 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000578{
579 Image
580 *charcoal_image,
581 *clone_image,
582 *edge_image;
583
584 assert(image != (Image *) NULL);
585 assert(image->signature == MagickSignature);
586 if (image->debug != MagickFalse)
587 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
588 assert(exception != (ExceptionInfo *) NULL);
589 assert(exception->signature == MagickSignature);
590 clone_image=CloneImage(image,0,0,MagickTrue,exception);
591 if (clone_image == (Image *) NULL)
592 return((Image *) NULL);
cristy018f07f2011-09-04 21:15:19 +0000593 (void) SetImageType(clone_image,GrayscaleType,exception);
cristy8ae632d2011-09-05 17:29:53 +0000594 edge_image=EdgeImage(clone_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000595 clone_image=DestroyImage(clone_image);
596 if (edge_image == (Image *) NULL)
597 return((Image *) NULL);
cristyaa2c16c2012-03-25 22:21:35 +0000598 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000599 edge_image=DestroyImage(edge_image);
600 if (charcoal_image == (Image *) NULL)
601 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +0000602 (void) NormalizeImage(charcoal_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +0000603 (void) NegateImage(charcoal_image,MagickFalse,exception);
cristy018f07f2011-09-04 21:15:19 +0000604 (void) SetImageType(charcoal_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000605 return(charcoal_image);
606}
607
608/*
609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
610% %
611% %
612% %
613% C o l o r i z e I m a g e %
614% %
615% %
616% %
617%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
618%
619% ColorizeImage() blends the fill color with each pixel in the image.
620% A percentage blend is specified with opacity. Control the application
621% of different color components by specifying a different percentage for
622% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
623%
624% The format of the ColorizeImage method is:
625%
cristyc7e6ff62011-10-03 13:46:11 +0000626% Image *ColorizeImage(const Image *image,const char *blend,
627% const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000628%
629% A description of each parameter follows:
630%
631% o image: the image.
632%
cristyc7e6ff62011-10-03 13:46:11 +0000633% o blend: A character string indicating the level of blending as a
cristy3ed852e2009-09-05 21:47:34 +0000634% percentage.
635%
636% o colorize: A color value.
637%
638% o exception: return any errors or warnings in this structure.
639%
640*/
cristyc7e6ff62011-10-03 13:46:11 +0000641MagickExport Image *ColorizeImage(const Image *image,const char *blend,
642 const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000643{
644#define ColorizeImageTag "Colorize/Image"
645
cristyc4c8d132010-01-07 01:58:38 +0000646 CacheView
647 *colorize_view,
648 *image_view;
649
cristy3ed852e2009-09-05 21:47:34 +0000650 GeometryInfo
651 geometry_info;
652
653 Image
654 *colorize_image;
655
cristy3ed852e2009-09-05 21:47:34 +0000656 MagickBooleanType
657 status;
658
cristybb503372010-05-27 20:51:26 +0000659 MagickOffsetType
660 progress;
661
cristy3ed852e2009-09-05 21:47:34 +0000662 MagickStatusType
663 flags;
664
cristyc7e6ff62011-10-03 13:46:11 +0000665 PixelInfo
666 pixel;
667
cristybb503372010-05-27 20:51:26 +0000668 ssize_t
669 y;
670
cristy3ed852e2009-09-05 21:47:34 +0000671 /*
672 Allocate colorized image.
673 */
674 assert(image != (const Image *) NULL);
675 assert(image->signature == MagickSignature);
676 if (image->debug != MagickFalse)
677 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
678 assert(exception != (ExceptionInfo *) NULL);
679 assert(exception->signature == MagickSignature);
680 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
681 exception);
682 if (colorize_image == (Image *) NULL)
683 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000684 if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000685 {
cristy3ed852e2009-09-05 21:47:34 +0000686 colorize_image=DestroyImage(colorize_image);
687 return((Image *) NULL);
688 }
cristy5b67d4e2012-02-07 19:43:53 +0000689 if ((colorize->matte != MagickFalse) &&
690 (colorize_image->matte == MagickFalse))
691 (void) SetImageAlpha(colorize_image,OpaqueAlpha,exception);
cristyc7e6ff62011-10-03 13:46:11 +0000692 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000693 return(colorize_image);
694 /*
anthonyfd706f92012-01-19 04:22:02 +0000695 Determine RGB values of the fill color for pixel
cristy3ed852e2009-09-05 21:47:34 +0000696 */
cristyc7e6ff62011-10-03 13:46:11 +0000697 GetPixelInfo(image,&pixel);
cristyb817c3f2011-10-03 14:00:35 +0000698 flags=ParseGeometry(blend,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000699 pixel.red=geometry_info.rho;
700 pixel.green=geometry_info.rho;
701 pixel.blue=geometry_info.rho;
cristye71f2942012-04-04 11:07:05 +0000702 pixel.black=geometry_info.rho;
cristyc7e6ff62011-10-03 13:46:11 +0000703 pixel.alpha=100.0;
cristy3ed852e2009-09-05 21:47:34 +0000704 if ((flags & SigmaValue) != 0)
705 pixel.green=geometry_info.sigma;
706 if ((flags & XiValue) != 0)
707 pixel.blue=geometry_info.xi;
708 if ((flags & PsiValue) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000709 pixel.alpha=geometry_info.psi;
cristyc7e6ff62011-10-03 13:46:11 +0000710 if (pixel.colorspace == CMYKColorspace)
711 {
cristyc7e6ff62011-10-03 13:46:11 +0000712 if ((flags & PsiValue) != 0)
713 pixel.black=geometry_info.psi;
714 if ((flags & ChiValue) != 0)
715 pixel.alpha=geometry_info.chi;
716 }
cristye71f2942012-04-04 11:07:05 +0000717 if ((colorize_image->colorspace == GRAYColorspace) &&
718 (IsPixelInfoGray(&pixel) != MagickFalse))
719 (void) SetImageColorspace(colorize_image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000720 /*
721 Colorize DirectClass image.
722 */
723 status=MagickTrue;
724 progress=0;
725 image_view=AcquireCacheView(image);
726 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000727#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000728 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000729#endif
cristybb503372010-05-27 20:51:26 +0000730 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000731 {
732 MagickBooleanType
733 sync;
734
cristy4c08aed2011-07-01 19:47:50 +0000735 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000736 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000737
cristybb503372010-05-27 20:51:26 +0000738 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000739 x;
740
cristy4c08aed2011-07-01 19:47:50 +0000741 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000742 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000743
744 if (status == MagickFalse)
745 continue;
746 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
747 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
748 exception);
cristy4c08aed2011-07-01 19:47:50 +0000749 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000750 {
751 status=MagickFalse;
752 continue;
753 }
cristybb503372010-05-27 20:51:26 +0000754 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000755 {
cristyc7e6ff62011-10-03 13:46:11 +0000756 register ssize_t
757 i;
758
759 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
760 {
761 PixelChannel
762 channel;
763
764 PixelTrait
765 colorize_traits,
766 traits;
767
cristye2a912b2011-12-05 20:02:07 +0000768 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000769 traits=GetPixelChannelMapTraits(image,channel);
cristyc7e6ff62011-10-03 13:46:11 +0000770 colorize_traits=GetPixelChannelMapTraits(colorize_image,channel);
771 if ((traits == UndefinedPixelTrait) ||
772 (colorize_traits == UndefinedPixelTrait))
773 continue;
cristyd09f8802012-02-04 16:44:10 +0000774 if (((colorize_traits & CopyPixelTrait) != 0) ||
775 (GetPixelMask(image,p) != 0))
cristyc7e6ff62011-10-03 13:46:11 +0000776 {
777 SetPixelChannel(colorize_image,channel,p[i],q);
778 continue;
779 }
780 switch (channel)
781 {
782 case RedPixelChannel:
783 {
784 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
785 (100.0-pixel.red)+colorize->red*pixel.red)/100.0),q);
786 break;
787 }
788 case GreenPixelChannel:
789 {
790 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
791 (100.0-pixel.green)+colorize->green*pixel.green)/100.0),q);
792 break;
793 }
794 case BluePixelChannel:
795 {
796 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
797 (100.0-pixel.blue)+colorize->blue*pixel.blue)/100.0),q);
798 break;
799 }
800 case BlackPixelChannel:
801 {
802 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
803 (100.0-pixel.black)+colorize->black*pixel.black)/100.0),q);
804 break;
805 }
806 case AlphaPixelChannel:
807 {
808 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
809 (100.0-pixel.alpha)+colorize->alpha*pixel.alpha)/100.0),q);
810 break;
811 }
812 default:
813 {
814 SetPixelChannel(colorize_image,channel,p[i],q);
815 break;
816 }
817 }
818 }
cristyed231572011-07-14 02:18:59 +0000819 p+=GetPixelChannels(image);
820 q+=GetPixelChannels(colorize_image);
cristy3ed852e2009-09-05 21:47:34 +0000821 }
822 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
823 if (sync == MagickFalse)
824 status=MagickFalse;
825 if (image->progress_monitor != (MagickProgressMonitor) NULL)
826 {
827 MagickBooleanType
828 proceed;
829
cristy319a1e72010-02-21 15:13:11 +0000830#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000831 #pragma omp critical (MagickCore_ColorizeImage)
cristy3ed852e2009-09-05 21:47:34 +0000832#endif
833 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
834 if (proceed == MagickFalse)
835 status=MagickFalse;
836 }
837 }
838 image_view=DestroyCacheView(image_view);
839 colorize_view=DestroyCacheView(colorize_view);
840 if (status == MagickFalse)
841 colorize_image=DestroyImage(colorize_image);
842 return(colorize_image);
843}
844
845/*
846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847% %
848% %
849% %
cristye6365592010-04-02 17:31:23 +0000850% C o l o r M a t r i x I m a g e %
851% %
852% %
853% %
854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855%
856% ColorMatrixImage() applies color transformation to an image. This method
857% permits saturation changes, hue rotation, luminance to alpha, and various
858% other effects. Although variable-sized transformation matrices can be used,
859% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
860% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
861% except offsets are in column 6 rather than 5 (in support of CMYKA images)
862% and offsets are normalized (divide Flash offset by 255).
863%
864% The format of the ColorMatrixImage method is:
865%
866% Image *ColorMatrixImage(const Image *image,
867% const KernelInfo *color_matrix,ExceptionInfo *exception)
868%
869% A description of each parameter follows:
870%
871% o image: the image.
872%
873% o color_matrix: the color matrix.
874%
875% o exception: return any errors or warnings in this structure.
876%
877*/
anthonyfd706f92012-01-19 04:22:02 +0000878/* FUTURE: modify to make use of a MagickMatrix Mutliply function
879 That should be provided in "matrix.c"
880 (ASIDE: actually distorts should do this too but currently doesn't)
881*/
882
cristye6365592010-04-02 17:31:23 +0000883MagickExport Image *ColorMatrixImage(const Image *image,
884 const KernelInfo *color_matrix,ExceptionInfo *exception)
885{
886#define ColorMatrixImageTag "ColorMatrix/Image"
887
888 CacheView
889 *color_view,
890 *image_view;
891
892 double
893 ColorMatrix[6][6] =
894 {
895 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
896 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
897 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
898 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
899 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
900 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
901 };
902
903 Image
904 *color_image;
905
cristye6365592010-04-02 17:31:23 +0000906 MagickBooleanType
907 status;
908
cristybb503372010-05-27 20:51:26 +0000909 MagickOffsetType
910 progress;
911
912 register ssize_t
cristye6365592010-04-02 17:31:23 +0000913 i;
914
cristybb503372010-05-27 20:51:26 +0000915 ssize_t
916 u,
917 v,
918 y;
919
cristye6365592010-04-02 17:31:23 +0000920 /*
anthonyfd706f92012-01-19 04:22:02 +0000921 Map given color_matrix, into a 6x6 matrix RGBKA and a constant
cristye6365592010-04-02 17:31:23 +0000922 */
923 assert(image != (Image *) NULL);
924 assert(image->signature == MagickSignature);
925 if (image->debug != MagickFalse)
926 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
927 assert(exception != (ExceptionInfo *) NULL);
928 assert(exception->signature == MagickSignature);
929 i=0;
cristybb503372010-05-27 20:51:26 +0000930 for (v=0; v < (ssize_t) color_matrix->height; v++)
931 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000932 {
933 if ((v < 6) && (u < 6))
934 ColorMatrix[v][u]=color_matrix->values[i];
935 i++;
936 }
937 /*
938 Initialize color image.
939 */
cristy12550e62010-06-07 12:46:40 +0000940 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000941 if (color_image == (Image *) NULL)
942 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000943 if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
cristye6365592010-04-02 17:31:23 +0000944 {
cristye6365592010-04-02 17:31:23 +0000945 color_image=DestroyImage(color_image);
946 return((Image *) NULL);
947 }
948 if (image->debug != MagickFalse)
949 {
950 char
951 format[MaxTextExtent],
952 *message;
953
954 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
955 " ColorMatrix image with color matrix:");
956 message=AcquireString("");
957 for (v=0; v < 6; v++)
958 {
959 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000960 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000961 (void) ConcatenateString(&message,format);
962 for (u=0; u < 6; u++)
963 {
cristyb51dff52011-05-19 16:55:47 +0000964 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
cristye6365592010-04-02 17:31:23 +0000965 ColorMatrix[v][u]);
966 (void) ConcatenateString(&message,format);
967 }
968 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
969 }
970 message=DestroyString(message);
971 }
972 /*
anthonyfd706f92012-01-19 04:22:02 +0000973 Apply the ColorMatrix to image.
cristye6365592010-04-02 17:31:23 +0000974 */
975 status=MagickTrue;
976 progress=0;
977 image_view=AcquireCacheView(image);
978 color_view=AcquireCacheView(color_image);
979#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000980 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye6365592010-04-02 17:31:23 +0000981#endif
cristybb503372010-05-27 20:51:26 +0000982 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000983 {
cristyfcc25d92012-02-19 23:06:48 +0000984 PixelInfo
cristye6365592010-04-02 17:31:23 +0000985 pixel;
986
cristy4c08aed2011-07-01 19:47:50 +0000987 register const Quantum
cristye6365592010-04-02 17:31:23 +0000988 *restrict p;
989
cristy4c08aed2011-07-01 19:47:50 +0000990 register Quantum
991 *restrict q;
992
cristybb503372010-05-27 20:51:26 +0000993 register ssize_t
cristye6365592010-04-02 17:31:23 +0000994 x;
995
cristye6365592010-04-02 17:31:23 +0000996 if (status == MagickFalse)
997 continue;
998 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
999 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
1000 exception);
cristy4c08aed2011-07-01 19:47:50 +00001001 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristye6365592010-04-02 17:31:23 +00001002 {
1003 status=MagickFalse;
1004 continue;
1005 }
cristyfcc25d92012-02-19 23:06:48 +00001006 GetPixelInfo(image,&pixel);
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
cristyfcc25d92012-02-19 23:06:48 +00001015 GetPixelInfoPixel(image,p,&pixel);
cristye6365592010-04-02 17:31:23 +00001016 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +00001017 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +00001018 {
cristyfcc25d92012-02-19 23:06:48 +00001019 MagickRealType
1020 sum;
1021
1022 sum=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
cristy4c08aed2011-07-01 19:47:50 +00001023 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +00001024 if (image->colorspace == CMYKColorspace)
cristyfcc25d92012-02-19 23:06:48 +00001025 sum+=ColorMatrix[v][3]*GetPixelBlack(image,p);
cristy4c08aed2011-07-01 19:47:50 +00001026 if (image->matte != MagickFalse)
cristyfcc25d92012-02-19 23:06:48 +00001027 sum+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
1028 sum+=QuantumRange*ColorMatrix[v][5];
cristye6365592010-04-02 17:31:23 +00001029 switch (v)
1030 {
cristyfcc25d92012-02-19 23:06:48 +00001031 case 0: pixel.red=sum; break;
1032 case 1: pixel.green=sum; break;
1033 case 2: pixel.blue=sum; break;
1034 case 3: pixel.black=sum; break;
1035 case 4: pixel.alpha=sum; break;
1036 default: break;
cristye6365592010-04-02 17:31:23 +00001037 }
1038 }
cristyfcc25d92012-02-19 23:06:48 +00001039 SetPixelInfoPixel(color_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001040 p+=GetPixelChannels(image);
1041 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +00001042 }
1043 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1044 status=MagickFalse;
1045 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1046 {
1047 MagickBooleanType
1048 proceed;
1049
1050#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00001051 #pragma omp critical (MagickCore_ColorMatrixImage)
cristye6365592010-04-02 17:31:23 +00001052#endif
1053 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1054 image->rows);
1055 if (proceed == MagickFalse)
1056 status=MagickFalse;
1057 }
1058 }
1059 color_view=DestroyCacheView(color_view);
1060 image_view=DestroyCacheView(image_view);
1061 if (status == MagickFalse)
1062 color_image=DestroyImage(color_image);
1063 return(color_image);
1064}
1065
1066/*
1067%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1068% %
1069% %
1070% %
cristy3ed852e2009-09-05 21:47:34 +00001071+ D e s t r o y F x I n f o %
1072% %
1073% %
1074% %
1075%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1076%
1077% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1078%
1079% The format of the DestroyFxInfo method is:
1080%
1081% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1082%
1083% A description of each parameter follows:
1084%
1085% o fx_info: the fx info.
1086%
1087*/
cristy7832dc22011-09-05 01:21:53 +00001088MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
cristy3ed852e2009-09-05 21:47:34 +00001089{
cristybb503372010-05-27 20:51:26 +00001090 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001091 i;
1092
1093 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1094 fx_info->expression=DestroyString(fx_info->expression);
1095 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1096 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001097 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001098 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1099 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001100 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1101 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1102 return(fx_info);
1103}
1104
1105/*
1106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1107% %
1108% %
1109% %
cristy3ed852e2009-09-05 21:47:34 +00001110+ 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 %
1111% %
1112% %
1113% %
1114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1115%
1116% FxEvaluateChannelExpression() evaluates an expression and returns the
1117% results.
1118%
1119% The format of the FxEvaluateExpression method is:
1120%
1121% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001122% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001123% MagickRealType *alpha,Exceptioninfo *exception)
1124% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1125% MagickRealType *alpha,Exceptioninfo *exception)
1126%
1127% A description of each parameter follows:
1128%
1129% o fx_info: the fx info.
1130%
1131% o channel: the channel.
1132%
1133% o x,y: the pixel position.
1134%
1135% o alpha: the result.
1136%
1137% o exception: return any errors or warnings in this structure.
1138%
1139*/
1140
cristy351842f2010-03-07 15:27:38 +00001141static inline double MagickMax(const double x,const double y)
1142{
1143 if (x > y)
1144 return(x);
1145 return(y);
1146}
1147
1148static inline double MagickMin(const double x,const double y)
1149{
1150 if (x < y)
1151 return(x);
1152 return(y);
1153}
1154
cristy3ed852e2009-09-05 21:47:34 +00001155static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001156 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001157{
1158 char
1159 key[MaxTextExtent],
1160 statistic[MaxTextExtent];
1161
1162 const char
1163 *value;
1164
1165 register const char
1166 *p;
1167
1168 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1169 if (*p == '.')
1170 switch (*++p) /* e.g. depth.r */
1171 {
cristy541ae572011-08-05 19:08:59 +00001172 case 'r': channel=RedPixelChannel; break;
1173 case 'g': channel=GreenPixelChannel; break;
1174 case 'b': channel=BluePixelChannel; break;
1175 case 'c': channel=CyanPixelChannel; break;
1176 case 'm': channel=MagentaPixelChannel; break;
1177 case 'y': channel=YellowPixelChannel; break;
1178 case 'k': channel=BlackPixelChannel; break;
cristy3ed852e2009-09-05 21:47:34 +00001179 default: break;
1180 }
cristyb51dff52011-05-19 16:55:47 +00001181 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001182 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001183 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1184 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001185 return(QuantumScale*StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001186 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1187 if (LocaleNCompare(symbol,"depth",5) == 0)
1188 {
cristybb503372010-05-27 20:51:26 +00001189 size_t
cristy3ed852e2009-09-05 21:47:34 +00001190 depth;
1191
cristyfefab1b2011-07-05 00:33:22 +00001192 depth=GetImageDepth(image,exception);
1193 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001194 }
1195 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1196 {
1197 double
1198 kurtosis,
1199 skewness;
1200
cristyd42d9952011-07-08 14:21:50 +00001201 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001202 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001203 }
1204 if (LocaleNCompare(symbol,"maxima",6) == 0)
1205 {
1206 double
1207 maxima,
1208 minima;
1209
cristyd42d9952011-07-08 14:21:50 +00001210 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001211 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001212 }
1213 if (LocaleNCompare(symbol,"mean",4) == 0)
1214 {
1215 double
1216 mean,
1217 standard_deviation;
1218
cristyd42d9952011-07-08 14:21:50 +00001219 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001220 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001221 }
1222 if (LocaleNCompare(symbol,"minima",6) == 0)
1223 {
1224 double
1225 maxima,
1226 minima;
1227
cristyd42d9952011-07-08 14:21:50 +00001228 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001229 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001230 }
1231 if (LocaleNCompare(symbol,"skewness",8) == 0)
1232 {
1233 double
1234 kurtosis,
1235 skewness;
1236
cristyd42d9952011-07-08 14:21:50 +00001237 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001238 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001239 }
1240 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1241 {
1242 double
1243 mean,
1244 standard_deviation;
1245
cristyd42d9952011-07-08 14:21:50 +00001246 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001247 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001248 standard_deviation);
1249 }
1250 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1251 ConstantString(statistic));
cristydbdd0e32011-11-04 23:29:40 +00001252 return(QuantumScale*StringToDouble(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001253}
1254
1255static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001256 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001257 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001258
cristyb0aad4c2011-11-02 19:30:35 +00001259static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
1260{
1261 if (beta != 0)
1262 return(FxGCD(beta,alpha % beta));
1263 return(alpha);
1264}
1265
cristy3ed852e2009-09-05 21:47:34 +00001266static inline const char *FxSubexpression(const char *expression,
1267 ExceptionInfo *exception)
1268{
1269 const char
1270 *subexpression;
1271
cristybb503372010-05-27 20:51:26 +00001272 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001273 level;
1274
1275 level=0;
1276 subexpression=expression;
1277 while ((*subexpression != '\0') &&
1278 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1279 {
1280 if (strchr("(",(int) *subexpression) != (char *) NULL)
1281 level++;
1282 else
1283 if (strchr(")",(int) *subexpression) != (char *) NULL)
1284 level--;
1285 subexpression++;
1286 }
1287 if (*subexpression == '\0')
1288 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1289 "UnbalancedParenthesis","`%s'",expression);
1290 return(subexpression);
1291}
1292
cristy0568ffc2011-07-25 16:54:14 +00001293static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001294 const ssize_t x,const ssize_t y,const char *expression,
1295 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001296{
1297 char
1298 *q,
1299 subexpression[MaxTextExtent],
1300 symbol[MaxTextExtent];
1301
1302 const char
1303 *p,
1304 *value;
1305
1306 Image
1307 *image;
1308
cristy4c08aed2011-07-01 19:47:50 +00001309 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001310 pixel;
1311
1312 MagickRealType
1313 alpha,
1314 beta;
1315
1316 PointInfo
1317 point;
1318
cristybb503372010-05-27 20:51:26 +00001319 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001320 i;
1321
1322 size_t
cristy1707c6c2012-01-18 23:30:54 +00001323 length,
cristy3ed852e2009-09-05 21:47:34 +00001324 level;
1325
1326 p=expression;
1327 i=GetImageIndexInList(fx_info->images);
1328 level=0;
1329 point.x=(double) x;
1330 point.y=(double) y;
1331 if (isalpha((int) *(p+1)) == 0)
1332 {
1333 if (strchr("suv",(int) *p) != (char *) NULL)
1334 {
1335 switch (*p)
1336 {
1337 case 's':
1338 default:
1339 {
1340 i=GetImageIndexInList(fx_info->images);
1341 break;
1342 }
1343 case 'u': i=0; break;
1344 case 'v': i=1; break;
1345 }
1346 p++;
1347 if (*p == '[')
1348 {
1349 level++;
1350 q=subexpression;
1351 for (p++; *p != '\0'; )
1352 {
1353 if (*p == '[')
1354 level++;
1355 else
1356 if (*p == ']')
1357 {
1358 level--;
1359 if (level == 0)
1360 break;
1361 }
1362 *q++=(*p++);
1363 }
1364 *q='\0';
1365 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1366 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001367 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001368 p++;
1369 }
1370 if (*p == '.')
1371 p++;
1372 }
1373 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1374 {
1375 p++;
1376 if (*p == '{')
1377 {
1378 level++;
1379 q=subexpression;
1380 for (p++; *p != '\0'; )
1381 {
1382 if (*p == '{')
1383 level++;
1384 else
1385 if (*p == '}')
1386 {
1387 level--;
1388 if (level == 0)
1389 break;
1390 }
1391 *q++=(*p++);
1392 }
1393 *q='\0';
1394 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1395 &beta,exception);
1396 point.x=alpha;
1397 point.y=beta;
1398 p++;
1399 }
1400 else
1401 if (*p == '[')
1402 {
1403 level++;
1404 q=subexpression;
1405 for (p++; *p != '\0'; )
1406 {
1407 if (*p == '[')
1408 level++;
1409 else
1410 if (*p == ']')
1411 {
1412 level--;
1413 if (level == 0)
1414 break;
1415 }
1416 *q++=(*p++);
1417 }
1418 *q='\0';
1419 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1420 &beta,exception);
1421 point.x+=alpha;
1422 point.y+=beta;
1423 p++;
1424 }
1425 if (*p == '.')
1426 p++;
1427 }
1428 }
1429 length=GetImageListLength(fx_info->images);
1430 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001431 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001432 i%=length;
1433 image=GetImageFromList(fx_info->images,i);
1434 if (image == (Image *) NULL)
1435 {
1436 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1437 "NoSuchImage","`%s'",expression);
1438 return(0.0);
1439 }
cristy4c08aed2011-07-01 19:47:50 +00001440 GetPixelInfo(image,&pixel);
1441 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001442 point.x,point.y,&pixel,exception);
cristy1707c6c2012-01-18 23:30:54 +00001443 if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1444 (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001445 (LocaleCompare(p,"saturation") != 0) &&
1446 (LocaleCompare(p,"lightness") != 0))
1447 {
1448 char
1449 name[MaxTextExtent];
1450
1451 (void) CopyMagickString(name,p,MaxTextExtent);
1452 for (q=name+(strlen(name)-1); q > name; q--)
1453 {
1454 if (*q == ')')
1455 break;
1456 if (*q == '.')
1457 {
1458 *q='\0';
1459 break;
1460 }
1461 }
1462 if ((strlen(name) > 2) &&
1463 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1464 {
cristy4c08aed2011-07-01 19:47:50 +00001465 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001466 *color;
1467
cristy4c08aed2011-07-01 19:47:50 +00001468 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1469 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001470 {
1471 pixel=(*color);
1472 p+=strlen(name);
1473 }
1474 else
cristy1707c6c2012-01-18 23:30:54 +00001475 {
1476 MagickBooleanType
1477 status;
1478
1479 status=QueryColorCompliance(name,AllCompliance,&pixel,
1480 fx_info->exception);
1481 if (status != MagickFalse)
1482 {
1483 (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1484 name),ClonePixelInfo(&pixel));
1485 p+=strlen(name);
1486 }
1487 }
cristy3ed852e2009-09-05 21:47:34 +00001488 }
1489 }
1490 (void) CopyMagickString(symbol,p,MaxTextExtent);
1491 StripString(symbol);
1492 if (*symbol == '\0')
1493 {
1494 switch (channel)
1495 {
cristy0568ffc2011-07-25 16:54:14 +00001496 case RedPixelChannel: return(QuantumScale*pixel.red);
1497 case GreenPixelChannel: return(QuantumScale*pixel.green);
1498 case BluePixelChannel: return(QuantumScale*pixel.blue);
1499 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001500 {
1501 if (image->colorspace != CMYKColorspace)
1502 {
1503 (void) ThrowMagickException(exception,GetMagickModule(),
cristy1a020e42011-12-06 18:13:23 +00001504 ImageError,"ColorSeparatedImageRequired","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00001505 image->filename);
1506 return(0.0);
1507 }
cristy4c08aed2011-07-01 19:47:50 +00001508 return(QuantumScale*pixel.black);
1509 }
cristy0568ffc2011-07-25 16:54:14 +00001510 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001511 {
1512 MagickRealType
1513 alpha;
1514
1515 if (pixel.matte == MagickFalse)
1516 return(1.0);
1517 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1518 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001519 }
cristya382aca2011-12-06 18:22:48 +00001520 case IndexPixelChannel:
1521 return(0.0);
cristyb3a73b52011-07-26 01:34:43 +00001522 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001523 {
cristy4c08aed2011-07-01 19:47:50 +00001524 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001525 }
cristy3ed852e2009-09-05 21:47:34 +00001526 default:
1527 break;
1528 }
1529 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1530 "UnableToParseExpression","`%s'",p);
1531 return(0.0);
1532 }
1533 switch (*symbol)
1534 {
1535 case 'A':
1536 case 'a':
1537 {
1538 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001539 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001540 break;
1541 }
1542 case 'B':
1543 case 'b':
1544 {
1545 if (LocaleCompare(symbol,"b") == 0)
1546 return(QuantumScale*pixel.blue);
1547 break;
1548 }
1549 case 'C':
1550 case 'c':
1551 {
1552 if (LocaleNCompare(symbol,"channel",7) == 0)
1553 {
1554 GeometryInfo
1555 channel_info;
1556
1557 MagickStatusType
1558 flags;
1559
1560 flags=ParseGeometry(symbol+7,&channel_info);
1561 if (image->colorspace == CMYKColorspace)
1562 switch (channel)
1563 {
cristy0568ffc2011-07-25 16:54:14 +00001564 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001565 {
1566 if ((flags & RhoValue) == 0)
1567 return(0.0);
1568 return(channel_info.rho);
1569 }
cristy0568ffc2011-07-25 16:54:14 +00001570 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001571 {
1572 if ((flags & SigmaValue) == 0)
1573 return(0.0);
1574 return(channel_info.sigma);
1575 }
cristy0568ffc2011-07-25 16:54:14 +00001576 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001577 {
1578 if ((flags & XiValue) == 0)
1579 return(0.0);
1580 return(channel_info.xi);
1581 }
cristy0568ffc2011-07-25 16:54:14 +00001582 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001583 {
1584 if ((flags & PsiValue) == 0)
1585 return(0.0);
1586 return(channel_info.psi);
1587 }
cristy0568ffc2011-07-25 16:54:14 +00001588 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((flags & ChiValue) == 0)
1591 return(0.0);
1592 return(channel_info.chi);
1593 }
1594 default:
1595 return(0.0);
1596 }
1597 switch (channel)
1598 {
cristy0568ffc2011-07-25 16:54:14 +00001599 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001600 {
1601 if ((flags & RhoValue) == 0)
1602 return(0.0);
1603 return(channel_info.rho);
1604 }
cristy0568ffc2011-07-25 16:54:14 +00001605 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001606 {
1607 if ((flags & SigmaValue) == 0)
1608 return(0.0);
1609 return(channel_info.sigma);
1610 }
cristy0568ffc2011-07-25 16:54:14 +00001611 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001612 {
1613 if ((flags & XiValue) == 0)
1614 return(0.0);
1615 return(channel_info.xi);
1616 }
cristy0568ffc2011-07-25 16:54:14 +00001617 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001618 {
1619 if ((flags & ChiValue) == 0)
1620 return(0.0);
1621 return(channel_info.chi);
1622 }
cristy0568ffc2011-07-25 16:54:14 +00001623 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001624 {
1625 if ((flags & PsiValue) == 0)
1626 return(0.0);
1627 return(channel_info.psi);
1628 }
cristy3ed852e2009-09-05 21:47:34 +00001629 default:
1630 return(0.0);
1631 }
1632 return(0.0);
1633 }
1634 if (LocaleCompare(symbol,"c") == 0)
1635 return(QuantumScale*pixel.red);
1636 break;
1637 }
1638 case 'D':
1639 case 'd':
1640 {
1641 if (LocaleNCompare(symbol,"depth",5) == 0)
1642 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1643 break;
1644 }
1645 case 'G':
1646 case 'g':
1647 {
1648 if (LocaleCompare(symbol,"g") == 0)
1649 return(QuantumScale*pixel.green);
1650 break;
1651 }
1652 case 'K':
1653 case 'k':
1654 {
1655 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1656 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1657 if (LocaleCompare(symbol,"k") == 0)
1658 {
1659 if (image->colorspace != CMYKColorspace)
1660 {
1661 (void) ThrowMagickException(exception,GetMagickModule(),
1662 OptionError,"ColorSeparatedImageRequired","`%s'",
1663 image->filename);
1664 return(0.0);
1665 }
cristy4c08aed2011-07-01 19:47:50 +00001666 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001667 }
1668 break;
1669 }
1670 case 'H':
1671 case 'h':
1672 {
1673 if (LocaleCompare(symbol,"h") == 0)
1674 return((MagickRealType) image->rows);
1675 if (LocaleCompare(symbol,"hue") == 0)
1676 {
1677 double
1678 hue,
1679 lightness,
1680 saturation;
1681
cristyda1f9c12011-10-02 21:39:49 +00001682 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1683 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001684 return(hue);
1685 }
1686 break;
1687 }
1688 case 'I':
1689 case 'i':
1690 {
1691 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1692 (LocaleCompare(symbol,"image.minima") == 0) ||
1693 (LocaleCompare(symbol,"image.maxima") == 0) ||
1694 (LocaleCompare(symbol,"image.mean") == 0) ||
1695 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1696 (LocaleCompare(symbol,"image.skewness") == 0) ||
1697 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1698 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1699 if (LocaleCompare(symbol,"image.resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001700 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001701 if (LocaleCompare(symbol,"image.resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001702 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001703 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001704 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001705 if (LocaleCompare(symbol,"i") == 0)
1706 return((MagickRealType) x);
1707 break;
1708 }
1709 case 'J':
1710 case 'j':
1711 {
1712 if (LocaleCompare(symbol,"j") == 0)
1713 return((MagickRealType) y);
1714 break;
1715 }
1716 case 'L':
1717 case 'l':
1718 {
1719 if (LocaleCompare(symbol,"lightness") == 0)
1720 {
1721 double
1722 hue,
1723 lightness,
1724 saturation;
1725
cristyda1f9c12011-10-02 21:39:49 +00001726 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1727 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001728 return(lightness);
1729 }
1730 if (LocaleCompare(symbol,"luminance") == 0)
1731 {
1732 double
1733 luminence;
1734
1735 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1736 return(QuantumScale*luminence);
1737 }
1738 break;
1739 }
1740 case 'M':
1741 case 'm':
1742 {
1743 if (LocaleNCompare(symbol,"maxima",6) == 0)
1744 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1745 if (LocaleNCompare(symbol,"mean",4) == 0)
1746 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1747 if (LocaleNCompare(symbol,"minima",6) == 0)
1748 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1749 if (LocaleCompare(symbol,"m") == 0)
1750 return(QuantumScale*pixel.blue);
1751 break;
1752 }
1753 case 'N':
1754 case 'n':
1755 {
1756 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001757 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001758 break;
1759 }
1760 case 'O':
1761 case 'o':
1762 {
1763 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001764 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001765 break;
1766 }
1767 case 'P':
1768 case 'p':
1769 {
1770 if (LocaleCompare(symbol,"page.height") == 0)
1771 return((MagickRealType) image->page.height);
1772 if (LocaleCompare(symbol,"page.width") == 0)
1773 return((MagickRealType) image->page.width);
1774 if (LocaleCompare(symbol,"page.x") == 0)
1775 return((MagickRealType) image->page.x);
1776 if (LocaleCompare(symbol,"page.y") == 0)
1777 return((MagickRealType) image->page.y);
1778 break;
1779 }
1780 case 'R':
1781 case 'r':
1782 {
1783 if (LocaleCompare(symbol,"resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001784 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001785 if (LocaleCompare(symbol,"resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001786 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001787 if (LocaleCompare(symbol,"r") == 0)
1788 return(QuantumScale*pixel.red);
1789 break;
1790 }
1791 case 'S':
1792 case 's':
1793 {
1794 if (LocaleCompare(symbol,"saturation") == 0)
1795 {
1796 double
1797 hue,
1798 lightness,
1799 saturation;
1800
cristyda1f9c12011-10-02 21:39:49 +00001801 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1802 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001803 return(saturation);
1804 }
1805 if (LocaleNCompare(symbol,"skewness",8) == 0)
1806 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1807 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1808 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1809 break;
1810 }
1811 case 'T':
1812 case 't':
1813 {
1814 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001815 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001816 break;
1817 }
1818 case 'W':
1819 case 'w':
1820 {
1821 if (LocaleCompare(symbol,"w") == 0)
1822 return((MagickRealType) image->columns);
1823 break;
1824 }
1825 case 'Y':
1826 case 'y':
1827 {
1828 if (LocaleCompare(symbol,"y") == 0)
1829 return(QuantumScale*pixel.green);
1830 break;
1831 }
1832 case 'Z':
1833 case 'z':
1834 {
1835 if (LocaleCompare(symbol,"z") == 0)
1836 {
1837 MagickRealType
1838 depth;
1839
cristyfefab1b2011-07-05 00:33:22 +00001840 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001841 return(depth);
1842 }
1843 break;
1844 }
1845 default:
1846 break;
1847 }
1848 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1849 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001850 return((MagickRealType) StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001851 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1852 "UnableToParseExpression","`%s'",symbol);
1853 return(0.0);
1854}
1855
1856static const char *FxOperatorPrecedence(const char *expression,
1857 ExceptionInfo *exception)
1858{
1859 typedef enum
1860 {
1861 UndefinedPrecedence,
1862 NullPrecedence,
1863 BitwiseComplementPrecedence,
1864 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001865 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001866 MultiplyPrecedence,
1867 AdditionPrecedence,
1868 ShiftPrecedence,
1869 RelationalPrecedence,
1870 EquivalencyPrecedence,
1871 BitwiseAndPrecedence,
1872 BitwiseOrPrecedence,
1873 LogicalAndPrecedence,
1874 LogicalOrPrecedence,
1875 TernaryPrecedence,
1876 AssignmentPrecedence,
1877 CommaPrecedence,
1878 SeparatorPrecedence
1879 } FxPrecedence;
1880
1881 FxPrecedence
1882 precedence,
1883 target;
1884
1885 register const char
1886 *subexpression;
1887
1888 register int
1889 c;
1890
cristybb503372010-05-27 20:51:26 +00001891 size_t
cristy3ed852e2009-09-05 21:47:34 +00001892 level;
1893
1894 c=0;
1895 level=0;
1896 subexpression=(const char *) NULL;
1897 target=NullPrecedence;
1898 while (*expression != '\0')
1899 {
1900 precedence=UndefinedPrecedence;
1901 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1902 {
1903 expression++;
1904 continue;
1905 }
cristy488fa882010-03-01 22:34:24 +00001906 switch (*expression)
1907 {
1908 case 'A':
1909 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001910 {
cristyb33454f2011-08-03 02:10:45 +00001911#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001912 if (LocaleNCompare(expression,"acosh",5) == 0)
1913 {
1914 expression+=5;
1915 break;
1916 }
cristyb33454f2011-08-03 02:10:45 +00001917#endif
1918#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001919 if (LocaleNCompare(expression,"asinh",5) == 0)
1920 {
1921 expression+=5;
1922 break;
1923 }
cristyb33454f2011-08-03 02:10:45 +00001924#endif
1925#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001926 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001927 {
1928 expression+=5;
1929 break;
1930 }
cristyb33454f2011-08-03 02:10:45 +00001931#endif
cristy488fa882010-03-01 22:34:24 +00001932 break;
cristy3ed852e2009-09-05 21:47:34 +00001933 }
cristy62d455f2011-11-03 11:42:28 +00001934 case 'E':
1935 case 'e':
1936 {
1937 if ((LocaleNCompare(expression,"E+",2) == 0) ||
1938 (LocaleNCompare(expression,"E-",2) == 0))
1939 {
1940 expression+=2; /* scientific notation */
1941 break;
1942 }
1943 }
cristy488fa882010-03-01 22:34:24 +00001944 case 'J':
1945 case 'j':
1946 {
1947 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1948 (LocaleNCompare(expression,"j1",2) == 0))
1949 {
1950 expression+=2;
1951 break;
1952 }
1953 break;
1954 }
cristy2def9322010-06-18 23:59:37 +00001955 case '#':
1956 {
1957 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1958 expression++;
1959 break;
1960 }
cristy488fa882010-03-01 22:34:24 +00001961 default:
1962 break;
1963 }
cristy3ed852e2009-09-05 21:47:34 +00001964 if ((c == (int) '{') || (c == (int) '['))
1965 level++;
1966 else
1967 if ((c == (int) '}') || (c == (int) ']'))
1968 level--;
1969 if (level == 0)
1970 switch ((unsigned char) *expression)
1971 {
1972 case '~':
1973 case '!':
1974 {
1975 precedence=BitwiseComplementPrecedence;
1976 break;
1977 }
1978 case '^':
cristy6621e252010-08-13 00:42:57 +00001979 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001980 {
1981 precedence=ExponentPrecedence;
1982 break;
1983 }
1984 default:
1985 {
1986 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1987 (strchr(")",c) != (char *) NULL))) &&
1988 (((islower((int) ((char) *expression)) != 0) ||
1989 (strchr("(",(int) *expression) != (char *) NULL)) ||
1990 ((isdigit((int) ((char) c)) == 0) &&
1991 (isdigit((int) ((char) *expression)) != 0))) &&
1992 (strchr("xy",(int) *expression) == (char *) NULL))
1993 precedence=MultiplyPrecedence;
1994 break;
1995 }
1996 case '*':
1997 case '/':
1998 case '%':
1999 {
2000 precedence=MultiplyPrecedence;
2001 break;
2002 }
2003 case '+':
2004 case '-':
2005 {
2006 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
2007 (isalpha(c) != 0))
2008 precedence=AdditionPrecedence;
2009 break;
2010 }
2011 case LeftShiftOperator:
2012 case RightShiftOperator:
2013 {
2014 precedence=ShiftPrecedence;
2015 break;
2016 }
2017 case '<':
2018 case LessThanEqualOperator:
2019 case GreaterThanEqualOperator:
2020 case '>':
2021 {
2022 precedence=RelationalPrecedence;
2023 break;
2024 }
2025 case EqualOperator:
2026 case NotEqualOperator:
2027 {
2028 precedence=EquivalencyPrecedence;
2029 break;
2030 }
2031 case '&':
2032 {
2033 precedence=BitwiseAndPrecedence;
2034 break;
2035 }
2036 case '|':
2037 {
2038 precedence=BitwiseOrPrecedence;
2039 break;
2040 }
2041 case LogicalAndOperator:
2042 {
2043 precedence=LogicalAndPrecedence;
2044 break;
2045 }
2046 case LogicalOrOperator:
2047 {
2048 precedence=LogicalOrPrecedence;
2049 break;
2050 }
cristy116af162010-08-13 01:25:47 +00002051 case ExponentialNotation:
2052 {
2053 precedence=ExponentialNotationPrecedence;
2054 break;
2055 }
cristy3ed852e2009-09-05 21:47:34 +00002056 case ':':
2057 case '?':
2058 {
2059 precedence=TernaryPrecedence;
2060 break;
2061 }
2062 case '=':
2063 {
2064 precedence=AssignmentPrecedence;
2065 break;
2066 }
2067 case ',':
2068 {
2069 precedence=CommaPrecedence;
2070 break;
2071 }
2072 case ';':
2073 {
2074 precedence=SeparatorPrecedence;
2075 break;
2076 }
2077 }
2078 if ((precedence == BitwiseComplementPrecedence) ||
2079 (precedence == TernaryPrecedence) ||
2080 (precedence == AssignmentPrecedence))
2081 {
2082 if (precedence > target)
2083 {
2084 /*
2085 Right-to-left associativity.
2086 */
2087 target=precedence;
2088 subexpression=expression;
2089 }
2090 }
2091 else
2092 if (precedence >= target)
2093 {
2094 /*
2095 Left-to-right associativity.
2096 */
2097 target=precedence;
2098 subexpression=expression;
2099 }
2100 if (strchr("(",(int) *expression) != (char *) NULL)
2101 expression=FxSubexpression(expression,exception);
2102 c=(int) (*expression++);
2103 }
2104 return(subexpression);
2105}
2106
2107static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002108 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002109 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002110{
2111 char
2112 *q,
2113 subexpression[MaxTextExtent];
2114
2115 MagickRealType
2116 alpha,
2117 gamma;
2118
2119 register const char
2120 *p;
2121
2122 *beta=0.0;
2123 if (exception->severity != UndefinedException)
2124 return(0.0);
2125 while (isspace((int) *expression) != 0)
2126 expression++;
2127 if (*expression == '\0')
2128 {
2129 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2130 "MissingExpression","`%s'",expression);
2131 return(0.0);
2132 }
cristy66322f02010-05-17 11:40:48 +00002133 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002134 p=FxOperatorPrecedence(expression,exception);
2135 if (p != (const char *) NULL)
2136 {
2137 (void) CopyMagickString(subexpression,expression,(size_t)
2138 (p-expression+1));
2139 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2140 exception);
2141 switch ((unsigned char) *p)
2142 {
2143 case '~':
2144 {
2145 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002146 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002147 return(*beta);
2148 }
2149 case '!':
2150 {
2151 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2152 return(*beta == 0.0 ? 1.0 : 0.0);
2153 }
2154 case '^':
2155 {
2156 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2157 channel,x,y,++p,beta,exception));
2158 return(*beta);
2159 }
2160 case '*':
cristy116af162010-08-13 01:25:47 +00002161 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002162 {
2163 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2164 return(alpha*(*beta));
2165 }
2166 case '/':
2167 {
2168 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2169 if (*beta == 0.0)
2170 {
2171 if (exception->severity == UndefinedException)
2172 (void) ThrowMagickException(exception,GetMagickModule(),
2173 OptionError,"DivideByZero","`%s'",expression);
2174 return(0.0);
2175 }
2176 return(alpha/(*beta));
2177 }
2178 case '%':
2179 {
2180 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2181 *beta=fabs(floor(((double) *beta)+0.5));
2182 if (*beta == 0.0)
2183 {
2184 (void) ThrowMagickException(exception,GetMagickModule(),
2185 OptionError,"DivideByZero","`%s'",expression);
2186 return(0.0);
2187 }
2188 return(fmod((double) alpha,(double) *beta));
2189 }
2190 case '+':
2191 {
2192 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2193 return(alpha+(*beta));
2194 }
2195 case '-':
2196 {
2197 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2198 return(alpha-(*beta));
2199 }
2200 case LeftShiftOperator:
2201 {
2202 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002203 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002204 return(*beta);
2205 }
2206 case RightShiftOperator:
2207 {
2208 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002209 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002210 return(*beta);
2211 }
2212 case '<':
2213 {
2214 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2215 return(alpha < *beta ? 1.0 : 0.0);
2216 }
2217 case LessThanEqualOperator:
2218 {
2219 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2220 return(alpha <= *beta ? 1.0 : 0.0);
2221 }
2222 case '>':
2223 {
2224 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2225 return(alpha > *beta ? 1.0 : 0.0);
2226 }
2227 case GreaterThanEqualOperator:
2228 {
2229 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2230 return(alpha >= *beta ? 1.0 : 0.0);
2231 }
2232 case EqualOperator:
2233 {
2234 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2235 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2236 }
2237 case NotEqualOperator:
2238 {
2239 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2240 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2241 }
2242 case '&':
2243 {
2244 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002245 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002246 return(*beta);
2247 }
2248 case '|':
2249 {
2250 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002251 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002252 return(*beta);
2253 }
2254 case LogicalAndOperator:
2255 {
2256 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2257 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2258 return(*beta);
2259 }
2260 case LogicalOrOperator:
2261 {
2262 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2263 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2264 return(*beta);
2265 }
2266 case '?':
2267 {
2268 MagickRealType
2269 gamma;
2270
2271 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2272 q=subexpression;
2273 p=StringToken(":",&q);
2274 if (q == (char *) NULL)
2275 {
2276 (void) ThrowMagickException(exception,GetMagickModule(),
2277 OptionError,"UnableToParseExpression","`%s'",subexpression);
2278 return(0.0);
2279 }
2280 if (fabs((double) alpha) > MagickEpsilon)
2281 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2282 else
2283 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2284 return(gamma);
2285 }
2286 case '=':
2287 {
2288 char
2289 numeric[MaxTextExtent];
2290
2291 q=subexpression;
2292 while (isalpha((int) ((unsigned char) *q)) != 0)
2293 q++;
2294 if (*q != '\0')
2295 {
2296 (void) ThrowMagickException(exception,GetMagickModule(),
2297 OptionError,"UnableToParseExpression","`%s'",subexpression);
2298 return(0.0);
2299 }
2300 ClearMagickException(exception);
2301 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002302 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002303 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002304 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2305 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2306 subexpression),ConstantString(numeric));
2307 return(*beta);
2308 }
2309 case ',':
2310 {
2311 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2312 return(alpha);
2313 }
2314 case ';':
2315 {
2316 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2317 return(*beta);
2318 }
2319 default:
2320 {
2321 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2322 exception);
2323 return(gamma);
2324 }
2325 }
2326 }
2327 if (strchr("(",(int) *expression) != (char *) NULL)
2328 {
2329 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2330 subexpression[strlen(subexpression)-1]='\0';
2331 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2332 exception);
2333 return(gamma);
2334 }
cristy8b8a3ae2010-10-23 18:49:46 +00002335 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002336 {
2337 case '+':
2338 {
2339 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2340 exception);
2341 return(1.0*gamma);
2342 }
2343 case '-':
2344 {
2345 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2346 exception);
2347 return(-1.0*gamma);
2348 }
2349 case '~':
2350 {
2351 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2352 exception);
cristybb503372010-05-27 20:51:26 +00002353 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002354 }
2355 case 'A':
2356 case 'a':
2357 {
2358 if (LocaleNCompare(expression,"abs",3) == 0)
2359 {
2360 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2361 exception);
2362 return((MagickRealType) fabs((double) alpha));
2363 }
cristyb33454f2011-08-03 02:10:45 +00002364#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002365 if (LocaleNCompare(expression,"acosh",5) == 0)
2366 {
2367 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2368 exception);
2369 return((MagickRealType) acosh((double) alpha));
2370 }
cristyb33454f2011-08-03 02:10:45 +00002371#endif
cristy3ed852e2009-09-05 21:47:34 +00002372 if (LocaleNCompare(expression,"acos",4) == 0)
2373 {
2374 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2375 exception);
2376 return((MagickRealType) acos((double) alpha));
2377 }
cristy43c22f42010-03-30 12:34:07 +00002378#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002379 if (LocaleNCompare(expression,"airy",4) == 0)
2380 {
2381 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2382 exception);
2383 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002384 return(1.0);
2385 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002386 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002387 }
cristy43c22f42010-03-30 12:34:07 +00002388#endif
cristyb33454f2011-08-03 02:10:45 +00002389#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002390 if (LocaleNCompare(expression,"asinh",5) == 0)
2391 {
2392 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2393 exception);
2394 return((MagickRealType) asinh((double) alpha));
2395 }
cristyb33454f2011-08-03 02:10:45 +00002396#endif
cristy3ed852e2009-09-05 21:47:34 +00002397 if (LocaleNCompare(expression,"asin",4) == 0)
2398 {
2399 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2400 exception);
2401 return((MagickRealType) asin((double) alpha));
2402 }
2403 if (LocaleNCompare(expression,"alt",3) == 0)
2404 {
2405 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2406 exception);
cristybb503372010-05-27 20:51:26 +00002407 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002408 }
2409 if (LocaleNCompare(expression,"atan2",5) == 0)
2410 {
2411 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2412 exception);
2413 return((MagickRealType) atan2((double) alpha,(double) *beta));
2414 }
cristyb33454f2011-08-03 02:10:45 +00002415#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002416 if (LocaleNCompare(expression,"atanh",5) == 0)
2417 {
2418 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2419 exception);
2420 return((MagickRealType) atanh((double) alpha));
2421 }
cristyb33454f2011-08-03 02:10:45 +00002422#endif
cristy3ed852e2009-09-05 21:47:34 +00002423 if (LocaleNCompare(expression,"atan",4) == 0)
2424 {
2425 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2426 exception);
2427 return((MagickRealType) atan((double) alpha));
2428 }
2429 if (LocaleCompare(expression,"a") == 0)
2430 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2431 break;
2432 }
2433 case 'B':
2434 case 'b':
2435 {
2436 if (LocaleCompare(expression,"b") == 0)
2437 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2438 break;
2439 }
2440 case 'C':
2441 case 'c':
2442 {
2443 if (LocaleNCompare(expression,"ceil",4) == 0)
2444 {
2445 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2446 exception);
2447 return((MagickRealType) ceil((double) alpha));
2448 }
2449 if (LocaleNCompare(expression,"cosh",4) == 0)
2450 {
2451 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2452 exception);
2453 return((MagickRealType) cosh((double) alpha));
2454 }
2455 if (LocaleNCompare(expression,"cos",3) == 0)
2456 {
2457 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2458 exception);
2459 return((MagickRealType) cos((double) alpha));
2460 }
2461 if (LocaleCompare(expression,"c") == 0)
2462 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2463 break;
2464 }
2465 case 'D':
2466 case 'd':
2467 {
2468 if (LocaleNCompare(expression,"debug",5) == 0)
2469 {
2470 const char
2471 *type;
2472
2473 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2474 exception);
2475 if (fx_info->images->colorspace == CMYKColorspace)
2476 switch (channel)
2477 {
cristy0568ffc2011-07-25 16:54:14 +00002478 case CyanPixelChannel: type="cyan"; break;
2479 case MagentaPixelChannel: type="magenta"; break;
2480 case YellowPixelChannel: type="yellow"; break;
2481 case AlphaPixelChannel: type="opacity"; break;
2482 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002483 default: type="unknown"; break;
2484 }
2485 else
2486 switch (channel)
2487 {
cristy0568ffc2011-07-25 16:54:14 +00002488 case RedPixelChannel: type="red"; break;
2489 case GreenPixelChannel: type="green"; break;
2490 case BluePixelChannel: type="blue"; break;
2491 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002492 default: type="unknown"; break;
2493 }
2494 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2495 if (strlen(subexpression) > 1)
2496 subexpression[strlen(subexpression)-1]='\0';
2497 if (fx_info->file != (FILE *) NULL)
cristy1707c6c2012-01-18 23:30:54 +00002498 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2499 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2500 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002501 return(0.0);
2502 }
cristy5597a8d2011-11-04 00:25:32 +00002503 if (LocaleNCompare(expression,"drc",3) == 0)
2504 {
2505 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2506 exception);
2507 return((MagickRealType) (alpha/(*beta*(alpha-1.0)+1.0)));
2508 }
cristy3ed852e2009-09-05 21:47:34 +00002509 break;
2510 }
2511 case 'E':
2512 case 'e':
2513 {
2514 if (LocaleCompare(expression,"epsilon") == 0)
2515 return((MagickRealType) MagickEpsilon);
2516 if (LocaleNCompare(expression,"exp",3) == 0)
2517 {
2518 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2519 exception);
2520 return((MagickRealType) exp((double) alpha));
2521 }
2522 if (LocaleCompare(expression,"e") == 0)
2523 return((MagickRealType) 2.7182818284590452354);
2524 break;
2525 }
2526 case 'F':
2527 case 'f':
2528 {
2529 if (LocaleNCompare(expression,"floor",5) == 0)
2530 {
2531 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2532 exception);
2533 return((MagickRealType) floor((double) alpha));
2534 }
2535 break;
2536 }
2537 case 'G':
2538 case 'g':
2539 {
cristy9eeedea2011-11-02 19:04:05 +00002540 if (LocaleNCompare(expression,"gauss",5) == 0)
2541 {
2542 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2543 exception);
2544 gamma=exp((double) (-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2545 return((MagickRealType) gamma);
2546 }
cristyb0aad4c2011-11-02 19:30:35 +00002547 if (LocaleNCompare(expression,"gcd",3) == 0)
2548 {
2549 MagickOffsetType
2550 gcd;
2551
2552 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2553 exception);
cristy1707c6c2012-01-18 23:30:54 +00002554 gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2555 0.5));
cristyb0aad4c2011-11-02 19:30:35 +00002556 return((MagickRealType) gcd);
2557 }
cristy3ed852e2009-09-05 21:47:34 +00002558 if (LocaleCompare(expression,"g") == 0)
2559 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2560 break;
2561 }
2562 case 'H':
2563 case 'h':
2564 {
2565 if (LocaleCompare(expression,"h") == 0)
2566 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2567 if (LocaleCompare(expression,"hue") == 0)
2568 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2569 if (LocaleNCompare(expression,"hypot",5) == 0)
2570 {
2571 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2572 exception);
2573 return((MagickRealType) hypot((double) alpha,(double) *beta));
2574 }
2575 break;
2576 }
2577 case 'K':
2578 case 'k':
2579 {
2580 if (LocaleCompare(expression,"k") == 0)
2581 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2582 break;
2583 }
2584 case 'I':
2585 case 'i':
2586 {
2587 if (LocaleCompare(expression,"intensity") == 0)
2588 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2589 if (LocaleNCompare(expression,"int",3) == 0)
2590 {
2591 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2592 exception);
cristy16788e42010-08-13 13:44:26 +00002593 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002594 }
cristy82b20722011-11-05 21:52:36 +00002595#if defined(MAGICKCORE_HAVE_ISNAN)
cristy639399c2011-11-02 19:16:15 +00002596 if (LocaleNCompare(expression,"isnan",5) == 0)
2597 {
2598 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2599 exception);
cristy17a10202011-11-02 19:17:04 +00002600 return((MagickRealType) !!isnan((double) alpha));
cristy639399c2011-11-02 19:16:15 +00002601 }
cristy82b20722011-11-05 21:52:36 +00002602#endif
cristy3ed852e2009-09-05 21:47:34 +00002603 if (LocaleCompare(expression,"i") == 0)
2604 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2605 break;
2606 }
2607 case 'J':
2608 case 'j':
2609 {
2610 if (LocaleCompare(expression,"j") == 0)
2611 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002612#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002613 if (LocaleNCompare(expression,"j0",2) == 0)
2614 {
2615 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2616 exception);
2617 return((MagickRealType) j0((double) alpha));
2618 }
cristy161b9262010-03-20 19:34:32 +00002619#endif
2620#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002621 if (LocaleNCompare(expression,"j1",2) == 0)
2622 {
2623 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2624 exception);
2625 return((MagickRealType) j1((double) alpha));
2626 }
cristy161b9262010-03-20 19:34:32 +00002627#endif
cristyaa018fa2010-04-08 23:03:54 +00002628#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002629 if (LocaleNCompare(expression,"jinc",4) == 0)
2630 {
2631 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2632 exception);
cristy0946a822010-03-12 17:14:58 +00002633 if (alpha == 0.0)
2634 return(1.0);
cristy1707c6c2012-01-18 23:30:54 +00002635 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/(MagickPI*
2636 alpha));
cristyfce2f7b2010-03-12 00:29:49 +00002637 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002638 }
cristyaa018fa2010-04-08 23:03:54 +00002639#endif
cristy3ed852e2009-09-05 21:47:34 +00002640 break;
2641 }
2642 case 'L':
2643 case 'l':
2644 {
2645 if (LocaleNCompare(expression,"ln",2) == 0)
2646 {
2647 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2648 exception);
2649 return((MagickRealType) log((double) alpha));
2650 }
cristyc8ed5322010-08-31 12:07:59 +00002651 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002652 {
cristyc8ed5322010-08-31 12:07:59 +00002653 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002654 exception);
2655 return((MagickRealType) log10((double) alpha))/log10(2.0);
2656 }
2657 if (LocaleNCompare(expression,"log",3) == 0)
2658 {
2659 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2660 exception);
2661 return((MagickRealType) log10((double) alpha));
2662 }
2663 if (LocaleCompare(expression,"lightness") == 0)
2664 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2665 break;
2666 }
2667 case 'M':
2668 case 'm':
2669 {
2670 if (LocaleCompare(expression,"MaxRGB") == 0)
2671 return((MagickRealType) QuantumRange);
2672 if (LocaleNCompare(expression,"maxima",6) == 0)
2673 break;
2674 if (LocaleNCompare(expression,"max",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002675 {
2676 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2677 exception);
2678 return(alpha > *beta ? alpha : *beta);
2679 }
cristy3ed852e2009-09-05 21:47:34 +00002680 if (LocaleNCompare(expression,"minima",6) == 0)
2681 break;
2682 if (LocaleNCompare(expression,"min",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002683 {
2684 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2685 exception);
2686 return(alpha < *beta ? alpha : *beta);
2687 }
cristy3ed852e2009-09-05 21:47:34 +00002688 if (LocaleNCompare(expression,"mod",3) == 0)
2689 {
2690 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2691 exception);
cristy984049c2011-11-03 18:34:58 +00002692 gamma=alpha-floor((double) (alpha/(*beta)))*(*beta);
2693 return(gamma);
cristy3ed852e2009-09-05 21:47:34 +00002694 }
2695 if (LocaleCompare(expression,"m") == 0)
2696 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2697 break;
2698 }
2699 case 'N':
2700 case 'n':
2701 {
cristyad3502e2011-11-02 19:10:45 +00002702 if (LocaleNCompare(expression,"not",3) == 0)
2703 {
2704 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2705 exception);
2706 return((MagickRealType) (alpha < MagickEpsilon));
2707 }
cristy3ed852e2009-09-05 21:47:34 +00002708 if (LocaleCompare(expression,"n") == 0)
2709 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2710 break;
2711 }
2712 case 'O':
2713 case 'o':
2714 {
2715 if (LocaleCompare(expression,"Opaque") == 0)
2716 return(1.0);
2717 if (LocaleCompare(expression,"o") == 0)
2718 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2719 break;
2720 }
2721 case 'P':
2722 case 'p':
2723 {
cristy670aa3c2011-11-03 00:54:00 +00002724 if (LocaleCompare(expression,"phi") == 0)
2725 return((MagickRealType) MagickPHI);
cristy3ed852e2009-09-05 21:47:34 +00002726 if (LocaleCompare(expression,"pi") == 0)
2727 return((MagickRealType) MagickPI);
2728 if (LocaleNCompare(expression,"pow",3) == 0)
2729 {
2730 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2731 exception);
2732 return((MagickRealType) pow((double) alpha,(double) *beta));
2733 }
2734 if (LocaleCompare(expression,"p") == 0)
2735 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2736 break;
2737 }
2738 case 'Q':
2739 case 'q':
2740 {
2741 if (LocaleCompare(expression,"QuantumRange") == 0)
2742 return((MagickRealType) QuantumRange);
2743 if (LocaleCompare(expression,"QuantumScale") == 0)
2744 return((MagickRealType) QuantumScale);
2745 break;
2746 }
2747 case 'R':
2748 case 'r':
2749 {
2750 if (LocaleNCompare(expression,"rand",4) == 0)
2751 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2752 if (LocaleNCompare(expression,"round",5) == 0)
2753 {
2754 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2755 exception);
cristy16788e42010-08-13 13:44:26 +00002756 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002757 }
2758 if (LocaleCompare(expression,"r") == 0)
2759 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2760 break;
2761 }
2762 case 'S':
2763 case 's':
2764 {
2765 if (LocaleCompare(expression,"saturation") == 0)
2766 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2767 if (LocaleNCompare(expression,"sign",4) == 0)
2768 {
2769 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2770 exception);
2771 return(alpha < 0.0 ? -1.0 : 1.0);
2772 }
cristya6a09e72010-03-02 14:51:02 +00002773 if (LocaleNCompare(expression,"sinc",4) == 0)
2774 {
2775 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2776 exception);
2777 if (alpha == 0)
2778 return(1.0);
2779 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2780 (MagickPI*alpha));
2781 return(gamma);
2782 }
cristy3ed852e2009-09-05 21:47:34 +00002783 if (LocaleNCompare(expression,"sinh",4) == 0)
2784 {
2785 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2786 exception);
2787 return((MagickRealType) sinh((double) alpha));
2788 }
2789 if (LocaleNCompare(expression,"sin",3) == 0)
2790 {
2791 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2792 exception);
2793 return((MagickRealType) sin((double) alpha));
2794 }
2795 if (LocaleNCompare(expression,"sqrt",4) == 0)
2796 {
2797 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2798 exception);
2799 return((MagickRealType) sqrt((double) alpha));
2800 }
cristy9eeedea2011-11-02 19:04:05 +00002801 if (LocaleNCompare(expression,"squish",6) == 0)
2802 {
2803 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2804 exception);
2805 return((MagickRealType) (1.0/(1.0+exp((double) (4.0*alpha)))));
2806 }
cristy3ed852e2009-09-05 21:47:34 +00002807 if (LocaleCompare(expression,"s") == 0)
2808 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2809 break;
2810 }
2811 case 'T':
2812 case 't':
2813 {
2814 if (LocaleNCompare(expression,"tanh",4) == 0)
2815 {
2816 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2817 exception);
2818 return((MagickRealType) tanh((double) alpha));
2819 }
2820 if (LocaleNCompare(expression,"tan",3) == 0)
2821 {
2822 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2823 exception);
2824 return((MagickRealType) tan((double) alpha));
2825 }
2826 if (LocaleCompare(expression,"Transparent") == 0)
2827 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002828 if (LocaleNCompare(expression,"trunc",5) == 0)
2829 {
2830 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2831 exception);
2832 if (alpha >= 0.0)
2833 return((MagickRealType) floor((double) alpha));
2834 return((MagickRealType) ceil((double) alpha));
2835 }
cristy3ed852e2009-09-05 21:47:34 +00002836 if (LocaleCompare(expression,"t") == 0)
2837 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2838 break;
2839 }
2840 case 'U':
2841 case 'u':
2842 {
2843 if (LocaleCompare(expression,"u") == 0)
2844 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2845 break;
2846 }
2847 case 'V':
2848 case 'v':
2849 {
2850 if (LocaleCompare(expression,"v") == 0)
2851 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2852 break;
2853 }
2854 case 'W':
2855 case 'w':
2856 {
cristy9eeedea2011-11-02 19:04:05 +00002857 if (LocaleNCompare(expression,"while",5) == 0)
2858 {
2859 do
2860 {
2861 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2862 exception);
2863 } while (fabs((double) alpha) >= MagickEpsilon);
2864 return((MagickRealType) *beta);
2865 }
cristy3ed852e2009-09-05 21:47:34 +00002866 if (LocaleCompare(expression,"w") == 0)
2867 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2868 break;
2869 }
2870 case 'Y':
2871 case 'y':
2872 {
2873 if (LocaleCompare(expression,"y") == 0)
2874 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2875 break;
2876 }
2877 case 'Z':
2878 case 'z':
2879 {
2880 if (LocaleCompare(expression,"z") == 0)
2881 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2882 break;
2883 }
2884 default:
2885 break;
2886 }
2887 q=(char *) expression;
cristydbdd0e32011-11-04 23:29:40 +00002888 alpha=InterpretSiPrefixValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002889 if (q == expression)
2890 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2891 return(alpha);
2892}
2893
cristy7832dc22011-09-05 01:21:53 +00002894MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
cristy3ed852e2009-09-05 21:47:34 +00002895 MagickRealType *alpha,ExceptionInfo *exception)
2896{
2897 MagickBooleanType
2898 status;
2899
cristy541ae572011-08-05 19:08:59 +00002900 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2901 exception);
cristy3ed852e2009-09-05 21:47:34 +00002902 return(status);
2903}
2904
2905MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2906 MagickRealType *alpha,ExceptionInfo *exception)
2907{
2908 FILE
2909 *file;
2910
2911 MagickBooleanType
2912 status;
2913
2914 file=fx_info->file;
2915 fx_info->file=(FILE *) NULL;
cristy541ae572011-08-05 19:08:59 +00002916 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2917 exception);
cristy3ed852e2009-09-05 21:47:34 +00002918 fx_info->file=file;
2919 return(status);
2920}
2921
cristy7832dc22011-09-05 01:21:53 +00002922MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002923 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002924 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002925{
2926 MagickRealType
2927 beta;
2928
2929 beta=0.0;
2930 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2931 exception);
2932 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2933}
2934
2935/*
2936%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2937% %
2938% %
2939% %
2940% F x I m a g e %
2941% %
2942% %
2943% %
2944%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2945%
2946% FxImage() applies a mathematical expression to the specified image.
2947%
2948% The format of the FxImage method is:
2949%
2950% Image *FxImage(const Image *image,const char *expression,
2951% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002952%
2953% A description of each parameter follows:
2954%
2955% o image: the image.
2956%
cristy3ed852e2009-09-05 21:47:34 +00002957% o expression: A mathematical expression.
2958%
2959% o exception: return any errors or warnings in this structure.
2960%
2961*/
2962
2963static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2964{
cristybb503372010-05-27 20:51:26 +00002965 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002966 i;
2967
2968 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002969 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002970 if (fx_info[i] != (FxInfo *) NULL)
2971 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002972 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002973 return(fx_info);
2974}
2975
2976static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2977 ExceptionInfo *exception)
2978{
2979 char
2980 *fx_expression;
2981
2982 FxInfo
2983 **fx_info;
2984
2985 MagickRealType
2986 alpha;
2987
cristybb503372010-05-27 20:51:26 +00002988 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002989 i;
2990
cristybb503372010-05-27 20:51:26 +00002991 size_t
cristy3ed852e2009-09-05 21:47:34 +00002992 number_threads;
2993
2994 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002995 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002996 if (fx_info == (FxInfo **) NULL)
2997 return((FxInfo **) NULL);
2998 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2999 if (*expression != '@')
3000 fx_expression=ConstantString(expression);
3001 else
3002 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00003003 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00003004 {
3005 fx_info[i]=AcquireFxInfo(image,fx_expression);
3006 if (fx_info[i] == (FxInfo *) NULL)
3007 return(DestroyFxThreadSet(fx_info));
3008 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
3009 }
3010 fx_expression=DestroyString(fx_expression);
3011 return(fx_info);
3012}
3013
3014MagickExport Image *FxImage(const Image *image,const char *expression,
3015 ExceptionInfo *exception)
3016{
cristy3ed852e2009-09-05 21:47:34 +00003017#define FxImageTag "Fx/Image"
3018
cristyfa112112010-01-04 17:48:07 +00003019 CacheView
cristy79cedc72011-07-25 00:41:15 +00003020 *fx_view,
3021 *image_view;
cristyfa112112010-01-04 17:48:07 +00003022
cristy3ed852e2009-09-05 21:47:34 +00003023 FxInfo
cristyfa112112010-01-04 17:48:07 +00003024 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00003025
3026 Image
3027 *fx_image;
3028
cristy3ed852e2009-09-05 21:47:34 +00003029 MagickBooleanType
3030 status;
3031
cristybb503372010-05-27 20:51:26 +00003032 MagickOffsetType
3033 progress;
3034
cristy3ed852e2009-09-05 21:47:34 +00003035 MagickRealType
3036 alpha;
3037
cristybb503372010-05-27 20:51:26 +00003038 ssize_t
3039 y;
3040
cristy3ed852e2009-09-05 21:47:34 +00003041 assert(image != (Image *) NULL);
3042 assert(image->signature == MagickSignature);
3043 if (image->debug != MagickFalse)
3044 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00003045 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003046 if (fx_image == (Image *) NULL)
3047 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003048 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003049 {
cristy3ed852e2009-09-05 21:47:34 +00003050 fx_image=DestroyImage(fx_image);
3051 return((Image *) NULL);
3052 }
3053 fx_info=AcquireFxThreadSet(image,expression,exception);
3054 if (fx_info == (FxInfo **) NULL)
3055 {
3056 fx_image=DestroyImage(fx_image);
3057 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3058 }
3059 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3060 if (status == MagickFalse)
3061 {
3062 fx_image=DestroyImage(fx_image);
3063 fx_info=DestroyFxThreadSet(fx_info);
3064 return((Image *) NULL);
3065 }
3066 /*
3067 Fx image.
3068 */
3069 status=MagickTrue;
3070 progress=0;
cristy79cedc72011-07-25 00:41:15 +00003071 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003072 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003073#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003074 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003075#endif
cristybb503372010-05-27 20:51:26 +00003076 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003077 {
cristy5c9e6f22010-09-17 17:31:01 +00003078 const int
3079 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003080
cristy79cedc72011-07-25 00:41:15 +00003081 register const Quantum
3082 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003083
cristy4c08aed2011-07-01 19:47:50 +00003084 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003085 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003086
cristy79cedc72011-07-25 00:41:15 +00003087 register ssize_t
3088 x;
3089
cristy3ed852e2009-09-05 21:47:34 +00003090 if (status == MagickFalse)
3091 continue;
cristy79cedc72011-07-25 00:41:15 +00003092 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003093 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003094 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003095 {
3096 status=MagickFalse;
3097 continue;
3098 }
cristybb503372010-05-27 20:51:26 +00003099 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003100 {
cristy79cedc72011-07-25 00:41:15 +00003101 register ssize_t
3102 i;
3103
3104 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3105 {
3106 MagickRealType
3107 alpha;
3108
3109 PixelChannel
3110 channel;
3111
3112 PixelTrait
3113 fx_traits,
3114 traits;
3115
cristye2a912b2011-12-05 20:02:07 +00003116 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003117 traits=GetPixelChannelMapTraits(image,channel);
cristy79cedc72011-07-25 00:41:15 +00003118 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
cristy010d7d12011-08-31 01:02:48 +00003119 if ((traits == UndefinedPixelTrait) ||
3120 (fx_traits == UndefinedPixelTrait))
cristy79cedc72011-07-25 00:41:15 +00003121 continue;
cristyd09f8802012-02-04 16:44:10 +00003122 if (((fx_traits & CopyPixelTrait) != 0) ||
3123 (GetPixelMask(image,p) != 0))
cristy79cedc72011-07-25 00:41:15 +00003124 {
cristy0beccfa2011-09-25 20:47:53 +00003125 SetPixelChannel(fx_image,channel,p[i],q);
cristy79cedc72011-07-25 00:41:15 +00003126 continue;
3127 }
3128 alpha=0.0;
cristya382aca2011-12-06 18:22:48 +00003129 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3130 exception);
cristyb3a73b52011-07-26 01:34:43 +00003131 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003132 }
3133 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003134 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003135 }
3136 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3137 status=MagickFalse;
3138 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3139 {
3140 MagickBooleanType
3141 proceed;
3142
cristyb5d5f722009-11-04 03:03:49 +00003143#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003144 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003145#endif
3146 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3147 if (proceed == MagickFalse)
3148 status=MagickFalse;
3149 }
3150 }
cristy3ed852e2009-09-05 21:47:34 +00003151 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003152 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003153 fx_info=DestroyFxThreadSet(fx_info);
3154 if (status == MagickFalse)
3155 fx_image=DestroyImage(fx_image);
3156 return(fx_image);
3157}
3158
3159/*
3160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3161% %
3162% %
3163% %
3164% I m p l o d e I m a g e %
3165% %
3166% %
3167% %
3168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3169%
3170% ImplodeImage() creates a new image that is a copy of an existing
3171% one with the image pixels "implode" by the specified percentage. It
3172% allocates the memory necessary for the new Image structure and returns a
3173% pointer to the new image.
3174%
3175% The format of the ImplodeImage method is:
3176%
3177% Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003178% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003179%
3180% A description of each parameter follows:
3181%
3182% o implode_image: Method ImplodeImage returns a pointer to the image
3183% after it is implode. A null image is returned if there is a memory
3184% shortage.
3185%
3186% o image: the image.
3187%
3188% o amount: Define the extent of the implosion.
3189%
cristy76f512e2011-09-12 01:26:56 +00003190% o method: the pixel interpolation method.
3191%
cristy3ed852e2009-09-05 21:47:34 +00003192% o exception: return any errors or warnings in this structure.
3193%
3194*/
3195MagickExport Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003196 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003197{
3198#define ImplodeImageTag "Implode/Image"
3199
cristyfa112112010-01-04 17:48:07 +00003200 CacheView
3201 *image_view,
3202 *implode_view;
3203
cristy3ed852e2009-09-05 21:47:34 +00003204 Image
3205 *implode_image;
3206
cristy3ed852e2009-09-05 21:47:34 +00003207 MagickBooleanType
3208 status;
3209
cristybb503372010-05-27 20:51:26 +00003210 MagickOffsetType
3211 progress;
3212
cristy3ed852e2009-09-05 21:47:34 +00003213 MagickRealType
3214 radius;
3215
3216 PointInfo
3217 center,
3218 scale;
3219
cristybb503372010-05-27 20:51:26 +00003220 ssize_t
3221 y;
3222
cristy3ed852e2009-09-05 21:47:34 +00003223 /*
3224 Initialize implode image attributes.
3225 */
3226 assert(image != (Image *) NULL);
3227 assert(image->signature == MagickSignature);
3228 if (image->debug != MagickFalse)
3229 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3230 assert(exception != (ExceptionInfo *) NULL);
3231 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00003232 implode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3233 exception);
cristy3ed852e2009-09-05 21:47:34 +00003234 if (implode_image == (Image *) NULL)
3235 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003236 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003237 {
cristy3ed852e2009-09-05 21:47:34 +00003238 implode_image=DestroyImage(implode_image);
3239 return((Image *) NULL);
3240 }
cristy4c08aed2011-07-01 19:47:50 +00003241 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003242 implode_image->matte=MagickTrue;
3243 /*
3244 Compute scaling factor.
3245 */
3246 scale.x=1.0;
3247 scale.y=1.0;
3248 center.x=0.5*image->columns;
3249 center.y=0.5*image->rows;
3250 radius=center.x;
3251 if (image->columns > image->rows)
3252 scale.y=(double) image->columns/(double) image->rows;
3253 else
3254 if (image->columns < image->rows)
3255 {
3256 scale.x=(double) image->rows/(double) image->columns;
3257 radius=center.y;
3258 }
3259 /*
3260 Implode image.
3261 */
3262 status=MagickTrue;
3263 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003264 image_view=AcquireCacheView(image);
3265 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003266#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003267 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003268#endif
cristybb503372010-05-27 20:51:26 +00003269 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003270 {
cristy3ed852e2009-09-05 21:47:34 +00003271 MagickRealType
3272 distance;
3273
3274 PointInfo
3275 delta;
3276
cristy6d188022011-09-12 13:23:33 +00003277 register const Quantum
3278 *restrict p;
3279
cristybb503372010-05-27 20:51:26 +00003280 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003281 x;
3282
cristy4c08aed2011-07-01 19:47:50 +00003283 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003284 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003285
3286 if (status == MagickFalse)
3287 continue;
cristy6d188022011-09-12 13:23:33 +00003288 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003289 q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003290 exception);
cristy6d188022011-09-12 13:23:33 +00003291 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003292 {
3293 status=MagickFalse;
3294 continue;
3295 }
cristy3ed852e2009-09-05 21:47:34 +00003296 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00003297 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003298 {
cristy6d188022011-09-12 13:23:33 +00003299 register ssize_t
3300 i;
3301
cristy3ed852e2009-09-05 21:47:34 +00003302 /*
3303 Determine if the pixel is within an ellipse.
3304 */
cristy10a6c612012-01-29 21:41:05 +00003305 if (GetPixelMask(image,p) != 0)
3306 {
3307 p+=GetPixelChannels(image);
3308 q+=GetPixelChannels(implode_image);
3309 continue;
3310 }
cristy3ed852e2009-09-05 21:47:34 +00003311 delta.x=scale.x*(double) (x-center.x);
3312 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00003313 if (distance >= (radius*radius))
cristya6d7a9b2012-01-18 20:04:48 +00003314 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy6d188022011-09-12 13:23:33 +00003315 {
cristya6d7a9b2012-01-18 20:04:48 +00003316 PixelChannel
3317 channel;
3318
cristy1707c6c2012-01-18 23:30:54 +00003319 PixelTrait
3320 implode_traits,
3321 traits;
3322
cristya6d7a9b2012-01-18 20:04:48 +00003323 channel=GetPixelChannelMapChannel(image,i);
cristy1707c6c2012-01-18 23:30:54 +00003324 traits=GetPixelChannelMapTraits(image,channel);
3325 implode_traits=GetPixelChannelMapTraits(implode_image,channel);
3326 if ((traits == UndefinedPixelTrait) ||
3327 (implode_traits == UndefinedPixelTrait))
3328 continue;
cristya6d7a9b2012-01-18 20:04:48 +00003329 SetPixelChannel(implode_image,channel,p[i],q);
cristy6d188022011-09-12 13:23:33 +00003330 }
3331 else
cristy3ed852e2009-09-05 21:47:34 +00003332 {
3333 double
3334 factor;
3335
3336 /*
3337 Implode the pixel.
3338 */
3339 factor=1.0;
3340 if (distance > 0.0)
cristy1707c6c2012-01-18 23:30:54 +00003341 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/radius/
3342 2)),-amount);
cristy76f512e2011-09-12 01:26:56 +00003343 status=InterpolatePixelChannels(image,image_view,implode_image,method,
3344 (double) (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3345 scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00003346 }
cristy6d188022011-09-12 13:23:33 +00003347 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003348 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003349 }
3350 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3351 status=MagickFalse;
3352 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3353 {
3354 MagickBooleanType
3355 proceed;
3356
cristyb5d5f722009-11-04 03:03:49 +00003357#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003358 #pragma omp critical (MagickCore_ImplodeImage)
cristy3ed852e2009-09-05 21:47:34 +00003359#endif
3360 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3361 if (proceed == MagickFalse)
3362 status=MagickFalse;
3363 }
3364 }
3365 implode_view=DestroyCacheView(implode_view);
3366 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003367 if (status == MagickFalse)
3368 implode_image=DestroyImage(implode_image);
3369 return(implode_image);
3370}
3371
3372/*
3373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3374% %
3375% %
3376% %
3377% M o r p h I m a g e s %
3378% %
3379% %
3380% %
3381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3382%
3383% The MorphImages() method requires a minimum of two images. The first
3384% image is transformed into the second by a number of intervening images
3385% as specified by frames.
3386%
3387% The format of the MorphImage method is:
3388%
cristybb503372010-05-27 20:51:26 +00003389% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003390% ExceptionInfo *exception)
3391%
3392% A description of each parameter follows:
3393%
3394% o image: the image.
3395%
3396% o number_frames: Define the number of in-between image to generate.
3397% The more in-between frames, the smoother the morph.
3398%
3399% o exception: return any errors or warnings in this structure.
3400%
3401*/
3402MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003403 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003404{
3405#define MorphImageTag "Morph/Image"
3406
3407 Image
3408 *morph_image,
3409 *morph_images;
3410
cristy9d314ff2011-03-09 01:30:28 +00003411 MagickBooleanType
3412 status;
cristy3ed852e2009-09-05 21:47:34 +00003413
3414 MagickOffsetType
3415 scene;
3416
3417 MagickRealType
3418 alpha,
3419 beta;
3420
3421 register const Image
3422 *next;
3423
cristybb503372010-05-27 20:51:26 +00003424 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003425 i;
3426
cristy9d314ff2011-03-09 01:30:28 +00003427 ssize_t
3428 y;
cristy3ed852e2009-09-05 21:47:34 +00003429
3430 /*
3431 Clone first frame in sequence.
3432 */
3433 assert(image != (Image *) NULL);
3434 assert(image->signature == MagickSignature);
3435 if (image->debug != MagickFalse)
3436 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3437 assert(exception != (ExceptionInfo *) NULL);
3438 assert(exception->signature == MagickSignature);
3439 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3440 if (morph_images == (Image *) NULL)
3441 return((Image *) NULL);
3442 if (GetNextImageInList(image) == (Image *) NULL)
3443 {
3444 /*
3445 Morph single image.
3446 */
cristybb503372010-05-27 20:51:26 +00003447 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003448 {
3449 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3450 if (morph_image == (Image *) NULL)
3451 {
3452 morph_images=DestroyImageList(morph_images);
3453 return((Image *) NULL);
3454 }
3455 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003456 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003457 {
cristy8b27a6d2010-02-14 03:31:15 +00003458 MagickBooleanType
3459 proceed;
3460
cristybb503372010-05-27 20:51:26 +00003461 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3462 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003463 if (proceed == MagickFalse)
3464 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003465 }
3466 }
3467 return(GetFirstImageInList(morph_images));
3468 }
3469 /*
3470 Morph image sequence.
3471 */
3472 status=MagickTrue;
3473 scene=0;
3474 next=image;
3475 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3476 {
cristybb503372010-05-27 20:51:26 +00003477 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003478 {
3479 CacheView
3480 *image_view,
3481 *morph_view;
3482
3483 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3484 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003485 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristyaa2c16c2012-03-25 22:21:35 +00003486 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*next->rows+beta*
3487 GetNextImageInList(next)->rows+0.5),next->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00003488 if (morph_image == (Image *) NULL)
3489 {
3490 morph_images=DestroyImageList(morph_images);
3491 return((Image *) NULL);
3492 }
cristy1707c6c2012-01-18 23:30:54 +00003493 status=SetImageStorageClass(morph_image,DirectClass,exception);
3494 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003495 {
cristy3ed852e2009-09-05 21:47:34 +00003496 morph_image=DestroyImage(morph_image);
3497 return((Image *) NULL);
3498 }
3499 AppendImageToList(&morph_images,morph_image);
3500 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003501 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
cristyaa2c16c2012-03-25 22:21:35 +00003502 morph_images->rows,GetNextImageInList(next)->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00003503 if (morph_image == (Image *) NULL)
3504 {
3505 morph_images=DestroyImageList(morph_images);
3506 return((Image *) NULL);
3507 }
3508 image_view=AcquireCacheView(morph_image);
3509 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003510#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003511 #pragma omp parallel for schedule(static,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003512#endif
cristybb503372010-05-27 20:51:26 +00003513 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003514 {
3515 MagickBooleanType
3516 sync;
3517
cristy4c08aed2011-07-01 19:47:50 +00003518 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003519 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003520
cristybb503372010-05-27 20:51:26 +00003521 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003522 x;
3523
cristy4c08aed2011-07-01 19:47:50 +00003524 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003525 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003526
3527 if (status == MagickFalse)
3528 continue;
3529 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3530 exception);
3531 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3532 exception);
cristy4c08aed2011-07-01 19:47:50 +00003533 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003534 {
3535 status=MagickFalse;
3536 continue;
3537 }
cristybb503372010-05-27 20:51:26 +00003538 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003539 {
cristy1707c6c2012-01-18 23:30:54 +00003540 register ssize_t
3541 i;
3542
cristy10a6c612012-01-29 21:41:05 +00003543 for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
cristy1707c6c2012-01-18 23:30:54 +00003544 {
3545 PixelChannel
3546 channel;
3547
3548 PixelTrait
3549 morph_traits,
3550 traits;
3551
3552 channel=GetPixelChannelMapChannel(image,i);
3553 traits=GetPixelChannelMapTraits(image,channel);
3554 morph_traits=GetPixelChannelMapTraits(morph_image,channel);
3555 if ((traits == UndefinedPixelTrait) ||
3556 (morph_traits == UndefinedPixelTrait))
3557 continue;
cristyd09f8802012-02-04 16:44:10 +00003558 if (((morph_traits & CopyPixelTrait) != 0) ||
3559 (GetPixelMask(image,p) != 0))
cristy1707c6c2012-01-18 23:30:54 +00003560 {
3561 SetPixelChannel(morph_image,channel,p[i],q);
3562 continue;
3563 }
3564 SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3565 GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3566 }
cristyed231572011-07-14 02:18:59 +00003567 p+=GetPixelChannels(morph_image);
3568 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003569 }
3570 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3571 if (sync == MagickFalse)
3572 status=MagickFalse;
3573 }
3574 morph_view=DestroyCacheView(morph_view);
3575 image_view=DestroyCacheView(image_view);
3576 morph_image=DestroyImage(morph_image);
3577 }
cristybb503372010-05-27 20:51:26 +00003578 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003579 break;
3580 /*
3581 Clone last frame in sequence.
3582 */
3583 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3584 if (morph_image == (Image *) NULL)
3585 {
3586 morph_images=DestroyImageList(morph_images);
3587 return((Image *) NULL);
3588 }
3589 AppendImageToList(&morph_images,morph_image);
3590 morph_images=GetLastImageInList(morph_images);
3591 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3592 {
3593 MagickBooleanType
3594 proceed;
3595
cristyb5d5f722009-11-04 03:03:49 +00003596#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003597 #pragma omp critical (MagickCore_MorphImages)
cristy3ed852e2009-09-05 21:47:34 +00003598#endif
3599 proceed=SetImageProgress(image,MorphImageTag,scene,
3600 GetImageListLength(image));
3601 if (proceed == MagickFalse)
3602 status=MagickFalse;
3603 }
3604 scene++;
3605 }
3606 if (GetNextImageInList(next) != (Image *) NULL)
3607 {
3608 morph_images=DestroyImageList(morph_images);
3609 return((Image *) NULL);
3610 }
3611 return(GetFirstImageInList(morph_images));
3612}
3613
3614/*
3615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3616% %
3617% %
3618% %
3619% P l a s m a I m a g e %
3620% %
3621% %
3622% %
3623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3624%
3625% PlasmaImage() initializes an image with plasma fractal values. The image
3626% must be initialized with a base color and the random number generator
3627% seeded before this method is called.
3628%
3629% The format of the PlasmaImage method is:
3630%
3631% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristy5cbc0162011-08-29 00:36:28 +00003632% size_t attenuate,size_t depth,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003633%
3634% A description of each parameter follows:
3635%
3636% o image: the image.
3637%
3638% o segment: Define the region to apply plasma fractals values.
3639%
glennrp7dae1ca2010-09-16 12:17:35 +00003640% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003641%
3642% o depth: Limit the plasma recursion depth.
3643%
cristy5cbc0162011-08-29 00:36:28 +00003644% o exception: return any errors or warnings in this structure.
3645%
cristy3ed852e2009-09-05 21:47:34 +00003646*/
3647
3648static inline Quantum PlasmaPixel(RandomInfo *random_info,
3649 const MagickRealType pixel,const MagickRealType noise)
3650{
3651 Quantum
3652 plasma;
3653
cristyce70c172010-01-07 17:15:30 +00003654 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003655 noise/2.0);
3656 return(plasma);
3657}
3658
cristyda1f9c12011-10-02 21:39:49 +00003659static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
3660 CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3661 const SegmentInfo *segment,size_t attenuate,size_t depth,
3662 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003663{
cristy3ed852e2009-09-05 21:47:34 +00003664 MagickRealType
3665 plasma;
3666
cristyda1f9c12011-10-02 21:39:49 +00003667 PixelChannel
3668 channel;
3669
3670 PixelTrait
3671 traits;
3672
3673 register const Quantum
3674 *restrict u,
3675 *restrict v;
3676
3677 register Quantum
3678 *restrict q;
3679
3680 register ssize_t
3681 i;
cristy3ed852e2009-09-05 21:47:34 +00003682
cristy9d314ff2011-03-09 01:30:28 +00003683 ssize_t
3684 x,
3685 x_mid,
3686 y,
3687 y_mid;
3688
cristy3ed852e2009-09-05 21:47:34 +00003689 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3690 return(MagickTrue);
3691 if (depth != 0)
3692 {
3693 SegmentInfo
3694 local_info;
3695
3696 /*
3697 Divide the area into quadrants and recurse.
3698 */
3699 depth--;
3700 attenuate++;
cristybb503372010-05-27 20:51:26 +00003701 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3702 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003703 local_info=(*segment);
3704 local_info.x2=(double) x_mid;
3705 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003706 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3707 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003708 local_info=(*segment);
3709 local_info.y1=(double) y_mid;
3710 local_info.x2=(double) x_mid;
cristyda1f9c12011-10-02 21:39:49 +00003711 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3712 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003713 local_info=(*segment);
3714 local_info.x1=(double) x_mid;
3715 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003716 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3717 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003718 local_info=(*segment);
3719 local_info.x1=(double) x_mid;
3720 local_info.y1=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003721 return(PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3722 &local_info,attenuate,depth,exception));
cristy3ed852e2009-09-05 21:47:34 +00003723 }
cristybb503372010-05-27 20:51:26 +00003724 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3725 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003726 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3727 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3728 return(MagickFalse);
3729 /*
3730 Average pixels and apply plasma.
3731 */
cristy3ed852e2009-09-05 21:47:34 +00003732 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3733 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3734 {
cristy3ed852e2009-09-05 21:47:34 +00003735 /*
3736 Left pixel.
3737 */
cristybb503372010-05-27 20:51:26 +00003738 x=(ssize_t) ceil(segment->x1-0.5);
cristy1707c6c2012-01-18 23:30:54 +00003739 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3740 exception);
3741 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3742 exception);
cristyc5c6f662010-09-22 14:23:02 +00003743 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003744 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3745 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003746 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003747 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3748 {
cristye2a912b2011-12-05 20:02:07 +00003749 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003750 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003751 if (traits == UndefinedPixelTrait)
3752 continue;
3753 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3754 }
cristyc5c6f662010-09-22 14:23:02 +00003755 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003756 if (segment->x1 != segment->x2)
3757 {
3758 /*
3759 Right pixel.
3760 */
cristybb503372010-05-27 20:51:26 +00003761 x=(ssize_t) ceil(segment->x2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003762 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3763 1,1,exception);
3764 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3765 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003766 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003767 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3768 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003769 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003770 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3771 {
cristye2a912b2011-12-05 20:02:07 +00003772 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003773 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003774 if (traits == UndefinedPixelTrait)
3775 continue;
3776 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3777 }
cristyc5c6f662010-09-22 14:23:02 +00003778 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003779 }
3780 }
3781 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3782 {
3783 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3784 {
cristy3ed852e2009-09-05 21:47:34 +00003785 /*
3786 Bottom pixel.
3787 */
cristybb503372010-05-27 20:51:26 +00003788 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003789 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3790 1,1,exception);
3791 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3792 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003793 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003794 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3795 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003796 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003797 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3798 {
cristye2a912b2011-12-05 20:02:07 +00003799 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003800 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003801 if (traits == UndefinedPixelTrait)
3802 continue;
3803 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3804 }
cristyc5c6f662010-09-22 14:23:02 +00003805 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003806 }
3807 if (segment->y1 != segment->y2)
3808 {
cristy3ed852e2009-09-05 21:47:34 +00003809 /*
3810 Top pixel.
3811 */
cristybb503372010-05-27 20:51:26 +00003812 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003813 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3814 1,1,exception);
3815 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3816 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003817 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003818 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3819 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003820 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003821 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3822 {
cristye2a912b2011-12-05 20:02:07 +00003823 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003824 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003825 if (traits == UndefinedPixelTrait)
3826 continue;
3827 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3828 }
cristyc5c6f662010-09-22 14:23:02 +00003829 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003830 }
3831 }
3832 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3833 {
cristy3ed852e2009-09-05 21:47:34 +00003834 /*
3835 Middle pixel.
3836 */
cristybb503372010-05-27 20:51:26 +00003837 x=(ssize_t) ceil(segment->x1-0.5);
3838 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003839 u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
cristybb503372010-05-27 20:51:26 +00003840 x=(ssize_t) ceil(segment->x2-0.5);
3841 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003842 v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003843 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003844 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3845 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003846 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003847 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3848 {
cristye2a912b2011-12-05 20:02:07 +00003849 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003850 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003851 if (traits == UndefinedPixelTrait)
3852 continue;
3853 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3854 }
cristyc5c6f662010-09-22 14:23:02 +00003855 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003856 }
3857 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3858 return(MagickTrue);
3859 return(MagickFalse);
3860}
cristyda1f9c12011-10-02 21:39:49 +00003861
cristy3ed852e2009-09-05 21:47:34 +00003862MagickExport MagickBooleanType PlasmaImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00003863 const SegmentInfo *segment,size_t attenuate,size_t depth,
3864 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003865{
cristyc5c6f662010-09-22 14:23:02 +00003866 CacheView
cristyda1f9c12011-10-02 21:39:49 +00003867 *image_view,
3868 *u_view,
3869 *v_view;
cristyc5c6f662010-09-22 14:23:02 +00003870
cristy3ed852e2009-09-05 21:47:34 +00003871 MagickBooleanType
3872 status;
3873
3874 RandomInfo
3875 *random_info;
3876
3877 if (image->debug != MagickFalse)
3878 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3879 assert(image != (Image *) NULL);
3880 assert(image->signature == MagickSignature);
3881 if (image->debug != MagickFalse)
3882 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy5cbc0162011-08-29 00:36:28 +00003883 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristyc5c6f662010-09-22 14:23:02 +00003884 return(MagickFalse);
3885 image_view=AcquireCacheView(image);
cristyda1f9c12011-10-02 21:39:49 +00003886 u_view=AcquireCacheView(image);
3887 v_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003888 random_info=AcquireRandomInfo();
cristyda1f9c12011-10-02 21:39:49 +00003889 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
3890 attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003891 random_info=DestroyRandomInfo(random_info);
cristyda1f9c12011-10-02 21:39:49 +00003892 v_view=DestroyCacheView(v_view);
3893 u_view=DestroyCacheView(u_view);
cristyc5c6f662010-09-22 14:23:02 +00003894 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003895 return(status);
3896}
3897
3898/*
3899%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3900% %
3901% %
3902% %
3903% P o l a r o i d I m a g e %
3904% %
3905% %
3906% %
3907%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3908%
3909% PolaroidImage() simulates a Polaroid picture.
3910%
3911% The format of the AnnotateImage method is:
3912%
3913% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003914% const char *caption,const double angle,
3915% const PixelInterpolateMethod method,ExceptionInfo exception)
cristy3ed852e2009-09-05 21:47:34 +00003916%
3917% A description of each parameter follows:
3918%
3919% o image: the image.
3920%
3921% o draw_info: the draw info.
3922%
cristye9e3d382011-12-14 01:50:13 +00003923% o caption: the Polaroid caption.
3924%
cristycee97112010-05-28 00:44:52 +00003925% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003926%
cristy5c4e2582011-09-11 19:21:03 +00003927% o method: the pixel interpolation method.
3928%
cristy3ed852e2009-09-05 21:47:34 +00003929% o exception: return any errors or warnings in this structure.
3930%
3931*/
3932MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003933 const char *caption,const double angle,const PixelInterpolateMethod method,
cristy5c4e2582011-09-11 19:21:03 +00003934 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003935{
cristy3ed852e2009-09-05 21:47:34 +00003936 Image
3937 *bend_image,
3938 *caption_image,
3939 *flop_image,
3940 *picture_image,
3941 *polaroid_image,
3942 *rotate_image,
3943 *trim_image;
3944
cristybb503372010-05-27 20:51:26 +00003945 size_t
cristy3ed852e2009-09-05 21:47:34 +00003946 height;
3947
cristy9d314ff2011-03-09 01:30:28 +00003948 ssize_t
3949 quantum;
3950
cristy3ed852e2009-09-05 21:47:34 +00003951 /*
3952 Simulate a Polaroid picture.
3953 */
3954 assert(image != (Image *) NULL);
3955 assert(image->signature == MagickSignature);
3956 if (image->debug != MagickFalse)
3957 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3958 assert(exception != (ExceptionInfo *) NULL);
3959 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003960 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003961 image->rows)/25.0,10.0);
3962 height=image->rows+2*quantum;
3963 caption_image=(Image *) NULL;
cristye9e3d382011-12-14 01:50:13 +00003964 if (caption != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003965 {
3966 char
cristye9e3d382011-12-14 01:50:13 +00003967 geometry[MaxTextExtent],
3968 *text;
cristy3ed852e2009-09-05 21:47:34 +00003969
3970 DrawInfo
3971 *annotate_info;
3972
cristy3ed852e2009-09-05 21:47:34 +00003973 MagickBooleanType
3974 status;
3975
cristy9d314ff2011-03-09 01:30:28 +00003976 ssize_t
3977 count;
3978
cristy3ed852e2009-09-05 21:47:34 +00003979 TypeMetric
3980 metrics;
3981
3982 /*
3983 Generate caption image.
3984 */
3985 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3986 if (caption_image == (Image *) NULL)
3987 return((Image *) NULL);
3988 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
cristye9e3d382011-12-14 01:50:13 +00003989 text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
3990 exception);
3991 (void) CloneString(&annotate_info->text,text);
cristy6b1d05e2010-09-22 19:17:27 +00003992 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
cristye9e3d382011-12-14 01:50:13 +00003993 &text,exception);
3994 status=SetImageExtent(caption_image,image->columns,(size_t) ((count+1)*
3995 (metrics.ascent-metrics.descent)+0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00003996 if (status == MagickFalse)
3997 caption_image=DestroyImage(caption_image);
3998 else
3999 {
4000 caption_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004001 (void) SetImageBackgroundColor(caption_image,exception);
cristye9e3d382011-12-14 01:50:13 +00004002 (void) CloneString(&annotate_info->text,text);
cristyb51dff52011-05-19 16:55:47 +00004003 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00004004 metrics.ascent);
4005 if (annotate_info->gravity == UndefinedGravity)
4006 (void) CloneString(&annotate_info->geometry,AcquireString(
4007 geometry));
cristy5cbc0162011-08-29 00:36:28 +00004008 (void) AnnotateImage(caption_image,annotate_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004009 height+=caption_image->rows;
4010 }
4011 annotate_info=DestroyDrawInfo(annotate_info);
cristye9e3d382011-12-14 01:50:13 +00004012 text=DestroyString(text);
cristy3ed852e2009-09-05 21:47:34 +00004013 }
4014 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4015 exception);
4016 if (picture_image == (Image *) NULL)
4017 {
4018 if (caption_image != (Image *) NULL)
4019 caption_image=DestroyImage(caption_image);
4020 return((Image *) NULL);
4021 }
4022 picture_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004023 (void) SetImageBackgroundColor(picture_image,exception);
cristy39172402012-03-30 13:04:39 +00004024 (void) CompositeImage(picture_image,image,OverCompositeOp,MagickTrue,quantum,
cristyfeb3e962012-03-29 17:25:55 +00004025 quantum,exception);
cristy3ed852e2009-09-05 21:47:34 +00004026 if (caption_image != (Image *) NULL)
4027 {
cristyfeb3e962012-03-29 17:25:55 +00004028 (void) CompositeImage(picture_image,caption_image,OverCompositeOp,
cristy39172402012-03-30 13:04:39 +00004029 MagickTrue,quantum,(ssize_t) (image->rows+3*quantum/2),exception);
cristy3ed852e2009-09-05 21:47:34 +00004030 caption_image=DestroyImage(caption_image);
4031 }
cristy9950d572011-10-01 18:22:35 +00004032 (void) QueryColorCompliance("none",AllCompliance,
4033 &picture_image->background_color,exception);
cristy63240882011-08-05 19:05:27 +00004034 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004035 rotate_image=RotateImage(picture_image,90.0,exception);
4036 picture_image=DestroyImage(picture_image);
4037 if (rotate_image == (Image *) NULL)
4038 return((Image *) NULL);
4039 picture_image=rotate_image;
4040 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
cristy5c4e2582011-09-11 19:21:03 +00004041 picture_image->columns,method,exception);
cristy3ed852e2009-09-05 21:47:34 +00004042 picture_image=DestroyImage(picture_image);
4043 if (bend_image == (Image *) NULL)
4044 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004045 picture_image=bend_image;
4046 rotate_image=RotateImage(picture_image,-90.0,exception);
4047 picture_image=DestroyImage(picture_image);
4048 if (rotate_image == (Image *) NULL)
4049 return((Image *) NULL);
4050 picture_image=rotate_image;
4051 picture_image->background_color=image->background_color;
anthonyf46d4262012-03-26 03:30:34 +00004052 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
cristy3ed852e2009-09-05 21:47:34 +00004053 exception);
4054 if (polaroid_image == (Image *) NULL)
4055 {
4056 picture_image=DestroyImage(picture_image);
4057 return(picture_image);
4058 }
4059 flop_image=FlopImage(polaroid_image,exception);
4060 polaroid_image=DestroyImage(polaroid_image);
4061 if (flop_image == (Image *) NULL)
4062 {
4063 picture_image=DestroyImage(picture_image);
4064 return(picture_image);
4065 }
4066 polaroid_image=flop_image;
cristyfeb3e962012-03-29 17:25:55 +00004067 (void) CompositeImage(polaroid_image,picture_image,OverCompositeOp,
cristy39172402012-03-30 13:04:39 +00004068 MagickTrue,(ssize_t) (-0.01*picture_image->columns/2.0),0L,exception);
cristy3ed852e2009-09-05 21:47:34 +00004069 picture_image=DestroyImage(picture_image);
cristy9950d572011-10-01 18:22:35 +00004070 (void) QueryColorCompliance("none",AllCompliance,
4071 &polaroid_image->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00004072 rotate_image=RotateImage(polaroid_image,angle,exception);
4073 polaroid_image=DestroyImage(polaroid_image);
4074 if (rotate_image == (Image *) NULL)
4075 return((Image *) NULL);
4076 polaroid_image=rotate_image;
4077 trim_image=TrimImage(polaroid_image,exception);
4078 polaroid_image=DestroyImage(polaroid_image);
4079 if (trim_image == (Image *) NULL)
4080 return((Image *) NULL);
4081 polaroid_image=trim_image;
4082 return(polaroid_image);
4083}
4084
4085/*
4086%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4087% %
4088% %
4089% %
cristy3ed852e2009-09-05 21:47:34 +00004090% S e p i a T o n e I m a g e %
4091% %
4092% %
4093% %
4094%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4095%
4096% MagickSepiaToneImage() applies a special effect to the image, similar to the
4097% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4098% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4099% threshold of 80% is a good starting point for a reasonable tone.
4100%
4101% The format of the SepiaToneImage method is:
4102%
4103% Image *SepiaToneImage(const Image *image,const double threshold,
4104% ExceptionInfo *exception)
4105%
4106% A description of each parameter follows:
4107%
4108% o image: the image.
4109%
4110% o threshold: the tone threshold.
4111%
4112% o exception: return any errors or warnings in this structure.
4113%
4114*/
4115MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4116 ExceptionInfo *exception)
4117{
4118#define SepiaToneImageTag "SepiaTone/Image"
4119
cristyc4c8d132010-01-07 01:58:38 +00004120 CacheView
4121 *image_view,
4122 *sepia_view;
4123
cristy3ed852e2009-09-05 21:47:34 +00004124 Image
4125 *sepia_image;
4126
cristy3ed852e2009-09-05 21:47:34 +00004127 MagickBooleanType
4128 status;
4129
cristybb503372010-05-27 20:51:26 +00004130 MagickOffsetType
4131 progress;
4132
4133 ssize_t
4134 y;
4135
cristy3ed852e2009-09-05 21:47:34 +00004136 /*
4137 Initialize sepia-toned image attributes.
4138 */
4139 assert(image != (const Image *) NULL);
4140 assert(image->signature == MagickSignature);
4141 if (image->debug != MagickFalse)
4142 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4143 assert(exception != (ExceptionInfo *) NULL);
4144 assert(exception->signature == MagickSignature);
cristy1707c6c2012-01-18 23:30:54 +00004145 sepia_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004146 if (sepia_image == (Image *) NULL)
4147 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004148 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004149 {
cristy3ed852e2009-09-05 21:47:34 +00004150 sepia_image=DestroyImage(sepia_image);
4151 return((Image *) NULL);
4152 }
4153 /*
4154 Tone each row of the image.
4155 */
4156 status=MagickTrue;
4157 progress=0;
4158 image_view=AcquireCacheView(image);
4159 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004160#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004161 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004162#endif
cristybb503372010-05-27 20:51:26 +00004163 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004164 {
cristy4c08aed2011-07-01 19:47:50 +00004165 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004166 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004167
cristybb503372010-05-27 20:51:26 +00004168 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004169 x;
4170
cristy4c08aed2011-07-01 19:47:50 +00004171 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004172 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004173
4174 if (status == MagickFalse)
4175 continue;
4176 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00004177 q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004178 exception);
cristy4c08aed2011-07-01 19:47:50 +00004179 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004180 {
4181 status=MagickFalse;
4182 continue;
4183 }
cristybb503372010-05-27 20:51:26 +00004184 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004185 {
4186 MagickRealType
4187 intensity,
4188 tone;
4189
cristy4c08aed2011-07-01 19:47:50 +00004190 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004191 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4192 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004193 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004194 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4195 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004196 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004197 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004198 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004199 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004200 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4201 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4202 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4203 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004204 p+=GetPixelChannels(image);
4205 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004206 }
4207 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4208 status=MagickFalse;
4209 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4210 {
4211 MagickBooleanType
4212 proceed;
4213
cristyb5d5f722009-11-04 03:03:49 +00004214#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004215 #pragma omp critical (MagickCore_SepiaToneImage)
cristy3ed852e2009-09-05 21:47:34 +00004216#endif
4217 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4218 image->rows);
4219 if (proceed == MagickFalse)
4220 status=MagickFalse;
4221 }
4222 }
4223 sepia_view=DestroyCacheView(sepia_view);
4224 image_view=DestroyCacheView(image_view);
cristye23ec9d2011-08-16 18:15:40 +00004225 (void) NormalizeImage(sepia_image,exception);
4226 (void) ContrastImage(sepia_image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004227 if (status == MagickFalse)
4228 sepia_image=DestroyImage(sepia_image);
4229 return(sepia_image);
4230}
4231
4232/*
4233%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4234% %
4235% %
4236% %
4237% S h a d o w I m a g e %
4238% %
4239% %
4240% %
4241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4242%
4243% ShadowImage() simulates a shadow from the specified image and returns it.
4244%
4245% The format of the ShadowImage method is:
4246%
cristy70cddf72011-12-10 22:42:42 +00004247% Image *ShadowImage(const Image *image,const double alpha,
cristyaa2c16c2012-03-25 22:21:35 +00004248% const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4249% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004250%
4251% A description of each parameter follows:
4252%
4253% o image: the image.
4254%
cristy70cddf72011-12-10 22:42:42 +00004255% o alpha: percentage transparency.
cristy3ed852e2009-09-05 21:47:34 +00004256%
4257% o sigma: the standard deviation of the Gaussian, in pixels.
4258%
4259% o x_offset: the shadow x-offset.
4260%
4261% o y_offset: the shadow y-offset.
4262%
4263% o exception: return any errors or warnings in this structure.
4264%
4265*/
cristy70cddf72011-12-10 22:42:42 +00004266MagickExport Image *ShadowImage(const Image *image,const double alpha,
cristyaa2c16c2012-03-25 22:21:35 +00004267 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4268 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004269{
4270#define ShadowImageTag "Shadow/Image"
4271
cristy70cddf72011-12-10 22:42:42 +00004272 CacheView
4273 *image_view;
4274
cristybd5a96c2011-08-21 00:04:26 +00004275 ChannelType
4276 channel_mask;
4277
cristy3ed852e2009-09-05 21:47:34 +00004278 Image
4279 *border_image,
4280 *clone_image,
4281 *shadow_image;
4282
cristy70cddf72011-12-10 22:42:42 +00004283 MagickBooleanType
4284 status;
4285
cristy3ed852e2009-09-05 21:47:34 +00004286 RectangleInfo
4287 border_info;
4288
cristy70cddf72011-12-10 22:42:42 +00004289 ssize_t
4290 y;
4291
cristy3ed852e2009-09-05 21:47:34 +00004292 assert(image != (Image *) NULL);
4293 assert(image->signature == MagickSignature);
4294 if (image->debug != MagickFalse)
4295 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4296 assert(exception != (ExceptionInfo *) NULL);
4297 assert(exception->signature == MagickSignature);
4298 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4299 if (clone_image == (Image *) NULL)
4300 return((Image *) NULL);
cristy387430f2012-02-07 13:09:46 +00004301 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod,
4302 exception);
cristybb503372010-05-27 20:51:26 +00004303 border_info.width=(size_t) floor(2.0*sigma+0.5);
4304 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004305 border_info.x=0;
4306 border_info.y=0;
cristy9950d572011-10-01 18:22:35 +00004307 (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4308 exception);
cristy70cddf72011-12-10 22:42:42 +00004309 clone_image->matte=MagickTrue;
4310 border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
cristy3ed852e2009-09-05 21:47:34 +00004311 clone_image=DestroyImage(clone_image);
4312 if (border_image == (Image *) NULL)
4313 return((Image *) NULL);
4314 if (border_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004315 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004316 /*
4317 Shadow image.
4318 */
cristy70cddf72011-12-10 22:42:42 +00004319 status=MagickTrue;
4320 image_view=AcquireCacheView(border_image);
4321 for (y=0; y < (ssize_t) border_image->rows; y++)
4322 {
4323 PixelInfo
4324 background_color;
4325
4326 register Quantum
4327 *restrict q;
4328
4329 register ssize_t
4330 x;
4331
4332 if (status == MagickFalse)
4333 continue;
4334 q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4335 exception);
4336 if (q == (Quantum *) NULL)
4337 {
4338 status=MagickFalse;
4339 continue;
4340 }
4341 background_color=border_image->background_color;
4342 background_color.matte=MagickTrue;
4343 for (x=0; x < (ssize_t) border_image->columns; x++)
4344 {
4345 if (border_image->matte != MagickFalse)
4346 background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4347 SetPixelInfoPixel(border_image,&background_color,q);
4348 q+=GetPixelChannels(border_image);
4349 }
4350 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4351 status=MagickFalse;
4352 }
4353 image_view=DestroyCacheView(image_view);
4354 if (status == MagickFalse)
4355 {
4356 border_image=DestroyImage(border_image);
4357 return((Image *) NULL);
4358 }
cristybd5a96c2011-08-21 00:04:26 +00004359 channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
cristyaa2c16c2012-03-25 22:21:35 +00004360 shadow_image=BlurImage(border_image,0.0,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00004361 border_image=DestroyImage(border_image);
4362 if (shadow_image == (Image *) NULL)
4363 return((Image *) NULL);
cristyae1969f2011-12-10 03:07:36 +00004364 (void) SetPixelChannelMapMask(shadow_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004365 if (shadow_image->page.width == 0)
4366 shadow_image->page.width=shadow_image->columns;
4367 if (shadow_image->page.height == 0)
4368 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004369 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4370 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4371 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4372 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004373 return(shadow_image);
4374}
4375
4376/*
4377%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4378% %
4379% %
4380% %
4381% S k e t c h I m a g e %
4382% %
4383% %
4384% %
4385%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4386%
4387% SketchImage() simulates a pencil sketch. We convolve the image with a
4388% Gaussian operator of the given radius and standard deviation (sigma). For
4389% reasonable results, radius should be larger than sigma. Use a radius of 0
4390% and SketchImage() selects a suitable radius for you. Angle gives the angle
4391% of the sketch.
4392%
4393% The format of the SketchImage method is:
4394%
4395% Image *SketchImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00004396% const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004397%
4398% A description of each parameter follows:
4399%
4400% o image: the image.
4401%
cristy574cc262011-08-05 01:23:58 +00004402% o radius: the radius of the Gaussian, in pixels, not counting the
4403% center pixel.
cristy3ed852e2009-09-05 21:47:34 +00004404%
4405% o sigma: the standard deviation of the Gaussian, in pixels.
4406%
cristyf7ef0252011-09-09 14:50:06 +00004407% o angle: apply the effect along this angle.
4408%
cristy3ed852e2009-09-05 21:47:34 +00004409% o exception: return any errors or warnings in this structure.
4410%
4411*/
4412MagickExport Image *SketchImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00004413 const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004414{
cristyfa112112010-01-04 17:48:07 +00004415 CacheView
4416 *random_view;
4417
cristy3ed852e2009-09-05 21:47:34 +00004418 Image
4419 *blend_image,
4420 *blur_image,
4421 *dodge_image,
4422 *random_image,
4423 *sketch_image;
4424
cristy3ed852e2009-09-05 21:47:34 +00004425 MagickBooleanType
4426 status;
4427
cristy3ed852e2009-09-05 21:47:34 +00004428 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004429 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004430
cristy9d314ff2011-03-09 01:30:28 +00004431 ssize_t
4432 y;
4433
cristy3ed852e2009-09-05 21:47:34 +00004434 /*
4435 Sketch image.
4436 */
4437 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4438 MagickTrue,exception);
4439 if (random_image == (Image *) NULL)
4440 return((Image *) NULL);
4441 status=MagickTrue;
cristy1b784432009-12-19 02:20:40 +00004442 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004443 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004444#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004445 #pragma omp parallel for schedule(static,4) shared(status)
cristy1b784432009-12-19 02:20:40 +00004446#endif
cristybb503372010-05-27 20:51:26 +00004447 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004448 {
cristy5c9e6f22010-09-17 17:31:01 +00004449 const int
4450 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004451
cristybb503372010-05-27 20:51:26 +00004452 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004453 x;
4454
cristy4c08aed2011-07-01 19:47:50 +00004455 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004456 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004457
cristy1b784432009-12-19 02:20:40 +00004458 if (status == MagickFalse)
4459 continue;
cristy3ed852e2009-09-05 21:47:34 +00004460 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4461 exception);
cristyacd2ed22011-08-30 01:44:23 +00004462 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004463 {
4464 status=MagickFalse;
4465 continue;
4466 }
cristybb503372010-05-27 20:51:26 +00004467 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004468 {
cristy76f512e2011-09-12 01:26:56 +00004469 MagickRealType
4470 value;
4471
4472 register ssize_t
4473 i;
4474
cristy10a6c612012-01-29 21:41:05 +00004475 if (GetPixelMask(random_image,q) != 0)
4476 {
4477 q+=GetPixelChannels(random_image);
4478 continue;
4479 }
cristy76f512e2011-09-12 01:26:56 +00004480 value=GetPseudoRandomValue(random_info[id]);
4481 for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4482 {
cristyabace412011-12-11 15:56:53 +00004483 PixelChannel
4484 channel;
4485
cristy76f512e2011-09-12 01:26:56 +00004486 PixelTrait
4487 traits;
4488
cristyabace412011-12-11 15:56:53 +00004489 channel=GetPixelChannelMapChannel(image,i);
4490 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004491 if (traits == UndefinedPixelTrait)
4492 continue;
4493 q[i]=ClampToQuantum(QuantumRange*value);
4494 }
cristyed231572011-07-14 02:18:59 +00004495 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004496 }
4497 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4498 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004499 }
4500 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004501 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004502 if (status == MagickFalse)
4503 {
4504 random_image=DestroyImage(random_image);
4505 return(random_image);
4506 }
cristyaa2c16c2012-03-25 22:21:35 +00004507 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
cristy3ed852e2009-09-05 21:47:34 +00004508 random_image=DestroyImage(random_image);
4509 if (blur_image == (Image *) NULL)
4510 return((Image *) NULL);
cristy6bfd6902011-12-09 01:33:45 +00004511 dodge_image=EdgeImage(blur_image,radius,1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004512 blur_image=DestroyImage(blur_image);
4513 if (dodge_image == (Image *) NULL)
4514 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +00004515 (void) NormalizeImage(dodge_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00004516 (void) NegateImage(dodge_image,MagickFalse,exception);
cristye941a752011-10-15 01:52:48 +00004517 (void) TransformImage(&dodge_image,(char *) NULL,"50%",exception);
cristy3ed852e2009-09-05 21:47:34 +00004518 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4519 if (sketch_image == (Image *) NULL)
4520 {
4521 dodge_image=DestroyImage(dodge_image);
4522 return((Image *) NULL);
4523 }
cristyfeb3e962012-03-29 17:25:55 +00004524 (void) CompositeImage(sketch_image,dodge_image,ColorDodgeCompositeOp,
cristy39172402012-03-30 13:04:39 +00004525 MagickTrue,0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004526 dodge_image=DestroyImage(dodge_image);
4527 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4528 if (blend_image == (Image *) NULL)
4529 {
4530 sketch_image=DestroyImage(sketch_image);
4531 return((Image *) NULL);
4532 }
4533 (void) SetImageArtifact(blend_image,"compose:args","20x80");
cristy39172402012-03-30 13:04:39 +00004534 (void) CompositeImage(sketch_image,blend_image,BlendCompositeOp,MagickTrue,
cristyfeb3e962012-03-29 17:25:55 +00004535 0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004536 blend_image=DestroyImage(blend_image);
4537 return(sketch_image);
4538}
4539
4540/*
4541%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4542% %
4543% %
4544% %
4545% S o l a r i z e I m a g e %
4546% %
4547% %
4548% %
4549%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4550%
4551% SolarizeImage() applies a special effect to the image, similar to the effect
4552% achieved in a photo darkroom by selectively exposing areas of photo
4553% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4554% measure of the extent of the solarization.
4555%
4556% The format of the SolarizeImage method is:
4557%
cristy5cbc0162011-08-29 00:36:28 +00004558% MagickBooleanType SolarizeImage(Image *image,const double threshold,
4559% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004560%
4561% A description of each parameter follows:
4562%
4563% o image: the image.
4564%
4565% o threshold: Define the extent of the solarization.
4566%
cristy5cbc0162011-08-29 00:36:28 +00004567% o exception: return any errors or warnings in this structure.
4568%
cristy3ed852e2009-09-05 21:47:34 +00004569*/
4570MagickExport MagickBooleanType SolarizeImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00004571 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004572{
4573#define SolarizeImageTag "Solarize/Image"
4574
cristyc4c8d132010-01-07 01:58:38 +00004575 CacheView
4576 *image_view;
4577
cristy3ed852e2009-09-05 21:47:34 +00004578 MagickBooleanType
4579 status;
4580
cristybb503372010-05-27 20:51:26 +00004581 MagickOffsetType
4582 progress;
4583
4584 ssize_t
4585 y;
4586
cristy3ed852e2009-09-05 21:47:34 +00004587 assert(image != (Image *) NULL);
4588 assert(image->signature == MagickSignature);
4589 if (image->debug != MagickFalse)
4590 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4591 if (image->storage_class == PseudoClass)
4592 {
cristybb503372010-05-27 20:51:26 +00004593 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004594 i;
4595
4596 /*
4597 Solarize colormap.
4598 */
cristybb503372010-05-27 20:51:26 +00004599 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004600 {
4601 if ((MagickRealType) image->colormap[i].red > threshold)
4602 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4603 if ((MagickRealType) image->colormap[i].green > threshold)
4604 image->colormap[i].green=(Quantum) QuantumRange-
4605 image->colormap[i].green;
4606 if ((MagickRealType) image->colormap[i].blue > threshold)
4607 image->colormap[i].blue=(Quantum) QuantumRange-
4608 image->colormap[i].blue;
4609 }
4610 }
4611 /*
4612 Solarize image.
4613 */
4614 status=MagickTrue;
4615 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00004616 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004617#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004618 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004619#endif
cristybb503372010-05-27 20:51:26 +00004620 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004621 {
cristybb503372010-05-27 20:51:26 +00004622 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004623 x;
4624
cristy4c08aed2011-07-01 19:47:50 +00004625 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004626 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004627
4628 if (status == MagickFalse)
4629 continue;
cristy5cbc0162011-08-29 00:36:28 +00004630 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00004631 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004632 {
4633 status=MagickFalse;
4634 continue;
4635 }
cristybb503372010-05-27 20:51:26 +00004636 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004637 {
cristy76f512e2011-09-12 01:26:56 +00004638 register ssize_t
4639 i;
4640
cristy10a6c612012-01-29 21:41:05 +00004641 if (GetPixelMask(image,q) != 0)
4642 {
4643 q+=GetPixelChannels(image);
4644 continue;
4645 }
cristy76f512e2011-09-12 01:26:56 +00004646 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4647 {
cristyabace412011-12-11 15:56:53 +00004648 PixelChannel
4649 channel;
4650
cristy76f512e2011-09-12 01:26:56 +00004651 PixelTrait
4652 traits;
4653
cristyabace412011-12-11 15:56:53 +00004654 channel=GetPixelChannelMapChannel(image,i);
4655 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004656 if ((traits == UndefinedPixelTrait) ||
4657 ((traits & CopyPixelTrait) != 0))
4658 continue;
4659 if ((MagickRealType) q[i] > threshold)
4660 q[i]=QuantumRange-q[i];
4661 }
cristyed231572011-07-14 02:18:59 +00004662 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004663 }
4664 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4665 status=MagickFalse;
4666 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4667 {
4668 MagickBooleanType
4669 proceed;
4670
cristyb5d5f722009-11-04 03:03:49 +00004671#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004672 #pragma omp critical (MagickCore_SolarizeImage)
cristy3ed852e2009-09-05 21:47:34 +00004673#endif
4674 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4675 if (proceed == MagickFalse)
4676 status=MagickFalse;
4677 }
4678 }
4679 image_view=DestroyCacheView(image_view);
4680 return(status);
4681}
4682
4683/*
4684%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4685% %
4686% %
4687% %
4688% S t e g a n o I m a g e %
4689% %
4690% %
4691% %
4692%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4693%
4694% SteganoImage() hides a digital watermark within the image. Recover
4695% the hidden watermark later to prove that the authenticity of an image.
4696% Offset defines the start position within the image to hide the watermark.
4697%
4698% The format of the SteganoImage method is:
4699%
4700% Image *SteganoImage(const Image *image,Image *watermark,
4701% ExceptionInfo *exception)
4702%
4703% A description of each parameter follows:
4704%
4705% o image: the image.
4706%
4707% o watermark: the watermark image.
4708%
4709% o exception: return any errors or warnings in this structure.
4710%
4711*/
4712MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4713 ExceptionInfo *exception)
4714{
cristye1bf8ad2010-09-19 17:07:03 +00004715#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004716#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004717 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004718#define SteganoImageTag "Stegano/Image"
4719
cristyb0d3bb92010-09-22 14:37:58 +00004720 CacheView
4721 *stegano_view,
4722 *watermark_view;
4723
cristy3ed852e2009-09-05 21:47:34 +00004724 Image
4725 *stegano_image;
4726
4727 int
4728 c;
4729
cristy3ed852e2009-09-05 21:47:34 +00004730 MagickBooleanType
4731 status;
4732
cristy101ab702011-10-13 13:06:32 +00004733 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004734 pixel;
4735
cristy4c08aed2011-07-01 19:47:50 +00004736 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004737 *q;
4738
cristye1bf8ad2010-09-19 17:07:03 +00004739 register ssize_t
4740 x;
4741
cristybb503372010-05-27 20:51:26 +00004742 size_t
cristyeaedf062010-05-29 22:36:02 +00004743 depth,
4744 one;
cristy3ed852e2009-09-05 21:47:34 +00004745
cristye1bf8ad2010-09-19 17:07:03 +00004746 ssize_t
4747 i,
4748 j,
4749 k,
4750 y;
4751
cristy3ed852e2009-09-05 21:47:34 +00004752 /*
4753 Initialize steganographic image attributes.
4754 */
4755 assert(image != (const Image *) NULL);
4756 assert(image->signature == MagickSignature);
4757 if (image->debug != MagickFalse)
4758 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4759 assert(watermark != (const Image *) NULL);
4760 assert(watermark->signature == MagickSignature);
4761 assert(exception != (ExceptionInfo *) NULL);
4762 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004763 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004764 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4765 if (stegano_image == (Image *) NULL)
4766 return((Image *) NULL);
cristyf61b1832012-04-01 01:38:19 +00004767 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
cristy574cc262011-08-05 01:23:58 +00004768 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004769 {
cristy3ed852e2009-09-05 21:47:34 +00004770 stegano_image=DestroyImage(stegano_image);
4771 return((Image *) NULL);
4772 }
cristy3ed852e2009-09-05 21:47:34 +00004773 /*
4774 Hide watermark in low-order bits of image.
4775 */
4776 c=0;
4777 i=0;
4778 j=0;
4779 depth=stegano_image->depth;
cristyf61b1832012-04-01 01:38:19 +00004780 k=stegano_image->offset;
cristyda16f162011-02-19 23:52:17 +00004781 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004782 watermark_view=AcquireCacheView(watermark);
4783 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004784 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004785 {
cristybb503372010-05-27 20:51:26 +00004786 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004787 {
cristybb503372010-05-27 20:51:26 +00004788 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004789 {
cristy1707c6c2012-01-18 23:30:54 +00004790 ssize_t
4791 offset;
4792
cristyf05d4942012-03-17 16:26:09 +00004793 (void) GetOneCacheViewVirtualPixelInfo(watermark_view,x,y,&pixel,
cristyda1f9c12011-10-02 21:39:49 +00004794 exception);
cristy1707c6c2012-01-18 23:30:54 +00004795 offset=k/(ssize_t) stegano_image->columns;
4796 if (offset >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004797 break;
cristyb0d3bb92010-09-22 14:37:58 +00004798 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4799 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4800 exception);
cristyacd2ed22011-08-30 01:44:23 +00004801 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004802 break;
4803 switch (c)
4804 {
4805 case 0:
4806 {
cristyf61b1832012-04-01 01:38:19 +00004807 SetPixelRed(stegano_image,SetBit(GetPixelRed(stegano_image,q),j,
4808 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004809 break;
4810 }
4811 case 1:
4812 {
cristyf61b1832012-04-01 01:38:19 +00004813 SetPixelGreen(stegano_image,SetBit(GetPixelGreen(stegano_image,q),j,
4814 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004815 break;
4816 }
4817 case 2:
4818 {
cristyf61b1832012-04-01 01:38:19 +00004819 SetPixelBlue(stegano_image,SetBit(GetPixelBlue(stegano_image,q),j,
4820 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004821 break;
4822 }
4823 }
cristyb0d3bb92010-09-22 14:37:58 +00004824 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004825 break;
4826 c++;
4827 if (c == 3)
4828 c=0;
4829 k++;
cristybb503372010-05-27 20:51:26 +00004830 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004831 k=0;
cristyf61b1832012-04-01 01:38:19 +00004832 if (k == stegano_image->offset)
cristy3ed852e2009-09-05 21:47:34 +00004833 j++;
4834 }
4835 }
cristy8b27a6d2010-02-14 03:31:15 +00004836 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004837 {
cristy8b27a6d2010-02-14 03:31:15 +00004838 MagickBooleanType
4839 proceed;
4840
4841 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4842 (depth-i),depth);
4843 if (proceed == MagickFalse)
4844 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004845 }
4846 }
cristyb0d3bb92010-09-22 14:37:58 +00004847 stegano_view=DestroyCacheView(stegano_view);
4848 watermark_view=DestroyCacheView(watermark_view);
cristyda16f162011-02-19 23:52:17 +00004849 if (status == MagickFalse)
4850 {
4851 stegano_image=DestroyImage(stegano_image);
4852 return((Image *) NULL);
4853 }
cristy3ed852e2009-09-05 21:47:34 +00004854 return(stegano_image);
4855}
4856
4857/*
4858%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4859% %
4860% %
4861% %
4862% S t e r e o A n a g l y p h I m a g e %
4863% %
4864% %
4865% %
4866%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4867%
4868% StereoAnaglyphImage() combines two images and produces a single image that
4869% is the composite of a left and right image of a stereo pair. Special
4870% red-green stereo glasses are required to view this effect.
4871%
4872% The format of the StereoAnaglyphImage method is:
4873%
4874% Image *StereoImage(const Image *left_image,const Image *right_image,
4875% ExceptionInfo *exception)
4876% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004877% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004878% ExceptionInfo *exception)
4879%
4880% A description of each parameter follows:
4881%
4882% o left_image: the left image.
4883%
4884% o right_image: the right image.
4885%
4886% o exception: return any errors or warnings in this structure.
4887%
4888% o x_offset: amount, in pixels, by which the left image is offset to the
4889% right of the right image.
4890%
4891% o y_offset: amount, in pixels, by which the left image is offset to the
4892% bottom of the right image.
4893%
4894%
4895*/
4896MagickExport Image *StereoImage(const Image *left_image,
4897 const Image *right_image,ExceptionInfo *exception)
4898{
4899 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4900}
4901
4902MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004903 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004904 ExceptionInfo *exception)
4905{
4906#define StereoImageTag "Stereo/Image"
4907
4908 const Image
4909 *image;
4910
4911 Image
4912 *stereo_image;
4913
cristy3ed852e2009-09-05 21:47:34 +00004914 MagickBooleanType
4915 status;
4916
cristy9d314ff2011-03-09 01:30:28 +00004917 ssize_t
4918 y;
4919
cristy3ed852e2009-09-05 21:47:34 +00004920 assert(left_image != (const Image *) NULL);
4921 assert(left_image->signature == MagickSignature);
4922 if (left_image->debug != MagickFalse)
4923 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4924 left_image->filename);
4925 assert(right_image != (const Image *) NULL);
4926 assert(right_image->signature == MagickSignature);
4927 assert(exception != (ExceptionInfo *) NULL);
4928 assert(exception->signature == MagickSignature);
4929 assert(right_image != (const Image *) NULL);
4930 image=left_image;
4931 if ((left_image->columns != right_image->columns) ||
4932 (left_image->rows != right_image->rows))
4933 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4934 /*
4935 Initialize stereo image attributes.
4936 */
4937 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4938 MagickTrue,exception);
4939 if (stereo_image == (Image *) NULL)
4940 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004941 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004942 {
cristy3ed852e2009-09-05 21:47:34 +00004943 stereo_image=DestroyImage(stereo_image);
4944 return((Image *) NULL);
4945 }
4946 /*
4947 Copy left image to red channel and right image to blue channel.
4948 */
cristyda16f162011-02-19 23:52:17 +00004949 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004950 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004951 {
cristy4c08aed2011-07-01 19:47:50 +00004952 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004953 *restrict p,
4954 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004955
cristybb503372010-05-27 20:51:26 +00004956 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004957 x;
4958
cristy4c08aed2011-07-01 19:47:50 +00004959 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004960 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004961
4962 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4963 exception);
4964 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4965 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy76f512e2011-09-12 01:26:56 +00004966 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
4967 (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004968 break;
cristybb503372010-05-27 20:51:26 +00004969 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004970 {
cristy4c08aed2011-07-01 19:47:50 +00004971 SetPixelRed(image,GetPixelRed(left_image,p),r);
cristy76f512e2011-09-12 01:26:56 +00004972 SetPixelGreen(image,GetPixelGreen(right_image,q),r);
4973 SetPixelBlue(image,GetPixelBlue(right_image,q),r);
4974 if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
4975 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4976 GetPixelAlpha(right_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00004977 p+=GetPixelChannels(left_image);
cristy76f512e2011-09-12 01:26:56 +00004978 q+=GetPixelChannels(right_image);
4979 r+=GetPixelChannels(stereo_image);
cristy3ed852e2009-09-05 21:47:34 +00004980 }
4981 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4982 break;
cristy8b27a6d2010-02-14 03:31:15 +00004983 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004984 {
cristy8b27a6d2010-02-14 03:31:15 +00004985 MagickBooleanType
4986 proceed;
4987
cristybb503372010-05-27 20:51:26 +00004988 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4989 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004990 if (proceed == MagickFalse)
4991 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004992 }
4993 }
cristyda16f162011-02-19 23:52:17 +00004994 if (status == MagickFalse)
4995 {
4996 stereo_image=DestroyImage(stereo_image);
4997 return((Image *) NULL);
4998 }
cristy3ed852e2009-09-05 21:47:34 +00004999 return(stereo_image);
5000}
5001
5002/*
5003%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5004% %
5005% %
5006% %
5007% S w i r l I m a g e %
5008% %
5009% %
5010% %
5011%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5012%
5013% SwirlImage() swirls the pixels about the center of the image, where
5014% degrees indicates the sweep of the arc through which each pixel is moved.
5015% You get a more dramatic effect as the degrees move from 1 to 360.
5016%
5017% The format of the SwirlImage method is:
5018%
5019% Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005020% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005021%
5022% A description of each parameter follows:
5023%
5024% o image: the image.
5025%
5026% o degrees: Define the tightness of the swirling effect.
5027%
cristy76f512e2011-09-12 01:26:56 +00005028% o method: the pixel interpolation method.
5029%
cristy3ed852e2009-09-05 21:47:34 +00005030% o exception: return any errors or warnings in this structure.
5031%
5032*/
5033MagickExport Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005034 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005035{
5036#define SwirlImageTag "Swirl/Image"
5037
cristyfa112112010-01-04 17:48:07 +00005038 CacheView
5039 *image_view,
5040 *swirl_view;
5041
cristy3ed852e2009-09-05 21:47:34 +00005042 Image
5043 *swirl_image;
5044
cristy3ed852e2009-09-05 21:47:34 +00005045 MagickBooleanType
5046 status;
5047
cristybb503372010-05-27 20:51:26 +00005048 MagickOffsetType
5049 progress;
5050
cristy3ed852e2009-09-05 21:47:34 +00005051 MagickRealType
5052 radius;
5053
5054 PointInfo
5055 center,
5056 scale;
5057
cristybb503372010-05-27 20:51:26 +00005058 ssize_t
5059 y;
5060
cristy3ed852e2009-09-05 21:47:34 +00005061 /*
5062 Initialize swirl image attributes.
5063 */
5064 assert(image != (const Image *) NULL);
5065 assert(image->signature == MagickSignature);
5066 if (image->debug != MagickFalse)
5067 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5068 assert(exception != (ExceptionInfo *) NULL);
5069 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00005070 swirl_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005071 if (swirl_image == (Image *) NULL)
5072 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005073 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005074 {
cristy3ed852e2009-09-05 21:47:34 +00005075 swirl_image=DestroyImage(swirl_image);
5076 return((Image *) NULL);
5077 }
cristy4c08aed2011-07-01 19:47:50 +00005078 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005079 swirl_image->matte=MagickTrue;
5080 /*
5081 Compute scaling factor.
5082 */
5083 center.x=(double) image->columns/2.0;
5084 center.y=(double) image->rows/2.0;
5085 radius=MagickMax(center.x,center.y);
5086 scale.x=1.0;
5087 scale.y=1.0;
5088 if (image->columns > image->rows)
5089 scale.y=(double) image->columns/(double) image->rows;
5090 else
5091 if (image->columns < image->rows)
5092 scale.x=(double) image->rows/(double) image->columns;
5093 degrees=(double) DegreesToRadians(degrees);
5094 /*
5095 Swirl image.
5096 */
5097 status=MagickTrue;
5098 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00005099 image_view=AcquireCacheView(image);
5100 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005101#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005102 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005103#endif
cristybb503372010-05-27 20:51:26 +00005104 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005105 {
cristy3ed852e2009-09-05 21:47:34 +00005106 MagickRealType
5107 distance;
5108
5109 PointInfo
5110 delta;
5111
cristy6d188022011-09-12 13:23:33 +00005112 register const Quantum
5113 *restrict p;
5114
cristybb503372010-05-27 20:51:26 +00005115 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005116 x;
5117
cristy4c08aed2011-07-01 19:47:50 +00005118 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005119 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005120
5121 if (status == MagickFalse)
5122 continue;
cristy6d188022011-09-12 13:23:33 +00005123 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00005124 q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00005125 exception);
cristy6d188022011-09-12 13:23:33 +00005126 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005127 {
5128 status=MagickFalse;
5129 continue;
5130 }
cristy3ed852e2009-09-05 21:47:34 +00005131 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00005132 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005133 {
5134 /*
5135 Determine if the pixel is within an ellipse.
5136 */
cristy10a6c612012-01-29 21:41:05 +00005137 if (GetPixelMask(image,p) != 0)
5138 {
5139 p+=GetPixelChannels(image);
5140 q+=GetPixelChannels(swirl_image);
5141 continue;
5142 }
cristy3ed852e2009-09-05 21:47:34 +00005143 delta.x=scale.x*(double) (x-center.x);
5144 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00005145 if (distance >= (radius*radius))
5146 {
cristy1707c6c2012-01-18 23:30:54 +00005147 register ssize_t
5148 i;
5149
cristy6d188022011-09-12 13:23:33 +00005150 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy1707c6c2012-01-18 23:30:54 +00005151 {
5152 PixelChannel
5153 channel;
5154
5155 PixelTrait
5156 swirl_traits,
5157 traits;
5158
5159 channel=GetPixelChannelMapChannel(image,i);
5160 traits=GetPixelChannelMapTraits(image,channel);
5161 swirl_traits=GetPixelChannelMapTraits(swirl_image,channel);
5162 if ((traits == UndefinedPixelTrait) ||
5163 (swirl_traits == UndefinedPixelTrait))
5164 continue;
5165 SetPixelChannel(swirl_image,channel,p[i],q);
5166 }
cristy6d188022011-09-12 13:23:33 +00005167 }
5168 else
cristy3ed852e2009-09-05 21:47:34 +00005169 {
5170 MagickRealType
5171 cosine,
5172 factor,
5173 sine;
5174
5175 /*
5176 Swirl the pixel.
5177 */
5178 factor=1.0-sqrt((double) distance)/radius;
5179 sine=sin((double) (degrees*factor*factor));
5180 cosine=cos((double) (degrees*factor*factor));
cristy76f512e2011-09-12 01:26:56 +00005181 status=InterpolatePixelChannels(image,image_view,swirl_image,method,
5182 ((cosine*delta.x-sine*delta.y)/scale.x+center.x),(double)
5183 ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00005184 }
cristy6d188022011-09-12 13:23:33 +00005185 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00005186 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00005187 }
5188 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5189 status=MagickFalse;
5190 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5191 {
5192 MagickBooleanType
5193 proceed;
5194
cristyb5d5f722009-11-04 03:03:49 +00005195#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005196 #pragma omp critical (MagickCore_SwirlImage)
cristy3ed852e2009-09-05 21:47:34 +00005197#endif
5198 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5199 if (proceed == MagickFalse)
5200 status=MagickFalse;
5201 }
5202 }
5203 swirl_view=DestroyCacheView(swirl_view);
5204 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005205 if (status == MagickFalse)
5206 swirl_image=DestroyImage(swirl_image);
5207 return(swirl_image);
5208}
5209
5210/*
5211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5212% %
5213% %
5214% %
5215% T i n t I m a g e %
5216% %
5217% %
5218% %
5219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5220%
5221% TintImage() applies a color vector to each pixel in the image. The length
5222% of the vector is 0 for black and white and at its maximum for the midtones.
5223% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5224%
5225% The format of the TintImage method is:
5226%
cristyb817c3f2011-10-03 14:00:35 +00005227% Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005228% const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005229%
5230% A description of each parameter follows:
5231%
5232% o image: the image.
5233%
cristyb817c3f2011-10-03 14:00:35 +00005234% o blend: A color value used for tinting.
cristy3ed852e2009-09-05 21:47:34 +00005235%
5236% o tint: A color value used for tinting.
5237%
5238% o exception: return any errors or warnings in this structure.
5239%
5240*/
cristyb817c3f2011-10-03 14:00:35 +00005241MagickExport Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005242 const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005243{
5244#define TintImageTag "Tint/Image"
5245
cristyc4c8d132010-01-07 01:58:38 +00005246 CacheView
5247 *image_view,
5248 *tint_view;
5249
cristy3ed852e2009-09-05 21:47:34 +00005250 GeometryInfo
5251 geometry_info;
5252
5253 Image
5254 *tint_image;
5255
cristy3ed852e2009-09-05 21:47:34 +00005256 MagickBooleanType
5257 status;
5258
cristybb503372010-05-27 20:51:26 +00005259 MagickOffsetType
5260 progress;
cristy3ed852e2009-09-05 21:47:34 +00005261
cristy28474bf2011-09-11 23:32:52 +00005262 MagickRealType
5263 intensity;
5264
cristy4c08aed2011-07-01 19:47:50 +00005265 PixelInfo
cristy1707c6c2012-01-18 23:30:54 +00005266 color_vector;
cristy3ed852e2009-09-05 21:47:34 +00005267
cristybb503372010-05-27 20:51:26 +00005268 MagickStatusType
5269 flags;
5270
5271 ssize_t
5272 y;
5273
cristy3ed852e2009-09-05 21:47:34 +00005274 /*
5275 Allocate tint image.
5276 */
5277 assert(image != (const Image *) NULL);
5278 assert(image->signature == MagickSignature);
5279 if (image->debug != MagickFalse)
5280 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5281 assert(exception != (ExceptionInfo *) NULL);
5282 assert(exception->signature == MagickSignature);
5283 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5284 if (tint_image == (Image *) NULL)
5285 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005286 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005287 {
cristy3ed852e2009-09-05 21:47:34 +00005288 tint_image=DestroyImage(tint_image);
5289 return((Image *) NULL);
5290 }
cristyaed9c382011-10-03 17:54:21 +00005291 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005292 return(tint_image);
5293 /*
5294 Determine RGB values of the color.
5295 */
cristy1707c6c2012-01-18 23:30:54 +00005296 GetPixelInfo(image,&color_vector);
cristyb817c3f2011-10-03 14:00:35 +00005297 flags=ParseGeometry(blend,&geometry_info);
cristy1707c6c2012-01-18 23:30:54 +00005298 color_vector.red=geometry_info.rho;
5299 color_vector.green=geometry_info.rho;
5300 color_vector.blue=geometry_info.rho;
5301 color_vector.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005302 if ((flags & SigmaValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005303 color_vector.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00005304 if ((flags & XiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005305 color_vector.blue=geometry_info.xi;
cristyb817c3f2011-10-03 14:00:35 +00005306 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005307 color_vector.alpha=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005308 if (image->colorspace == CMYKColorspace)
5309 {
cristy1707c6c2012-01-18 23:30:54 +00005310 color_vector.black=geometry_info.rho;
cristy76f512e2011-09-12 01:26:56 +00005311 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005312 color_vector.black=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005313 if ((flags & ChiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005314 color_vector.alpha=geometry_info.chi;
cristy76f512e2011-09-12 01:26:56 +00005315 }
cristy28474bf2011-09-11 23:32:52 +00005316 intensity=(MagickRealType) GetPixelInfoIntensity(tint);
cristy1707c6c2012-01-18 23:30:54 +00005317 color_vector.red=(MagickRealType) (color_vector.red*tint->red/100.0-
5318 intensity);
5319 color_vector.green=(MagickRealType) (color_vector.green*tint->green/100.0-
5320 intensity);
5321 color_vector.blue=(MagickRealType) (color_vector.blue*tint->blue/100.0-
5322 intensity);
5323 color_vector.black=(MagickRealType) (color_vector.black*tint->black/100.0-
5324 intensity);
5325 color_vector.alpha=(MagickRealType) (color_vector.alpha*tint->alpha/100.0-
5326 intensity);
cristy3ed852e2009-09-05 21:47:34 +00005327 /*
5328 Tint image.
5329 */
5330 status=MagickTrue;
5331 progress=0;
5332 image_view=AcquireCacheView(image);
5333 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005334#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005335 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005336#endif
cristybb503372010-05-27 20:51:26 +00005337 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005338 {
cristy4c08aed2011-07-01 19:47:50 +00005339 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005340 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005341
cristy4c08aed2011-07-01 19:47:50 +00005342 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005343 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005344
cristy6b91acb2011-04-19 12:23:54 +00005345 register ssize_t
5346 x;
5347
cristy3ed852e2009-09-05 21:47:34 +00005348 if (status == MagickFalse)
5349 continue;
5350 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5351 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5352 exception);
cristy4c08aed2011-07-01 19:47:50 +00005353 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005354 {
5355 status=MagickFalse;
5356 continue;
5357 }
cristybb503372010-05-27 20:51:26 +00005358 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005359 {
cristy4c08aed2011-07-01 19:47:50 +00005360 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005361 pixel;
5362
5363 MagickRealType
5364 weight;
5365
cristy1707c6c2012-01-18 23:30:54 +00005366 register ssize_t
5367 i;
5368
cristy10a6c612012-01-29 21:41:05 +00005369 if (GetPixelMask(image,p) != 0)
5370 {
5371 p+=GetPixelChannels(image);
5372 q+=GetPixelChannels(tint_image);
5373 continue;
5374 }
cristy1707c6c2012-01-18 23:30:54 +00005375 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
5376 {
5377 PixelChannel
5378 channel;
5379
5380 PixelTrait
5381 tint_traits,
5382 traits;
5383
5384 channel=GetPixelChannelMapChannel(image,i);
5385 traits=GetPixelChannelMapTraits(image,channel);
5386 tint_traits=GetPixelChannelMapTraits(tint_image,channel);
5387 if ((traits == UndefinedPixelTrait) ||
5388 (tint_traits == UndefinedPixelTrait))
5389 continue;
5390 if ((tint_traits & CopyPixelTrait) != 0)
5391 {
5392 SetPixelChannel(tint_image,channel,p[i],q);
5393 continue;
5394 }
5395 }
5396 GetPixelInfo(image,&pixel);
5397 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5398 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5399 (1.0-(4.0*(weight*weight)));
5400 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5401 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5402 (1.0-(4.0*(weight*weight)));
5403 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5404 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5405 (1.0-(4.0*(weight*weight)));
5406 weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5407 pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5408 (1.0-(4.0*(weight*weight)));
5409 SetPixelInfoPixel(tint_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005410 p+=GetPixelChannels(image);
5411 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005412 }
5413 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5414 status=MagickFalse;
5415 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5416 {
5417 MagickBooleanType
5418 proceed;
5419
cristyb5d5f722009-11-04 03:03:49 +00005420#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005421 #pragma omp critical (MagickCore_TintImage)
cristy3ed852e2009-09-05 21:47:34 +00005422#endif
5423 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5424 if (proceed == MagickFalse)
5425 status=MagickFalse;
5426 }
5427 }
5428 tint_view=DestroyCacheView(tint_view);
5429 image_view=DestroyCacheView(image_view);
5430 if (status == MagickFalse)
5431 tint_image=DestroyImage(tint_image);
5432 return(tint_image);
5433}
5434
5435/*
5436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5437% %
5438% %
5439% %
5440% V i g n e t t e I m a g e %
5441% %
5442% %
5443% %
5444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5445%
5446% VignetteImage() softens the edges of the image in vignette style.
5447%
5448% The format of the VignetteImage method is:
5449%
5450% Image *VignetteImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00005451% const double sigma,const ssize_t x,const ssize_t y,
cristy05c0c9a2011-09-05 23:16:13 +00005452% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005453%
5454% A description of each parameter follows:
5455%
5456% o image: the image.
5457%
5458% o radius: the radius of the pixel neighborhood.
5459%
5460% o sigma: the standard deviation of the Gaussian, in pixels.
5461%
5462% o x, y: Define the x and y ellipse offset.
5463%
5464% o exception: return any errors or warnings in this structure.
5465%
5466*/
5467MagickExport Image *VignetteImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00005468 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005469{
5470 char
5471 ellipse[MaxTextExtent];
5472
5473 DrawInfo
5474 *draw_info;
5475
5476 Image
5477 *canvas_image,
5478 *blur_image,
5479 *oval_image,
5480 *vignette_image;
5481
5482 assert(image != (Image *) NULL);
5483 assert(image->signature == MagickSignature);
5484 if (image->debug != MagickFalse)
5485 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5486 assert(exception != (ExceptionInfo *) NULL);
5487 assert(exception->signature == MagickSignature);
5488 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5489 if (canvas_image == (Image *) NULL)
5490 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005491 if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005492 {
cristy3ed852e2009-09-05 21:47:34 +00005493 canvas_image=DestroyImage(canvas_image);
5494 return((Image *) NULL);
5495 }
5496 canvas_image->matte=MagickTrue;
cristy98621462011-12-31 22:31:11 +00005497 oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
5498 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005499 if (oval_image == (Image *) NULL)
5500 {
5501 canvas_image=DestroyImage(canvas_image);
5502 return((Image *) NULL);
5503 }
cristy9950d572011-10-01 18:22:35 +00005504 (void) QueryColorCompliance("#000000",AllCompliance,
5505 &oval_image->background_color,exception);
cristyea1a8aa2011-10-20 13:24:06 +00005506 (void) SetImageBackgroundColor(oval_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005507 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00005508 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5509 exception);
5510 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5511 exception);
cristy1707c6c2012-01-18 23:30:54 +00005512 (void) FormatLocaleString(ellipse,MaxTextExtent,"ellipse %g,%g,%g,%g,"
5513 "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5514 image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005515 draw_info->primitive=AcquireString(ellipse);
cristy018f07f2011-09-04 21:15:19 +00005516 (void) DrawImage(oval_image,draw_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005517 draw_info=DestroyDrawInfo(draw_info);
cristyaa2c16c2012-03-25 22:21:35 +00005518 blur_image=BlurImage(oval_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00005519 oval_image=DestroyImage(oval_image);
5520 if (blur_image == (Image *) NULL)
5521 {
5522 canvas_image=DestroyImage(canvas_image);
5523 return((Image *) NULL);
5524 }
5525 blur_image->matte=MagickFalse;
cristy39172402012-03-30 13:04:39 +00005526 (void) CompositeImage(canvas_image,blur_image,IntensityCompositeOp,MagickTrue,
5527 0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00005528 blur_image=DestroyImage(blur_image);
5529 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5530 canvas_image=DestroyImage(canvas_image);
5531 return(vignette_image);
5532}
5533
5534/*
5535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5536% %
5537% %
5538% %
5539% W a v e I m a g e %
5540% %
5541% %
5542% %
5543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5544%
5545% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005546% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005547% by the given parameters.
5548%
5549% The format of the WaveImage method is:
5550%
5551% Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005552% const double wave_length,const PixelInterpolateMethod method,
5553% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005554%
5555% A description of each parameter follows:
5556%
5557% o image: the image.
5558%
5559% o amplitude, wave_length: Define the amplitude and wave length of the
5560% sine wave.
5561%
cristy5c4e2582011-09-11 19:21:03 +00005562% o interpolate: the pixel interpolation method.
5563%
cristy3ed852e2009-09-05 21:47:34 +00005564% o exception: return any errors or warnings in this structure.
5565%
5566*/
5567MagickExport Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005568 const double wave_length,const PixelInterpolateMethod method,
5569 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005570{
5571#define WaveImageTag "Wave/Image"
5572
cristyfa112112010-01-04 17:48:07 +00005573 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005574 *image_view,
cristyfa112112010-01-04 17:48:07 +00005575 *wave_view;
5576
cristy3ed852e2009-09-05 21:47:34 +00005577 Image
5578 *wave_image;
5579
cristy3ed852e2009-09-05 21:47:34 +00005580 MagickBooleanType
5581 status;
5582
cristybb503372010-05-27 20:51:26 +00005583 MagickOffsetType
5584 progress;
5585
cristy3ed852e2009-09-05 21:47:34 +00005586 MagickRealType
5587 *sine_map;
5588
cristybb503372010-05-27 20:51:26 +00005589 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005590 i;
5591
cristybb503372010-05-27 20:51:26 +00005592 ssize_t
5593 y;
5594
cristy3ed852e2009-09-05 21:47:34 +00005595 /*
5596 Initialize wave image attributes.
5597 */
5598 assert(image != (Image *) NULL);
5599 assert(image->signature == MagickSignature);
5600 if (image->debug != MagickFalse)
5601 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5602 assert(exception != (ExceptionInfo *) NULL);
5603 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005604 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005605 fabs(amplitude)),MagickTrue,exception);
5606 if (wave_image == (Image *) NULL)
5607 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005608 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005609 {
cristy3ed852e2009-09-05 21:47:34 +00005610 wave_image=DestroyImage(wave_image);
5611 return((Image *) NULL);
5612 }
cristy4c08aed2011-07-01 19:47:50 +00005613 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005614 wave_image->matte=MagickTrue;
5615 /*
5616 Allocate sine map.
5617 */
5618 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5619 sizeof(*sine_map));
5620 if (sine_map == (MagickRealType *) NULL)
5621 {
5622 wave_image=DestroyImage(wave_image);
5623 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5624 }
cristybb503372010-05-27 20:51:26 +00005625 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005626 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5627 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005628 /*
5629 Wave image.
5630 */
5631 status=MagickTrue;
5632 progress=0;
cristyd76c51e2011-03-26 00:21:26 +00005633 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005634 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005635 (void) SetCacheViewVirtualPixelMethod(image_view,
5636 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005637#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005638 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005639#endif
cristybb503372010-05-27 20:51:26 +00005640 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005641 {
cristy4c08aed2011-07-01 19:47:50 +00005642 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005643 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005644
cristye97bb922011-04-03 01:36:52 +00005645 register ssize_t
5646 x;
5647
cristy3ed852e2009-09-05 21:47:34 +00005648 if (status == MagickFalse)
5649 continue;
5650 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5651 exception);
cristyacd2ed22011-08-30 01:44:23 +00005652 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005653 {
5654 status=MagickFalse;
5655 continue;
5656 }
cristybb503372010-05-27 20:51:26 +00005657 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005658 {
cristy5c4e2582011-09-11 19:21:03 +00005659 status=InterpolatePixelChannels(image,image_view,wave_image,method,
5660 (double) x,(double) (y-sine_map[x]),q,exception);
cristyed231572011-07-14 02:18:59 +00005661 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005662 }
5663 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5664 status=MagickFalse;
5665 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5666 {
5667 MagickBooleanType
5668 proceed;
5669
cristyb5d5f722009-11-04 03:03:49 +00005670#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005671 #pragma omp critical (MagickCore_WaveImage)
cristy3ed852e2009-09-05 21:47:34 +00005672#endif
5673 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5674 if (proceed == MagickFalse)
5675 status=MagickFalse;
5676 }
5677 }
5678 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005679 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005680 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5681 if (status == MagickFalse)
5682 wave_image=DestroyImage(wave_image);
5683 return(wave_image);
5684}