blob: db4167335d4aedfc8171df6517f80cc6e0145761 [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;
cristyc7e6ff62011-10-03 13:46:11 +0000702 pixel.alpha=100.0;
cristy3ed852e2009-09-05 21:47:34 +0000703 if ((flags & SigmaValue) != 0)
704 pixel.green=geometry_info.sigma;
705 if ((flags & XiValue) != 0)
706 pixel.blue=geometry_info.xi;
707 if ((flags & PsiValue) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000708 pixel.alpha=geometry_info.psi;
cristyc7e6ff62011-10-03 13:46:11 +0000709 if (pixel.colorspace == CMYKColorspace)
710 {
711 pixel.black=geometry_info.rho;
712 if ((flags & PsiValue) != 0)
713 pixel.black=geometry_info.psi;
714 if ((flags & ChiValue) != 0)
715 pixel.alpha=geometry_info.chi;
716 }
cristy3ed852e2009-09-05 21:47:34 +0000717 /*
718 Colorize DirectClass image.
719 */
720 status=MagickTrue;
721 progress=0;
722 image_view=AcquireCacheView(image);
723 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000724#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000725 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000726#endif
cristybb503372010-05-27 20:51:26 +0000727 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000728 {
729 MagickBooleanType
730 sync;
731
cristy4c08aed2011-07-01 19:47:50 +0000732 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000733 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000734
cristybb503372010-05-27 20:51:26 +0000735 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000736 x;
737
cristy4c08aed2011-07-01 19:47:50 +0000738 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000739 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000740
741 if (status == MagickFalse)
742 continue;
743 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
744 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
745 exception);
cristy4c08aed2011-07-01 19:47:50 +0000746 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000747 {
748 status=MagickFalse;
749 continue;
750 }
cristybb503372010-05-27 20:51:26 +0000751 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000752 {
cristyc7e6ff62011-10-03 13:46:11 +0000753 register ssize_t
754 i;
755
756 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
757 {
758 PixelChannel
759 channel;
760
761 PixelTrait
762 colorize_traits,
763 traits;
764
cristye2a912b2011-12-05 20:02:07 +0000765 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000766 traits=GetPixelChannelMapTraits(image,channel);
cristyc7e6ff62011-10-03 13:46:11 +0000767 colorize_traits=GetPixelChannelMapTraits(colorize_image,channel);
768 if ((traits == UndefinedPixelTrait) ||
769 (colorize_traits == UndefinedPixelTrait))
770 continue;
cristyd09f8802012-02-04 16:44:10 +0000771 if (((colorize_traits & CopyPixelTrait) != 0) ||
772 (GetPixelMask(image,p) != 0))
cristyc7e6ff62011-10-03 13:46:11 +0000773 {
774 SetPixelChannel(colorize_image,channel,p[i],q);
775 continue;
776 }
777 switch (channel)
778 {
779 case RedPixelChannel:
780 {
781 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
782 (100.0-pixel.red)+colorize->red*pixel.red)/100.0),q);
783 break;
784 }
785 case GreenPixelChannel:
786 {
787 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
788 (100.0-pixel.green)+colorize->green*pixel.green)/100.0),q);
789 break;
790 }
791 case BluePixelChannel:
792 {
793 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
794 (100.0-pixel.blue)+colorize->blue*pixel.blue)/100.0),q);
795 break;
796 }
797 case BlackPixelChannel:
798 {
799 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
800 (100.0-pixel.black)+colorize->black*pixel.black)/100.0),q);
801 break;
802 }
803 case AlphaPixelChannel:
804 {
805 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
806 (100.0-pixel.alpha)+colorize->alpha*pixel.alpha)/100.0),q);
807 break;
808 }
809 default:
810 {
811 SetPixelChannel(colorize_image,channel,p[i],q);
812 break;
813 }
814 }
815 }
cristyed231572011-07-14 02:18:59 +0000816 p+=GetPixelChannels(image);
817 q+=GetPixelChannels(colorize_image);
cristy3ed852e2009-09-05 21:47:34 +0000818 }
819 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
820 if (sync == MagickFalse)
821 status=MagickFalse;
822 if (image->progress_monitor != (MagickProgressMonitor) NULL)
823 {
824 MagickBooleanType
825 proceed;
826
cristy319a1e72010-02-21 15:13:11 +0000827#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000828 #pragma omp critical (MagickCore_ColorizeImage)
cristy3ed852e2009-09-05 21:47:34 +0000829#endif
830 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
831 if (proceed == MagickFalse)
832 status=MagickFalse;
833 }
834 }
835 image_view=DestroyCacheView(image_view);
836 colorize_view=DestroyCacheView(colorize_view);
837 if (status == MagickFalse)
838 colorize_image=DestroyImage(colorize_image);
839 return(colorize_image);
840}
841
842/*
843%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
844% %
845% %
846% %
cristye6365592010-04-02 17:31:23 +0000847% C o l o r M a t r i x I m a g e %
848% %
849% %
850% %
851%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
852%
853% ColorMatrixImage() applies color transformation to an image. This method
854% permits saturation changes, hue rotation, luminance to alpha, and various
855% other effects. Although variable-sized transformation matrices can be used,
856% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
857% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
858% except offsets are in column 6 rather than 5 (in support of CMYKA images)
859% and offsets are normalized (divide Flash offset by 255).
860%
861% The format of the ColorMatrixImage method is:
862%
863% Image *ColorMatrixImage(const Image *image,
864% const KernelInfo *color_matrix,ExceptionInfo *exception)
865%
866% A description of each parameter follows:
867%
868% o image: the image.
869%
870% o color_matrix: the color matrix.
871%
872% o exception: return any errors or warnings in this structure.
873%
874*/
anthonyfd706f92012-01-19 04:22:02 +0000875/* FUTURE: modify to make use of a MagickMatrix Mutliply function
876 That should be provided in "matrix.c"
877 (ASIDE: actually distorts should do this too but currently doesn't)
878*/
879
cristye6365592010-04-02 17:31:23 +0000880MagickExport Image *ColorMatrixImage(const Image *image,
881 const KernelInfo *color_matrix,ExceptionInfo *exception)
882{
883#define ColorMatrixImageTag "ColorMatrix/Image"
884
885 CacheView
886 *color_view,
887 *image_view;
888
889 double
890 ColorMatrix[6][6] =
891 {
892 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
893 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
894 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
895 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
896 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
897 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
898 };
899
900 Image
901 *color_image;
902
cristye6365592010-04-02 17:31:23 +0000903 MagickBooleanType
904 status;
905
cristybb503372010-05-27 20:51:26 +0000906 MagickOffsetType
907 progress;
908
909 register ssize_t
cristye6365592010-04-02 17:31:23 +0000910 i;
911
cristybb503372010-05-27 20:51:26 +0000912 ssize_t
913 u,
914 v,
915 y;
916
cristye6365592010-04-02 17:31:23 +0000917 /*
anthonyfd706f92012-01-19 04:22:02 +0000918 Map given color_matrix, into a 6x6 matrix RGBKA and a constant
cristye6365592010-04-02 17:31:23 +0000919 */
920 assert(image != (Image *) NULL);
921 assert(image->signature == MagickSignature);
922 if (image->debug != MagickFalse)
923 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
924 assert(exception != (ExceptionInfo *) NULL);
925 assert(exception->signature == MagickSignature);
926 i=0;
cristybb503372010-05-27 20:51:26 +0000927 for (v=0; v < (ssize_t) color_matrix->height; v++)
928 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000929 {
930 if ((v < 6) && (u < 6))
931 ColorMatrix[v][u]=color_matrix->values[i];
932 i++;
933 }
934 /*
935 Initialize color image.
936 */
cristy12550e62010-06-07 12:46:40 +0000937 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000938 if (color_image == (Image *) NULL)
939 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000940 if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
cristye6365592010-04-02 17:31:23 +0000941 {
cristye6365592010-04-02 17:31:23 +0000942 color_image=DestroyImage(color_image);
943 return((Image *) NULL);
944 }
945 if (image->debug != MagickFalse)
946 {
947 char
948 format[MaxTextExtent],
949 *message;
950
951 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
952 " ColorMatrix image with color matrix:");
953 message=AcquireString("");
954 for (v=0; v < 6; v++)
955 {
956 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000957 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000958 (void) ConcatenateString(&message,format);
959 for (u=0; u < 6; u++)
960 {
cristyb51dff52011-05-19 16:55:47 +0000961 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
cristye6365592010-04-02 17:31:23 +0000962 ColorMatrix[v][u]);
963 (void) ConcatenateString(&message,format);
964 }
965 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
966 }
967 message=DestroyString(message);
968 }
969 /*
anthonyfd706f92012-01-19 04:22:02 +0000970 Apply the ColorMatrix to image.
cristye6365592010-04-02 17:31:23 +0000971 */
972 status=MagickTrue;
973 progress=0;
974 image_view=AcquireCacheView(image);
975 color_view=AcquireCacheView(color_image);
976#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000977 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye6365592010-04-02 17:31:23 +0000978#endif
cristybb503372010-05-27 20:51:26 +0000979 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000980 {
cristyfcc25d92012-02-19 23:06:48 +0000981 PixelInfo
cristye6365592010-04-02 17:31:23 +0000982 pixel;
983
cristy4c08aed2011-07-01 19:47:50 +0000984 register const Quantum
cristye6365592010-04-02 17:31:23 +0000985 *restrict p;
986
cristy4c08aed2011-07-01 19:47:50 +0000987 register Quantum
988 *restrict q;
989
cristybb503372010-05-27 20:51:26 +0000990 register ssize_t
cristye6365592010-04-02 17:31:23 +0000991 x;
992
cristye6365592010-04-02 17:31:23 +0000993 if (status == MagickFalse)
994 continue;
995 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
996 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
997 exception);
cristy4c08aed2011-07-01 19:47:50 +0000998 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristye6365592010-04-02 17:31:23 +0000999 {
1000 status=MagickFalse;
1001 continue;
1002 }
cristyfcc25d92012-02-19 23:06:48 +00001003 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001004 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +00001005 {
cristybb503372010-05-27 20:51:26 +00001006 register ssize_t
cristye6365592010-04-02 17:31:23 +00001007 v;
1008
cristybb503372010-05-27 20:51:26 +00001009 size_t
cristye6365592010-04-02 17:31:23 +00001010 height;
1011
cristyfcc25d92012-02-19 23:06:48 +00001012 GetPixelInfoPixel(image,p,&pixel);
cristye6365592010-04-02 17:31:23 +00001013 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +00001014 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +00001015 {
cristyfcc25d92012-02-19 23:06:48 +00001016 MagickRealType
1017 sum;
1018
1019 sum=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
cristy4c08aed2011-07-01 19:47:50 +00001020 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +00001021 if (image->colorspace == CMYKColorspace)
cristyfcc25d92012-02-19 23:06:48 +00001022 sum+=ColorMatrix[v][3]*GetPixelBlack(image,p);
cristy4c08aed2011-07-01 19:47:50 +00001023 if (image->matte != MagickFalse)
cristyfcc25d92012-02-19 23:06:48 +00001024 sum+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
1025 sum+=QuantumRange*ColorMatrix[v][5];
cristye6365592010-04-02 17:31:23 +00001026 switch (v)
1027 {
cristyfcc25d92012-02-19 23:06:48 +00001028 case 0: pixel.red=sum; break;
1029 case 1: pixel.green=sum; break;
1030 case 2: pixel.blue=sum; break;
1031 case 3: pixel.black=sum; break;
1032 case 4: pixel.alpha=sum; break;
1033 default: break;
cristye6365592010-04-02 17:31:23 +00001034 }
1035 }
cristyfcc25d92012-02-19 23:06:48 +00001036 SetPixelInfoPixel(color_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001037 p+=GetPixelChannels(image);
1038 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +00001039 }
1040 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1041 status=MagickFalse;
1042 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1043 {
1044 MagickBooleanType
1045 proceed;
1046
1047#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00001048 #pragma omp critical (MagickCore_ColorMatrixImage)
cristye6365592010-04-02 17:31:23 +00001049#endif
1050 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1051 image->rows);
1052 if (proceed == MagickFalse)
1053 status=MagickFalse;
1054 }
1055 }
1056 color_view=DestroyCacheView(color_view);
1057 image_view=DestroyCacheView(image_view);
1058 if (status == MagickFalse)
1059 color_image=DestroyImage(color_image);
1060 return(color_image);
1061}
1062
1063/*
1064%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1065% %
1066% %
1067% %
cristy3ed852e2009-09-05 21:47:34 +00001068+ D e s t r o y F x I n f o %
1069% %
1070% %
1071% %
1072%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1073%
1074% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1075%
1076% The format of the DestroyFxInfo method is:
1077%
1078% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1079%
1080% A description of each parameter follows:
1081%
1082% o fx_info: the fx info.
1083%
1084*/
cristy7832dc22011-09-05 01:21:53 +00001085MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
cristy3ed852e2009-09-05 21:47:34 +00001086{
cristybb503372010-05-27 20:51:26 +00001087 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001088 i;
1089
1090 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1091 fx_info->expression=DestroyString(fx_info->expression);
1092 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1093 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001094 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001095 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1096 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001097 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1098 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1099 return(fx_info);
1100}
1101
1102/*
1103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1104% %
1105% %
1106% %
cristy3ed852e2009-09-05 21:47:34 +00001107+ 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 %
1108% %
1109% %
1110% %
1111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1112%
1113% FxEvaluateChannelExpression() evaluates an expression and returns the
1114% results.
1115%
1116% The format of the FxEvaluateExpression method is:
1117%
1118% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001119% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001120% MagickRealType *alpha,Exceptioninfo *exception)
1121% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1122% MagickRealType *alpha,Exceptioninfo *exception)
1123%
1124% A description of each parameter follows:
1125%
1126% o fx_info: the fx info.
1127%
1128% o channel: the channel.
1129%
1130% o x,y: the pixel position.
1131%
1132% o alpha: the result.
1133%
1134% o exception: return any errors or warnings in this structure.
1135%
1136*/
1137
cristy351842f2010-03-07 15:27:38 +00001138static inline double MagickMax(const double x,const double y)
1139{
1140 if (x > y)
1141 return(x);
1142 return(y);
1143}
1144
1145static inline double MagickMin(const double x,const double y)
1146{
1147 if (x < y)
1148 return(x);
1149 return(y);
1150}
1151
cristy3ed852e2009-09-05 21:47:34 +00001152static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001153 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001154{
1155 char
1156 key[MaxTextExtent],
1157 statistic[MaxTextExtent];
1158
1159 const char
1160 *value;
1161
1162 register const char
1163 *p;
1164
1165 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1166 if (*p == '.')
1167 switch (*++p) /* e.g. depth.r */
1168 {
cristy541ae572011-08-05 19:08:59 +00001169 case 'r': channel=RedPixelChannel; break;
1170 case 'g': channel=GreenPixelChannel; break;
1171 case 'b': channel=BluePixelChannel; break;
1172 case 'c': channel=CyanPixelChannel; break;
1173 case 'm': channel=MagentaPixelChannel; break;
1174 case 'y': channel=YellowPixelChannel; break;
1175 case 'k': channel=BlackPixelChannel; break;
cristy3ed852e2009-09-05 21:47:34 +00001176 default: break;
1177 }
cristyb51dff52011-05-19 16:55:47 +00001178 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001179 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001180 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1181 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001182 return(QuantumScale*StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001183 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1184 if (LocaleNCompare(symbol,"depth",5) == 0)
1185 {
cristybb503372010-05-27 20:51:26 +00001186 size_t
cristy3ed852e2009-09-05 21:47:34 +00001187 depth;
1188
cristyfefab1b2011-07-05 00:33:22 +00001189 depth=GetImageDepth(image,exception);
1190 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001191 }
1192 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1193 {
1194 double
1195 kurtosis,
1196 skewness;
1197
cristyd42d9952011-07-08 14:21:50 +00001198 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001199 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001200 }
1201 if (LocaleNCompare(symbol,"maxima",6) == 0)
1202 {
1203 double
1204 maxima,
1205 minima;
1206
cristyd42d9952011-07-08 14:21:50 +00001207 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001208 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001209 }
1210 if (LocaleNCompare(symbol,"mean",4) == 0)
1211 {
1212 double
1213 mean,
1214 standard_deviation;
1215
cristyd42d9952011-07-08 14:21:50 +00001216 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001217 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001218 }
1219 if (LocaleNCompare(symbol,"minima",6) == 0)
1220 {
1221 double
1222 maxima,
1223 minima;
1224
cristyd42d9952011-07-08 14:21:50 +00001225 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001226 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001227 }
1228 if (LocaleNCompare(symbol,"skewness",8) == 0)
1229 {
1230 double
1231 kurtosis,
1232 skewness;
1233
cristyd42d9952011-07-08 14:21:50 +00001234 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001235 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001236 }
1237 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1238 {
1239 double
1240 mean,
1241 standard_deviation;
1242
cristyd42d9952011-07-08 14:21:50 +00001243 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001244 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001245 standard_deviation);
1246 }
1247 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1248 ConstantString(statistic));
cristydbdd0e32011-11-04 23:29:40 +00001249 return(QuantumScale*StringToDouble(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001250}
1251
1252static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001253 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001254 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001255
cristyb0aad4c2011-11-02 19:30:35 +00001256static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
1257{
1258 if (beta != 0)
1259 return(FxGCD(beta,alpha % beta));
1260 return(alpha);
1261}
1262
cristy3ed852e2009-09-05 21:47:34 +00001263static inline const char *FxSubexpression(const char *expression,
1264 ExceptionInfo *exception)
1265{
1266 const char
1267 *subexpression;
1268
cristybb503372010-05-27 20:51:26 +00001269 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001270 level;
1271
1272 level=0;
1273 subexpression=expression;
1274 while ((*subexpression != '\0') &&
1275 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1276 {
1277 if (strchr("(",(int) *subexpression) != (char *) NULL)
1278 level++;
1279 else
1280 if (strchr(")",(int) *subexpression) != (char *) NULL)
1281 level--;
1282 subexpression++;
1283 }
1284 if (*subexpression == '\0')
1285 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1286 "UnbalancedParenthesis","`%s'",expression);
1287 return(subexpression);
1288}
1289
cristy0568ffc2011-07-25 16:54:14 +00001290static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001291 const ssize_t x,const ssize_t y,const char *expression,
1292 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001293{
1294 char
1295 *q,
1296 subexpression[MaxTextExtent],
1297 symbol[MaxTextExtent];
1298
1299 const char
1300 *p,
1301 *value;
1302
1303 Image
1304 *image;
1305
cristy4c08aed2011-07-01 19:47:50 +00001306 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001307 pixel;
1308
1309 MagickRealType
1310 alpha,
1311 beta;
1312
1313 PointInfo
1314 point;
1315
cristybb503372010-05-27 20:51:26 +00001316 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001317 i;
1318
1319 size_t
cristy1707c6c2012-01-18 23:30:54 +00001320 length,
cristy3ed852e2009-09-05 21:47:34 +00001321 level;
1322
1323 p=expression;
1324 i=GetImageIndexInList(fx_info->images);
1325 level=0;
1326 point.x=(double) x;
1327 point.y=(double) y;
1328 if (isalpha((int) *(p+1)) == 0)
1329 {
1330 if (strchr("suv",(int) *p) != (char *) NULL)
1331 {
1332 switch (*p)
1333 {
1334 case 's':
1335 default:
1336 {
1337 i=GetImageIndexInList(fx_info->images);
1338 break;
1339 }
1340 case 'u': i=0; break;
1341 case 'v': i=1; break;
1342 }
1343 p++;
1344 if (*p == '[')
1345 {
1346 level++;
1347 q=subexpression;
1348 for (p++; *p != '\0'; )
1349 {
1350 if (*p == '[')
1351 level++;
1352 else
1353 if (*p == ']')
1354 {
1355 level--;
1356 if (level == 0)
1357 break;
1358 }
1359 *q++=(*p++);
1360 }
1361 *q='\0';
1362 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1363 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001364 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001365 p++;
1366 }
1367 if (*p == '.')
1368 p++;
1369 }
1370 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1371 {
1372 p++;
1373 if (*p == '{')
1374 {
1375 level++;
1376 q=subexpression;
1377 for (p++; *p != '\0'; )
1378 {
1379 if (*p == '{')
1380 level++;
1381 else
1382 if (*p == '}')
1383 {
1384 level--;
1385 if (level == 0)
1386 break;
1387 }
1388 *q++=(*p++);
1389 }
1390 *q='\0';
1391 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1392 &beta,exception);
1393 point.x=alpha;
1394 point.y=beta;
1395 p++;
1396 }
1397 else
1398 if (*p == '[')
1399 {
1400 level++;
1401 q=subexpression;
1402 for (p++; *p != '\0'; )
1403 {
1404 if (*p == '[')
1405 level++;
1406 else
1407 if (*p == ']')
1408 {
1409 level--;
1410 if (level == 0)
1411 break;
1412 }
1413 *q++=(*p++);
1414 }
1415 *q='\0';
1416 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1417 &beta,exception);
1418 point.x+=alpha;
1419 point.y+=beta;
1420 p++;
1421 }
1422 if (*p == '.')
1423 p++;
1424 }
1425 }
1426 length=GetImageListLength(fx_info->images);
1427 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001428 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001429 i%=length;
1430 image=GetImageFromList(fx_info->images,i);
1431 if (image == (Image *) NULL)
1432 {
1433 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1434 "NoSuchImage","`%s'",expression);
1435 return(0.0);
1436 }
cristy4c08aed2011-07-01 19:47:50 +00001437 GetPixelInfo(image,&pixel);
1438 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001439 point.x,point.y,&pixel,exception);
cristy1707c6c2012-01-18 23:30:54 +00001440 if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1441 (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001442 (LocaleCompare(p,"saturation") != 0) &&
1443 (LocaleCompare(p,"lightness") != 0))
1444 {
1445 char
1446 name[MaxTextExtent];
1447
1448 (void) CopyMagickString(name,p,MaxTextExtent);
1449 for (q=name+(strlen(name)-1); q > name; q--)
1450 {
1451 if (*q == ')')
1452 break;
1453 if (*q == '.')
1454 {
1455 *q='\0';
1456 break;
1457 }
1458 }
1459 if ((strlen(name) > 2) &&
1460 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1461 {
cristy4c08aed2011-07-01 19:47:50 +00001462 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001463 *color;
1464
cristy4c08aed2011-07-01 19:47:50 +00001465 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1466 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001467 {
1468 pixel=(*color);
1469 p+=strlen(name);
1470 }
1471 else
cristy1707c6c2012-01-18 23:30:54 +00001472 {
1473 MagickBooleanType
1474 status;
1475
1476 status=QueryColorCompliance(name,AllCompliance,&pixel,
1477 fx_info->exception);
1478 if (status != MagickFalse)
1479 {
1480 (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1481 name),ClonePixelInfo(&pixel));
1482 p+=strlen(name);
1483 }
1484 }
cristy3ed852e2009-09-05 21:47:34 +00001485 }
1486 }
1487 (void) CopyMagickString(symbol,p,MaxTextExtent);
1488 StripString(symbol);
1489 if (*symbol == '\0')
1490 {
1491 switch (channel)
1492 {
cristy0568ffc2011-07-25 16:54:14 +00001493 case RedPixelChannel: return(QuantumScale*pixel.red);
1494 case GreenPixelChannel: return(QuantumScale*pixel.green);
1495 case BluePixelChannel: return(QuantumScale*pixel.blue);
1496 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001497 {
1498 if (image->colorspace != CMYKColorspace)
1499 {
1500 (void) ThrowMagickException(exception,GetMagickModule(),
cristy1a020e42011-12-06 18:13:23 +00001501 ImageError,"ColorSeparatedImageRequired","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00001502 image->filename);
1503 return(0.0);
1504 }
cristy4c08aed2011-07-01 19:47:50 +00001505 return(QuantumScale*pixel.black);
1506 }
cristy0568ffc2011-07-25 16:54:14 +00001507 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001508 {
1509 MagickRealType
1510 alpha;
1511
1512 if (pixel.matte == MagickFalse)
1513 return(1.0);
1514 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1515 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001516 }
cristya382aca2011-12-06 18:22:48 +00001517 case IndexPixelChannel:
1518 return(0.0);
cristyb3a73b52011-07-26 01:34:43 +00001519 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001520 {
cristy4c08aed2011-07-01 19:47:50 +00001521 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001522 }
cristy3ed852e2009-09-05 21:47:34 +00001523 default:
1524 break;
1525 }
1526 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1527 "UnableToParseExpression","`%s'",p);
1528 return(0.0);
1529 }
1530 switch (*symbol)
1531 {
1532 case 'A':
1533 case 'a':
1534 {
1535 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001536 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001537 break;
1538 }
1539 case 'B':
1540 case 'b':
1541 {
1542 if (LocaleCompare(symbol,"b") == 0)
1543 return(QuantumScale*pixel.blue);
1544 break;
1545 }
1546 case 'C':
1547 case 'c':
1548 {
1549 if (LocaleNCompare(symbol,"channel",7) == 0)
1550 {
1551 GeometryInfo
1552 channel_info;
1553
1554 MagickStatusType
1555 flags;
1556
1557 flags=ParseGeometry(symbol+7,&channel_info);
1558 if (image->colorspace == CMYKColorspace)
1559 switch (channel)
1560 {
cristy0568ffc2011-07-25 16:54:14 +00001561 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001562 {
1563 if ((flags & RhoValue) == 0)
1564 return(0.0);
1565 return(channel_info.rho);
1566 }
cristy0568ffc2011-07-25 16:54:14 +00001567 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001568 {
1569 if ((flags & SigmaValue) == 0)
1570 return(0.0);
1571 return(channel_info.sigma);
1572 }
cristy0568ffc2011-07-25 16:54:14 +00001573 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001574 {
1575 if ((flags & XiValue) == 0)
1576 return(0.0);
1577 return(channel_info.xi);
1578 }
cristy0568ffc2011-07-25 16:54:14 +00001579 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001580 {
1581 if ((flags & PsiValue) == 0)
1582 return(0.0);
1583 return(channel_info.psi);
1584 }
cristy0568ffc2011-07-25 16:54:14 +00001585 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001586 {
1587 if ((flags & ChiValue) == 0)
1588 return(0.0);
1589 return(channel_info.chi);
1590 }
1591 default:
1592 return(0.0);
1593 }
1594 switch (channel)
1595 {
cristy0568ffc2011-07-25 16:54:14 +00001596 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001597 {
1598 if ((flags & RhoValue) == 0)
1599 return(0.0);
1600 return(channel_info.rho);
1601 }
cristy0568ffc2011-07-25 16:54:14 +00001602 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001603 {
1604 if ((flags & SigmaValue) == 0)
1605 return(0.0);
1606 return(channel_info.sigma);
1607 }
cristy0568ffc2011-07-25 16:54:14 +00001608 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001609 {
1610 if ((flags & XiValue) == 0)
1611 return(0.0);
1612 return(channel_info.xi);
1613 }
cristy0568ffc2011-07-25 16:54:14 +00001614 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001615 {
1616 if ((flags & ChiValue) == 0)
1617 return(0.0);
1618 return(channel_info.chi);
1619 }
cristy0568ffc2011-07-25 16:54:14 +00001620 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001621 {
1622 if ((flags & PsiValue) == 0)
1623 return(0.0);
1624 return(channel_info.psi);
1625 }
cristy3ed852e2009-09-05 21:47:34 +00001626 default:
1627 return(0.0);
1628 }
1629 return(0.0);
1630 }
1631 if (LocaleCompare(symbol,"c") == 0)
1632 return(QuantumScale*pixel.red);
1633 break;
1634 }
1635 case 'D':
1636 case 'd':
1637 {
1638 if (LocaleNCompare(symbol,"depth",5) == 0)
1639 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1640 break;
1641 }
1642 case 'G':
1643 case 'g':
1644 {
1645 if (LocaleCompare(symbol,"g") == 0)
1646 return(QuantumScale*pixel.green);
1647 break;
1648 }
1649 case 'K':
1650 case 'k':
1651 {
1652 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1653 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1654 if (LocaleCompare(symbol,"k") == 0)
1655 {
1656 if (image->colorspace != CMYKColorspace)
1657 {
1658 (void) ThrowMagickException(exception,GetMagickModule(),
1659 OptionError,"ColorSeparatedImageRequired","`%s'",
1660 image->filename);
1661 return(0.0);
1662 }
cristy4c08aed2011-07-01 19:47:50 +00001663 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001664 }
1665 break;
1666 }
1667 case 'H':
1668 case 'h':
1669 {
1670 if (LocaleCompare(symbol,"h") == 0)
1671 return((MagickRealType) image->rows);
1672 if (LocaleCompare(symbol,"hue") == 0)
1673 {
1674 double
1675 hue,
1676 lightness,
1677 saturation;
1678
cristyda1f9c12011-10-02 21:39:49 +00001679 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1680 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001681 return(hue);
1682 }
1683 break;
1684 }
1685 case 'I':
1686 case 'i':
1687 {
1688 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1689 (LocaleCompare(symbol,"image.minima") == 0) ||
1690 (LocaleCompare(symbol,"image.maxima") == 0) ||
1691 (LocaleCompare(symbol,"image.mean") == 0) ||
1692 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1693 (LocaleCompare(symbol,"image.skewness") == 0) ||
1694 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1695 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1696 if (LocaleCompare(symbol,"image.resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001697 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001698 if (LocaleCompare(symbol,"image.resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001699 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001700 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001701 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001702 if (LocaleCompare(symbol,"i") == 0)
1703 return((MagickRealType) x);
1704 break;
1705 }
1706 case 'J':
1707 case 'j':
1708 {
1709 if (LocaleCompare(symbol,"j") == 0)
1710 return((MagickRealType) y);
1711 break;
1712 }
1713 case 'L':
1714 case 'l':
1715 {
1716 if (LocaleCompare(symbol,"lightness") == 0)
1717 {
1718 double
1719 hue,
1720 lightness,
1721 saturation;
1722
cristyda1f9c12011-10-02 21:39:49 +00001723 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1724 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001725 return(lightness);
1726 }
1727 if (LocaleCompare(symbol,"luminance") == 0)
1728 {
1729 double
1730 luminence;
1731
1732 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1733 return(QuantumScale*luminence);
1734 }
1735 break;
1736 }
1737 case 'M':
1738 case 'm':
1739 {
1740 if (LocaleNCompare(symbol,"maxima",6) == 0)
1741 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1742 if (LocaleNCompare(symbol,"mean",4) == 0)
1743 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1744 if (LocaleNCompare(symbol,"minima",6) == 0)
1745 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1746 if (LocaleCompare(symbol,"m") == 0)
1747 return(QuantumScale*pixel.blue);
1748 break;
1749 }
1750 case 'N':
1751 case 'n':
1752 {
1753 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001754 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001755 break;
1756 }
1757 case 'O':
1758 case 'o':
1759 {
1760 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001761 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001762 break;
1763 }
1764 case 'P':
1765 case 'p':
1766 {
1767 if (LocaleCompare(symbol,"page.height") == 0)
1768 return((MagickRealType) image->page.height);
1769 if (LocaleCompare(symbol,"page.width") == 0)
1770 return((MagickRealType) image->page.width);
1771 if (LocaleCompare(symbol,"page.x") == 0)
1772 return((MagickRealType) image->page.x);
1773 if (LocaleCompare(symbol,"page.y") == 0)
1774 return((MagickRealType) image->page.y);
1775 break;
1776 }
1777 case 'R':
1778 case 'r':
1779 {
1780 if (LocaleCompare(symbol,"resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001781 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001782 if (LocaleCompare(symbol,"resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001783 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001784 if (LocaleCompare(symbol,"r") == 0)
1785 return(QuantumScale*pixel.red);
1786 break;
1787 }
1788 case 'S':
1789 case 's':
1790 {
1791 if (LocaleCompare(symbol,"saturation") == 0)
1792 {
1793 double
1794 hue,
1795 lightness,
1796 saturation;
1797
cristyda1f9c12011-10-02 21:39:49 +00001798 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1799 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001800 return(saturation);
1801 }
1802 if (LocaleNCompare(symbol,"skewness",8) == 0)
1803 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1804 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1805 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1806 break;
1807 }
1808 case 'T':
1809 case 't':
1810 {
1811 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001812 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001813 break;
1814 }
1815 case 'W':
1816 case 'w':
1817 {
1818 if (LocaleCompare(symbol,"w") == 0)
1819 return((MagickRealType) image->columns);
1820 break;
1821 }
1822 case 'Y':
1823 case 'y':
1824 {
1825 if (LocaleCompare(symbol,"y") == 0)
1826 return(QuantumScale*pixel.green);
1827 break;
1828 }
1829 case 'Z':
1830 case 'z':
1831 {
1832 if (LocaleCompare(symbol,"z") == 0)
1833 {
1834 MagickRealType
1835 depth;
1836
cristyfefab1b2011-07-05 00:33:22 +00001837 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001838 return(depth);
1839 }
1840 break;
1841 }
1842 default:
1843 break;
1844 }
1845 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1846 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001847 return((MagickRealType) StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001848 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1849 "UnableToParseExpression","`%s'",symbol);
1850 return(0.0);
1851}
1852
1853static const char *FxOperatorPrecedence(const char *expression,
1854 ExceptionInfo *exception)
1855{
1856 typedef enum
1857 {
1858 UndefinedPrecedence,
1859 NullPrecedence,
1860 BitwiseComplementPrecedence,
1861 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001862 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001863 MultiplyPrecedence,
1864 AdditionPrecedence,
1865 ShiftPrecedence,
1866 RelationalPrecedence,
1867 EquivalencyPrecedence,
1868 BitwiseAndPrecedence,
1869 BitwiseOrPrecedence,
1870 LogicalAndPrecedence,
1871 LogicalOrPrecedence,
1872 TernaryPrecedence,
1873 AssignmentPrecedence,
1874 CommaPrecedence,
1875 SeparatorPrecedence
1876 } FxPrecedence;
1877
1878 FxPrecedence
1879 precedence,
1880 target;
1881
1882 register const char
1883 *subexpression;
1884
1885 register int
1886 c;
1887
cristybb503372010-05-27 20:51:26 +00001888 size_t
cristy3ed852e2009-09-05 21:47:34 +00001889 level;
1890
1891 c=0;
1892 level=0;
1893 subexpression=(const char *) NULL;
1894 target=NullPrecedence;
1895 while (*expression != '\0')
1896 {
1897 precedence=UndefinedPrecedence;
1898 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1899 {
1900 expression++;
1901 continue;
1902 }
cristy488fa882010-03-01 22:34:24 +00001903 switch (*expression)
1904 {
1905 case 'A':
1906 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001907 {
cristyb33454f2011-08-03 02:10:45 +00001908#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001909 if (LocaleNCompare(expression,"acosh",5) == 0)
1910 {
1911 expression+=5;
1912 break;
1913 }
cristyb33454f2011-08-03 02:10:45 +00001914#endif
1915#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001916 if (LocaleNCompare(expression,"asinh",5) == 0)
1917 {
1918 expression+=5;
1919 break;
1920 }
cristyb33454f2011-08-03 02:10:45 +00001921#endif
1922#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001923 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001924 {
1925 expression+=5;
1926 break;
1927 }
cristyb33454f2011-08-03 02:10:45 +00001928#endif
cristy488fa882010-03-01 22:34:24 +00001929 break;
cristy3ed852e2009-09-05 21:47:34 +00001930 }
cristy62d455f2011-11-03 11:42:28 +00001931 case 'E':
1932 case 'e':
1933 {
1934 if ((LocaleNCompare(expression,"E+",2) == 0) ||
1935 (LocaleNCompare(expression,"E-",2) == 0))
1936 {
1937 expression+=2; /* scientific notation */
1938 break;
1939 }
1940 }
cristy488fa882010-03-01 22:34:24 +00001941 case 'J':
1942 case 'j':
1943 {
1944 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1945 (LocaleNCompare(expression,"j1",2) == 0))
1946 {
1947 expression+=2;
1948 break;
1949 }
1950 break;
1951 }
cristy2def9322010-06-18 23:59:37 +00001952 case '#':
1953 {
1954 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1955 expression++;
1956 break;
1957 }
cristy488fa882010-03-01 22:34:24 +00001958 default:
1959 break;
1960 }
cristy3ed852e2009-09-05 21:47:34 +00001961 if ((c == (int) '{') || (c == (int) '['))
1962 level++;
1963 else
1964 if ((c == (int) '}') || (c == (int) ']'))
1965 level--;
1966 if (level == 0)
1967 switch ((unsigned char) *expression)
1968 {
1969 case '~':
1970 case '!':
1971 {
1972 precedence=BitwiseComplementPrecedence;
1973 break;
1974 }
1975 case '^':
cristy6621e252010-08-13 00:42:57 +00001976 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001977 {
1978 precedence=ExponentPrecedence;
1979 break;
1980 }
1981 default:
1982 {
1983 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1984 (strchr(")",c) != (char *) NULL))) &&
1985 (((islower((int) ((char) *expression)) != 0) ||
1986 (strchr("(",(int) *expression) != (char *) NULL)) ||
1987 ((isdigit((int) ((char) c)) == 0) &&
1988 (isdigit((int) ((char) *expression)) != 0))) &&
1989 (strchr("xy",(int) *expression) == (char *) NULL))
1990 precedence=MultiplyPrecedence;
1991 break;
1992 }
1993 case '*':
1994 case '/':
1995 case '%':
1996 {
1997 precedence=MultiplyPrecedence;
1998 break;
1999 }
2000 case '+':
2001 case '-':
2002 {
2003 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
2004 (isalpha(c) != 0))
2005 precedence=AdditionPrecedence;
2006 break;
2007 }
2008 case LeftShiftOperator:
2009 case RightShiftOperator:
2010 {
2011 precedence=ShiftPrecedence;
2012 break;
2013 }
2014 case '<':
2015 case LessThanEqualOperator:
2016 case GreaterThanEqualOperator:
2017 case '>':
2018 {
2019 precedence=RelationalPrecedence;
2020 break;
2021 }
2022 case EqualOperator:
2023 case NotEqualOperator:
2024 {
2025 precedence=EquivalencyPrecedence;
2026 break;
2027 }
2028 case '&':
2029 {
2030 precedence=BitwiseAndPrecedence;
2031 break;
2032 }
2033 case '|':
2034 {
2035 precedence=BitwiseOrPrecedence;
2036 break;
2037 }
2038 case LogicalAndOperator:
2039 {
2040 precedence=LogicalAndPrecedence;
2041 break;
2042 }
2043 case LogicalOrOperator:
2044 {
2045 precedence=LogicalOrPrecedence;
2046 break;
2047 }
cristy116af162010-08-13 01:25:47 +00002048 case ExponentialNotation:
2049 {
2050 precedence=ExponentialNotationPrecedence;
2051 break;
2052 }
cristy3ed852e2009-09-05 21:47:34 +00002053 case ':':
2054 case '?':
2055 {
2056 precedence=TernaryPrecedence;
2057 break;
2058 }
2059 case '=':
2060 {
2061 precedence=AssignmentPrecedence;
2062 break;
2063 }
2064 case ',':
2065 {
2066 precedence=CommaPrecedence;
2067 break;
2068 }
2069 case ';':
2070 {
2071 precedence=SeparatorPrecedence;
2072 break;
2073 }
2074 }
2075 if ((precedence == BitwiseComplementPrecedence) ||
2076 (precedence == TernaryPrecedence) ||
2077 (precedence == AssignmentPrecedence))
2078 {
2079 if (precedence > target)
2080 {
2081 /*
2082 Right-to-left associativity.
2083 */
2084 target=precedence;
2085 subexpression=expression;
2086 }
2087 }
2088 else
2089 if (precedence >= target)
2090 {
2091 /*
2092 Left-to-right associativity.
2093 */
2094 target=precedence;
2095 subexpression=expression;
2096 }
2097 if (strchr("(",(int) *expression) != (char *) NULL)
2098 expression=FxSubexpression(expression,exception);
2099 c=(int) (*expression++);
2100 }
2101 return(subexpression);
2102}
2103
2104static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002105 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002106 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002107{
2108 char
2109 *q,
2110 subexpression[MaxTextExtent];
2111
2112 MagickRealType
2113 alpha,
2114 gamma;
2115
2116 register const char
2117 *p;
2118
2119 *beta=0.0;
2120 if (exception->severity != UndefinedException)
2121 return(0.0);
2122 while (isspace((int) *expression) != 0)
2123 expression++;
2124 if (*expression == '\0')
2125 {
2126 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2127 "MissingExpression","`%s'",expression);
2128 return(0.0);
2129 }
cristy66322f02010-05-17 11:40:48 +00002130 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002131 p=FxOperatorPrecedence(expression,exception);
2132 if (p != (const char *) NULL)
2133 {
2134 (void) CopyMagickString(subexpression,expression,(size_t)
2135 (p-expression+1));
2136 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2137 exception);
2138 switch ((unsigned char) *p)
2139 {
2140 case '~':
2141 {
2142 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002143 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002144 return(*beta);
2145 }
2146 case '!':
2147 {
2148 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2149 return(*beta == 0.0 ? 1.0 : 0.0);
2150 }
2151 case '^':
2152 {
2153 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2154 channel,x,y,++p,beta,exception));
2155 return(*beta);
2156 }
2157 case '*':
cristy116af162010-08-13 01:25:47 +00002158 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002159 {
2160 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2161 return(alpha*(*beta));
2162 }
2163 case '/':
2164 {
2165 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2166 if (*beta == 0.0)
2167 {
2168 if (exception->severity == UndefinedException)
2169 (void) ThrowMagickException(exception,GetMagickModule(),
2170 OptionError,"DivideByZero","`%s'",expression);
2171 return(0.0);
2172 }
2173 return(alpha/(*beta));
2174 }
2175 case '%':
2176 {
2177 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2178 *beta=fabs(floor(((double) *beta)+0.5));
2179 if (*beta == 0.0)
2180 {
2181 (void) ThrowMagickException(exception,GetMagickModule(),
2182 OptionError,"DivideByZero","`%s'",expression);
2183 return(0.0);
2184 }
2185 return(fmod((double) alpha,(double) *beta));
2186 }
2187 case '+':
2188 {
2189 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2190 return(alpha+(*beta));
2191 }
2192 case '-':
2193 {
2194 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2195 return(alpha-(*beta));
2196 }
2197 case LeftShiftOperator:
2198 {
2199 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002200 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002201 return(*beta);
2202 }
2203 case RightShiftOperator:
2204 {
2205 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002206 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002207 return(*beta);
2208 }
2209 case '<':
2210 {
2211 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2212 return(alpha < *beta ? 1.0 : 0.0);
2213 }
2214 case LessThanEqualOperator:
2215 {
2216 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2217 return(alpha <= *beta ? 1.0 : 0.0);
2218 }
2219 case '>':
2220 {
2221 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2222 return(alpha > *beta ? 1.0 : 0.0);
2223 }
2224 case GreaterThanEqualOperator:
2225 {
2226 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2227 return(alpha >= *beta ? 1.0 : 0.0);
2228 }
2229 case EqualOperator:
2230 {
2231 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2232 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2233 }
2234 case NotEqualOperator:
2235 {
2236 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2237 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2238 }
2239 case '&':
2240 {
2241 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002242 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002243 return(*beta);
2244 }
2245 case '|':
2246 {
2247 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002248 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002249 return(*beta);
2250 }
2251 case LogicalAndOperator:
2252 {
2253 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2254 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2255 return(*beta);
2256 }
2257 case LogicalOrOperator:
2258 {
2259 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2260 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2261 return(*beta);
2262 }
2263 case '?':
2264 {
2265 MagickRealType
2266 gamma;
2267
2268 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2269 q=subexpression;
2270 p=StringToken(":",&q);
2271 if (q == (char *) NULL)
2272 {
2273 (void) ThrowMagickException(exception,GetMagickModule(),
2274 OptionError,"UnableToParseExpression","`%s'",subexpression);
2275 return(0.0);
2276 }
2277 if (fabs((double) alpha) > MagickEpsilon)
2278 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2279 else
2280 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2281 return(gamma);
2282 }
2283 case '=':
2284 {
2285 char
2286 numeric[MaxTextExtent];
2287
2288 q=subexpression;
2289 while (isalpha((int) ((unsigned char) *q)) != 0)
2290 q++;
2291 if (*q != '\0')
2292 {
2293 (void) ThrowMagickException(exception,GetMagickModule(),
2294 OptionError,"UnableToParseExpression","`%s'",subexpression);
2295 return(0.0);
2296 }
2297 ClearMagickException(exception);
2298 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002299 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002300 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002301 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2302 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2303 subexpression),ConstantString(numeric));
2304 return(*beta);
2305 }
2306 case ',':
2307 {
2308 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2309 return(alpha);
2310 }
2311 case ';':
2312 {
2313 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2314 return(*beta);
2315 }
2316 default:
2317 {
2318 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2319 exception);
2320 return(gamma);
2321 }
2322 }
2323 }
2324 if (strchr("(",(int) *expression) != (char *) NULL)
2325 {
2326 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2327 subexpression[strlen(subexpression)-1]='\0';
2328 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2329 exception);
2330 return(gamma);
2331 }
cristy8b8a3ae2010-10-23 18:49:46 +00002332 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002333 {
2334 case '+':
2335 {
2336 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2337 exception);
2338 return(1.0*gamma);
2339 }
2340 case '-':
2341 {
2342 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2343 exception);
2344 return(-1.0*gamma);
2345 }
2346 case '~':
2347 {
2348 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2349 exception);
cristybb503372010-05-27 20:51:26 +00002350 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002351 }
2352 case 'A':
2353 case 'a':
2354 {
2355 if (LocaleNCompare(expression,"abs",3) == 0)
2356 {
2357 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2358 exception);
2359 return((MagickRealType) fabs((double) alpha));
2360 }
cristyb33454f2011-08-03 02:10:45 +00002361#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002362 if (LocaleNCompare(expression,"acosh",5) == 0)
2363 {
2364 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2365 exception);
2366 return((MagickRealType) acosh((double) alpha));
2367 }
cristyb33454f2011-08-03 02:10:45 +00002368#endif
cristy3ed852e2009-09-05 21:47:34 +00002369 if (LocaleNCompare(expression,"acos",4) == 0)
2370 {
2371 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2372 exception);
2373 return((MagickRealType) acos((double) alpha));
2374 }
cristy43c22f42010-03-30 12:34:07 +00002375#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002376 if (LocaleNCompare(expression,"airy",4) == 0)
2377 {
2378 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2379 exception);
2380 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002381 return(1.0);
2382 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002383 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002384 }
cristy43c22f42010-03-30 12:34:07 +00002385#endif
cristyb33454f2011-08-03 02:10:45 +00002386#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002387 if (LocaleNCompare(expression,"asinh",5) == 0)
2388 {
2389 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2390 exception);
2391 return((MagickRealType) asinh((double) alpha));
2392 }
cristyb33454f2011-08-03 02:10:45 +00002393#endif
cristy3ed852e2009-09-05 21:47:34 +00002394 if (LocaleNCompare(expression,"asin",4) == 0)
2395 {
2396 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2397 exception);
2398 return((MagickRealType) asin((double) alpha));
2399 }
2400 if (LocaleNCompare(expression,"alt",3) == 0)
2401 {
2402 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2403 exception);
cristybb503372010-05-27 20:51:26 +00002404 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002405 }
2406 if (LocaleNCompare(expression,"atan2",5) == 0)
2407 {
2408 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2409 exception);
2410 return((MagickRealType) atan2((double) alpha,(double) *beta));
2411 }
cristyb33454f2011-08-03 02:10:45 +00002412#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002413 if (LocaleNCompare(expression,"atanh",5) == 0)
2414 {
2415 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2416 exception);
2417 return((MagickRealType) atanh((double) alpha));
2418 }
cristyb33454f2011-08-03 02:10:45 +00002419#endif
cristy3ed852e2009-09-05 21:47:34 +00002420 if (LocaleNCompare(expression,"atan",4) == 0)
2421 {
2422 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2423 exception);
2424 return((MagickRealType) atan((double) alpha));
2425 }
2426 if (LocaleCompare(expression,"a") == 0)
2427 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2428 break;
2429 }
2430 case 'B':
2431 case 'b':
2432 {
2433 if (LocaleCompare(expression,"b") == 0)
2434 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2435 break;
2436 }
2437 case 'C':
2438 case 'c':
2439 {
2440 if (LocaleNCompare(expression,"ceil",4) == 0)
2441 {
2442 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2443 exception);
2444 return((MagickRealType) ceil((double) alpha));
2445 }
2446 if (LocaleNCompare(expression,"cosh",4) == 0)
2447 {
2448 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2449 exception);
2450 return((MagickRealType) cosh((double) alpha));
2451 }
2452 if (LocaleNCompare(expression,"cos",3) == 0)
2453 {
2454 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2455 exception);
2456 return((MagickRealType) cos((double) alpha));
2457 }
2458 if (LocaleCompare(expression,"c") == 0)
2459 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2460 break;
2461 }
2462 case 'D':
2463 case 'd':
2464 {
2465 if (LocaleNCompare(expression,"debug",5) == 0)
2466 {
2467 const char
2468 *type;
2469
2470 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2471 exception);
2472 if (fx_info->images->colorspace == CMYKColorspace)
2473 switch (channel)
2474 {
cristy0568ffc2011-07-25 16:54:14 +00002475 case CyanPixelChannel: type="cyan"; break;
2476 case MagentaPixelChannel: type="magenta"; break;
2477 case YellowPixelChannel: type="yellow"; break;
2478 case AlphaPixelChannel: type="opacity"; break;
2479 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002480 default: type="unknown"; break;
2481 }
2482 else
2483 switch (channel)
2484 {
cristy0568ffc2011-07-25 16:54:14 +00002485 case RedPixelChannel: type="red"; break;
2486 case GreenPixelChannel: type="green"; break;
2487 case BluePixelChannel: type="blue"; break;
2488 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002489 default: type="unknown"; break;
2490 }
2491 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2492 if (strlen(subexpression) > 1)
2493 subexpression[strlen(subexpression)-1]='\0';
2494 if (fx_info->file != (FILE *) NULL)
cristy1707c6c2012-01-18 23:30:54 +00002495 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2496 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2497 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002498 return(0.0);
2499 }
cristy5597a8d2011-11-04 00:25:32 +00002500 if (LocaleNCompare(expression,"drc",3) == 0)
2501 {
2502 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2503 exception);
2504 return((MagickRealType) (alpha/(*beta*(alpha-1.0)+1.0)));
2505 }
cristy3ed852e2009-09-05 21:47:34 +00002506 break;
2507 }
2508 case 'E':
2509 case 'e':
2510 {
2511 if (LocaleCompare(expression,"epsilon") == 0)
2512 return((MagickRealType) MagickEpsilon);
2513 if (LocaleNCompare(expression,"exp",3) == 0)
2514 {
2515 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2516 exception);
2517 return((MagickRealType) exp((double) alpha));
2518 }
2519 if (LocaleCompare(expression,"e") == 0)
2520 return((MagickRealType) 2.7182818284590452354);
2521 break;
2522 }
2523 case 'F':
2524 case 'f':
2525 {
2526 if (LocaleNCompare(expression,"floor",5) == 0)
2527 {
2528 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2529 exception);
2530 return((MagickRealType) floor((double) alpha));
2531 }
2532 break;
2533 }
2534 case 'G':
2535 case 'g':
2536 {
cristy9eeedea2011-11-02 19:04:05 +00002537 if (LocaleNCompare(expression,"gauss",5) == 0)
2538 {
2539 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2540 exception);
2541 gamma=exp((double) (-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2542 return((MagickRealType) gamma);
2543 }
cristyb0aad4c2011-11-02 19:30:35 +00002544 if (LocaleNCompare(expression,"gcd",3) == 0)
2545 {
2546 MagickOffsetType
2547 gcd;
2548
2549 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2550 exception);
cristy1707c6c2012-01-18 23:30:54 +00002551 gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2552 0.5));
cristyb0aad4c2011-11-02 19:30:35 +00002553 return((MagickRealType) gcd);
2554 }
cristy3ed852e2009-09-05 21:47:34 +00002555 if (LocaleCompare(expression,"g") == 0)
2556 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2557 break;
2558 }
2559 case 'H':
2560 case 'h':
2561 {
2562 if (LocaleCompare(expression,"h") == 0)
2563 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2564 if (LocaleCompare(expression,"hue") == 0)
2565 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2566 if (LocaleNCompare(expression,"hypot",5) == 0)
2567 {
2568 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2569 exception);
2570 return((MagickRealType) hypot((double) alpha,(double) *beta));
2571 }
2572 break;
2573 }
2574 case 'K':
2575 case 'k':
2576 {
2577 if (LocaleCompare(expression,"k") == 0)
2578 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2579 break;
2580 }
2581 case 'I':
2582 case 'i':
2583 {
2584 if (LocaleCompare(expression,"intensity") == 0)
2585 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2586 if (LocaleNCompare(expression,"int",3) == 0)
2587 {
2588 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2589 exception);
cristy16788e42010-08-13 13:44:26 +00002590 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002591 }
cristy82b20722011-11-05 21:52:36 +00002592#if defined(MAGICKCORE_HAVE_ISNAN)
cristy639399c2011-11-02 19:16:15 +00002593 if (LocaleNCompare(expression,"isnan",5) == 0)
2594 {
2595 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2596 exception);
cristy17a10202011-11-02 19:17:04 +00002597 return((MagickRealType) !!isnan((double) alpha));
cristy639399c2011-11-02 19:16:15 +00002598 }
cristy82b20722011-11-05 21:52:36 +00002599#endif
cristy3ed852e2009-09-05 21:47:34 +00002600 if (LocaleCompare(expression,"i") == 0)
2601 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2602 break;
2603 }
2604 case 'J':
2605 case 'j':
2606 {
2607 if (LocaleCompare(expression,"j") == 0)
2608 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002609#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002610 if (LocaleNCompare(expression,"j0",2) == 0)
2611 {
2612 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2613 exception);
2614 return((MagickRealType) j0((double) alpha));
2615 }
cristy161b9262010-03-20 19:34:32 +00002616#endif
2617#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002618 if (LocaleNCompare(expression,"j1",2) == 0)
2619 {
2620 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2621 exception);
2622 return((MagickRealType) j1((double) alpha));
2623 }
cristy161b9262010-03-20 19:34:32 +00002624#endif
cristyaa018fa2010-04-08 23:03:54 +00002625#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002626 if (LocaleNCompare(expression,"jinc",4) == 0)
2627 {
2628 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2629 exception);
cristy0946a822010-03-12 17:14:58 +00002630 if (alpha == 0.0)
2631 return(1.0);
cristy1707c6c2012-01-18 23:30:54 +00002632 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/(MagickPI*
2633 alpha));
cristyfce2f7b2010-03-12 00:29:49 +00002634 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002635 }
cristyaa018fa2010-04-08 23:03:54 +00002636#endif
cristy3ed852e2009-09-05 21:47:34 +00002637 break;
2638 }
2639 case 'L':
2640 case 'l':
2641 {
2642 if (LocaleNCompare(expression,"ln",2) == 0)
2643 {
2644 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2645 exception);
2646 return((MagickRealType) log((double) alpha));
2647 }
cristyc8ed5322010-08-31 12:07:59 +00002648 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002649 {
cristyc8ed5322010-08-31 12:07:59 +00002650 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002651 exception);
2652 return((MagickRealType) log10((double) alpha))/log10(2.0);
2653 }
2654 if (LocaleNCompare(expression,"log",3) == 0)
2655 {
2656 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2657 exception);
2658 return((MagickRealType) log10((double) alpha));
2659 }
2660 if (LocaleCompare(expression,"lightness") == 0)
2661 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2662 break;
2663 }
2664 case 'M':
2665 case 'm':
2666 {
2667 if (LocaleCompare(expression,"MaxRGB") == 0)
2668 return((MagickRealType) QuantumRange);
2669 if (LocaleNCompare(expression,"maxima",6) == 0)
2670 break;
2671 if (LocaleNCompare(expression,"max",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002672 {
2673 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2674 exception);
2675 return(alpha > *beta ? alpha : *beta);
2676 }
cristy3ed852e2009-09-05 21:47:34 +00002677 if (LocaleNCompare(expression,"minima",6) == 0)
2678 break;
2679 if (LocaleNCompare(expression,"min",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002680 {
2681 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2682 exception);
2683 return(alpha < *beta ? alpha : *beta);
2684 }
cristy3ed852e2009-09-05 21:47:34 +00002685 if (LocaleNCompare(expression,"mod",3) == 0)
2686 {
2687 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2688 exception);
cristy984049c2011-11-03 18:34:58 +00002689 gamma=alpha-floor((double) (alpha/(*beta)))*(*beta);
2690 return(gamma);
cristy3ed852e2009-09-05 21:47:34 +00002691 }
2692 if (LocaleCompare(expression,"m") == 0)
2693 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2694 break;
2695 }
2696 case 'N':
2697 case 'n':
2698 {
cristyad3502e2011-11-02 19:10:45 +00002699 if (LocaleNCompare(expression,"not",3) == 0)
2700 {
2701 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2702 exception);
2703 return((MagickRealType) (alpha < MagickEpsilon));
2704 }
cristy3ed852e2009-09-05 21:47:34 +00002705 if (LocaleCompare(expression,"n") == 0)
2706 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2707 break;
2708 }
2709 case 'O':
2710 case 'o':
2711 {
2712 if (LocaleCompare(expression,"Opaque") == 0)
2713 return(1.0);
2714 if (LocaleCompare(expression,"o") == 0)
2715 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2716 break;
2717 }
2718 case 'P':
2719 case 'p':
2720 {
cristy670aa3c2011-11-03 00:54:00 +00002721 if (LocaleCompare(expression,"phi") == 0)
2722 return((MagickRealType) MagickPHI);
cristy3ed852e2009-09-05 21:47:34 +00002723 if (LocaleCompare(expression,"pi") == 0)
2724 return((MagickRealType) MagickPI);
2725 if (LocaleNCompare(expression,"pow",3) == 0)
2726 {
2727 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2728 exception);
2729 return((MagickRealType) pow((double) alpha,(double) *beta));
2730 }
2731 if (LocaleCompare(expression,"p") == 0)
2732 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2733 break;
2734 }
2735 case 'Q':
2736 case 'q':
2737 {
2738 if (LocaleCompare(expression,"QuantumRange") == 0)
2739 return((MagickRealType) QuantumRange);
2740 if (LocaleCompare(expression,"QuantumScale") == 0)
2741 return((MagickRealType) QuantumScale);
2742 break;
2743 }
2744 case 'R':
2745 case 'r':
2746 {
2747 if (LocaleNCompare(expression,"rand",4) == 0)
2748 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2749 if (LocaleNCompare(expression,"round",5) == 0)
2750 {
2751 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2752 exception);
cristy16788e42010-08-13 13:44:26 +00002753 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002754 }
2755 if (LocaleCompare(expression,"r") == 0)
2756 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2757 break;
2758 }
2759 case 'S':
2760 case 's':
2761 {
2762 if (LocaleCompare(expression,"saturation") == 0)
2763 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2764 if (LocaleNCompare(expression,"sign",4) == 0)
2765 {
2766 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2767 exception);
2768 return(alpha < 0.0 ? -1.0 : 1.0);
2769 }
cristya6a09e72010-03-02 14:51:02 +00002770 if (LocaleNCompare(expression,"sinc",4) == 0)
2771 {
2772 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2773 exception);
2774 if (alpha == 0)
2775 return(1.0);
2776 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2777 (MagickPI*alpha));
2778 return(gamma);
2779 }
cristy3ed852e2009-09-05 21:47:34 +00002780 if (LocaleNCompare(expression,"sinh",4) == 0)
2781 {
2782 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2783 exception);
2784 return((MagickRealType) sinh((double) alpha));
2785 }
2786 if (LocaleNCompare(expression,"sin",3) == 0)
2787 {
2788 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2789 exception);
2790 return((MagickRealType) sin((double) alpha));
2791 }
2792 if (LocaleNCompare(expression,"sqrt",4) == 0)
2793 {
2794 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2795 exception);
2796 return((MagickRealType) sqrt((double) alpha));
2797 }
cristy9eeedea2011-11-02 19:04:05 +00002798 if (LocaleNCompare(expression,"squish",6) == 0)
2799 {
2800 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2801 exception);
2802 return((MagickRealType) (1.0/(1.0+exp((double) (4.0*alpha)))));
2803 }
cristy3ed852e2009-09-05 21:47:34 +00002804 if (LocaleCompare(expression,"s") == 0)
2805 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2806 break;
2807 }
2808 case 'T':
2809 case 't':
2810 {
2811 if (LocaleNCompare(expression,"tanh",4) == 0)
2812 {
2813 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2814 exception);
2815 return((MagickRealType) tanh((double) alpha));
2816 }
2817 if (LocaleNCompare(expression,"tan",3) == 0)
2818 {
2819 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2820 exception);
2821 return((MagickRealType) tan((double) alpha));
2822 }
2823 if (LocaleCompare(expression,"Transparent") == 0)
2824 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002825 if (LocaleNCompare(expression,"trunc",5) == 0)
2826 {
2827 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2828 exception);
2829 if (alpha >= 0.0)
2830 return((MagickRealType) floor((double) alpha));
2831 return((MagickRealType) ceil((double) alpha));
2832 }
cristy3ed852e2009-09-05 21:47:34 +00002833 if (LocaleCompare(expression,"t") == 0)
2834 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2835 break;
2836 }
2837 case 'U':
2838 case 'u':
2839 {
2840 if (LocaleCompare(expression,"u") == 0)
2841 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2842 break;
2843 }
2844 case 'V':
2845 case 'v':
2846 {
2847 if (LocaleCompare(expression,"v") == 0)
2848 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2849 break;
2850 }
2851 case 'W':
2852 case 'w':
2853 {
cristy9eeedea2011-11-02 19:04:05 +00002854 if (LocaleNCompare(expression,"while",5) == 0)
2855 {
2856 do
2857 {
2858 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2859 exception);
2860 } while (fabs((double) alpha) >= MagickEpsilon);
2861 return((MagickRealType) *beta);
2862 }
cristy3ed852e2009-09-05 21:47:34 +00002863 if (LocaleCompare(expression,"w") == 0)
2864 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2865 break;
2866 }
2867 case 'Y':
2868 case 'y':
2869 {
2870 if (LocaleCompare(expression,"y") == 0)
2871 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2872 break;
2873 }
2874 case 'Z':
2875 case 'z':
2876 {
2877 if (LocaleCompare(expression,"z") == 0)
2878 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2879 break;
2880 }
2881 default:
2882 break;
2883 }
2884 q=(char *) expression;
cristydbdd0e32011-11-04 23:29:40 +00002885 alpha=InterpretSiPrefixValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002886 if (q == expression)
2887 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2888 return(alpha);
2889}
2890
cristy7832dc22011-09-05 01:21:53 +00002891MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
cristy3ed852e2009-09-05 21:47:34 +00002892 MagickRealType *alpha,ExceptionInfo *exception)
2893{
2894 MagickBooleanType
2895 status;
2896
cristy541ae572011-08-05 19:08:59 +00002897 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2898 exception);
cristy3ed852e2009-09-05 21:47:34 +00002899 return(status);
2900}
2901
2902MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2903 MagickRealType *alpha,ExceptionInfo *exception)
2904{
2905 FILE
2906 *file;
2907
2908 MagickBooleanType
2909 status;
2910
2911 file=fx_info->file;
2912 fx_info->file=(FILE *) NULL;
cristy541ae572011-08-05 19:08:59 +00002913 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2914 exception);
cristy3ed852e2009-09-05 21:47:34 +00002915 fx_info->file=file;
2916 return(status);
2917}
2918
cristy7832dc22011-09-05 01:21:53 +00002919MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002920 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002921 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002922{
2923 MagickRealType
2924 beta;
2925
2926 beta=0.0;
2927 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2928 exception);
2929 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2930}
2931
2932/*
2933%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2934% %
2935% %
2936% %
2937% F x I m a g e %
2938% %
2939% %
2940% %
2941%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2942%
2943% FxImage() applies a mathematical expression to the specified image.
2944%
2945% The format of the FxImage method is:
2946%
2947% Image *FxImage(const Image *image,const char *expression,
2948% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002949%
2950% A description of each parameter follows:
2951%
2952% o image: the image.
2953%
cristy3ed852e2009-09-05 21:47:34 +00002954% o expression: A mathematical expression.
2955%
2956% o exception: return any errors or warnings in this structure.
2957%
2958*/
2959
2960static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2961{
cristybb503372010-05-27 20:51:26 +00002962 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002963 i;
2964
2965 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002966 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002967 if (fx_info[i] != (FxInfo *) NULL)
2968 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002969 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002970 return(fx_info);
2971}
2972
2973static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2974 ExceptionInfo *exception)
2975{
2976 char
2977 *fx_expression;
2978
2979 FxInfo
2980 **fx_info;
2981
2982 MagickRealType
2983 alpha;
2984
cristybb503372010-05-27 20:51:26 +00002985 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002986 i;
2987
cristybb503372010-05-27 20:51:26 +00002988 size_t
cristy3ed852e2009-09-05 21:47:34 +00002989 number_threads;
2990
2991 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002992 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002993 if (fx_info == (FxInfo **) NULL)
2994 return((FxInfo **) NULL);
2995 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2996 if (*expression != '@')
2997 fx_expression=ConstantString(expression);
2998 else
2999 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00003000 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00003001 {
3002 fx_info[i]=AcquireFxInfo(image,fx_expression);
3003 if (fx_info[i] == (FxInfo *) NULL)
3004 return(DestroyFxThreadSet(fx_info));
3005 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
3006 }
3007 fx_expression=DestroyString(fx_expression);
3008 return(fx_info);
3009}
3010
3011MagickExport Image *FxImage(const Image *image,const char *expression,
3012 ExceptionInfo *exception)
3013{
cristy3ed852e2009-09-05 21:47:34 +00003014#define FxImageTag "Fx/Image"
3015
cristyfa112112010-01-04 17:48:07 +00003016 CacheView
cristy79cedc72011-07-25 00:41:15 +00003017 *fx_view,
3018 *image_view;
cristyfa112112010-01-04 17:48:07 +00003019
cristy3ed852e2009-09-05 21:47:34 +00003020 FxInfo
cristyfa112112010-01-04 17:48:07 +00003021 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00003022
3023 Image
3024 *fx_image;
3025
cristy3ed852e2009-09-05 21:47:34 +00003026 MagickBooleanType
3027 status;
3028
cristybb503372010-05-27 20:51:26 +00003029 MagickOffsetType
3030 progress;
3031
cristy3ed852e2009-09-05 21:47:34 +00003032 MagickRealType
3033 alpha;
3034
cristybb503372010-05-27 20:51:26 +00003035 ssize_t
3036 y;
3037
cristy3ed852e2009-09-05 21:47:34 +00003038 assert(image != (Image *) NULL);
3039 assert(image->signature == MagickSignature);
3040 if (image->debug != MagickFalse)
3041 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00003042 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003043 if (fx_image == (Image *) NULL)
3044 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003045 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003046 {
cristy3ed852e2009-09-05 21:47:34 +00003047 fx_image=DestroyImage(fx_image);
3048 return((Image *) NULL);
3049 }
3050 fx_info=AcquireFxThreadSet(image,expression,exception);
3051 if (fx_info == (FxInfo **) NULL)
3052 {
3053 fx_image=DestroyImage(fx_image);
3054 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3055 }
3056 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3057 if (status == MagickFalse)
3058 {
3059 fx_image=DestroyImage(fx_image);
3060 fx_info=DestroyFxThreadSet(fx_info);
3061 return((Image *) NULL);
3062 }
3063 /*
3064 Fx image.
3065 */
3066 status=MagickTrue;
3067 progress=0;
cristy79cedc72011-07-25 00:41:15 +00003068 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003069 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003070#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003071 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003072#endif
cristybb503372010-05-27 20:51:26 +00003073 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003074 {
cristy5c9e6f22010-09-17 17:31:01 +00003075 const int
3076 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003077
cristy79cedc72011-07-25 00:41:15 +00003078 register const Quantum
3079 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003080
cristy4c08aed2011-07-01 19:47:50 +00003081 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003082 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003083
cristy79cedc72011-07-25 00:41:15 +00003084 register ssize_t
3085 x;
3086
cristy3ed852e2009-09-05 21:47:34 +00003087 if (status == MagickFalse)
3088 continue;
cristy79cedc72011-07-25 00:41:15 +00003089 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003090 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003091 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003092 {
3093 status=MagickFalse;
3094 continue;
3095 }
cristybb503372010-05-27 20:51:26 +00003096 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003097 {
cristy79cedc72011-07-25 00:41:15 +00003098 register ssize_t
3099 i;
3100
3101 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3102 {
3103 MagickRealType
3104 alpha;
3105
3106 PixelChannel
3107 channel;
3108
3109 PixelTrait
3110 fx_traits,
3111 traits;
3112
cristye2a912b2011-12-05 20:02:07 +00003113 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003114 traits=GetPixelChannelMapTraits(image,channel);
cristy79cedc72011-07-25 00:41:15 +00003115 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
cristy010d7d12011-08-31 01:02:48 +00003116 if ((traits == UndefinedPixelTrait) ||
3117 (fx_traits == UndefinedPixelTrait))
cristy79cedc72011-07-25 00:41:15 +00003118 continue;
cristyd09f8802012-02-04 16:44:10 +00003119 if (((fx_traits & CopyPixelTrait) != 0) ||
3120 (GetPixelMask(image,p) != 0))
cristy79cedc72011-07-25 00:41:15 +00003121 {
cristy0beccfa2011-09-25 20:47:53 +00003122 SetPixelChannel(fx_image,channel,p[i],q);
cristy79cedc72011-07-25 00:41:15 +00003123 continue;
3124 }
3125 alpha=0.0;
cristya382aca2011-12-06 18:22:48 +00003126 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3127 exception);
cristyb3a73b52011-07-26 01:34:43 +00003128 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003129 }
3130 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003131 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003132 }
3133 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3134 status=MagickFalse;
3135 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3136 {
3137 MagickBooleanType
3138 proceed;
3139
cristyb5d5f722009-11-04 03:03:49 +00003140#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003141 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003142#endif
3143 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3144 if (proceed == MagickFalse)
3145 status=MagickFalse;
3146 }
3147 }
cristy3ed852e2009-09-05 21:47:34 +00003148 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003149 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003150 fx_info=DestroyFxThreadSet(fx_info);
3151 if (status == MagickFalse)
3152 fx_image=DestroyImage(fx_image);
3153 return(fx_image);
3154}
3155
3156/*
3157%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3158% %
3159% %
3160% %
3161% I m p l o d e I m a g e %
3162% %
3163% %
3164% %
3165%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3166%
3167% ImplodeImage() creates a new image that is a copy of an existing
3168% one with the image pixels "implode" by the specified percentage. It
3169% allocates the memory necessary for the new Image structure and returns a
3170% pointer to the new image.
3171%
3172% The format of the ImplodeImage method is:
3173%
3174% Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003175% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003176%
3177% A description of each parameter follows:
3178%
3179% o implode_image: Method ImplodeImage returns a pointer to the image
3180% after it is implode. A null image is returned if there is a memory
3181% shortage.
3182%
3183% o image: the image.
3184%
3185% o amount: Define the extent of the implosion.
3186%
cristy76f512e2011-09-12 01:26:56 +00003187% o method: the pixel interpolation method.
3188%
cristy3ed852e2009-09-05 21:47:34 +00003189% o exception: return any errors or warnings in this structure.
3190%
3191*/
3192MagickExport Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003193 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003194{
3195#define ImplodeImageTag "Implode/Image"
3196
cristyfa112112010-01-04 17:48:07 +00003197 CacheView
3198 *image_view,
3199 *implode_view;
3200
cristy3ed852e2009-09-05 21:47:34 +00003201 Image
3202 *implode_image;
3203
cristy3ed852e2009-09-05 21:47:34 +00003204 MagickBooleanType
3205 status;
3206
cristybb503372010-05-27 20:51:26 +00003207 MagickOffsetType
3208 progress;
3209
cristy3ed852e2009-09-05 21:47:34 +00003210 MagickRealType
3211 radius;
3212
3213 PointInfo
3214 center,
3215 scale;
3216
cristybb503372010-05-27 20:51:26 +00003217 ssize_t
3218 y;
3219
cristy3ed852e2009-09-05 21:47:34 +00003220 /*
3221 Initialize implode image attributes.
3222 */
3223 assert(image != (Image *) NULL);
3224 assert(image->signature == MagickSignature);
3225 if (image->debug != MagickFalse)
3226 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3227 assert(exception != (ExceptionInfo *) NULL);
3228 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00003229 implode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3230 exception);
cristy3ed852e2009-09-05 21:47:34 +00003231 if (implode_image == (Image *) NULL)
3232 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003233 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003234 {
cristy3ed852e2009-09-05 21:47:34 +00003235 implode_image=DestroyImage(implode_image);
3236 return((Image *) NULL);
3237 }
cristy4c08aed2011-07-01 19:47:50 +00003238 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003239 implode_image->matte=MagickTrue;
3240 /*
3241 Compute scaling factor.
3242 */
3243 scale.x=1.0;
3244 scale.y=1.0;
3245 center.x=0.5*image->columns;
3246 center.y=0.5*image->rows;
3247 radius=center.x;
3248 if (image->columns > image->rows)
3249 scale.y=(double) image->columns/(double) image->rows;
3250 else
3251 if (image->columns < image->rows)
3252 {
3253 scale.x=(double) image->rows/(double) image->columns;
3254 radius=center.y;
3255 }
3256 /*
3257 Implode image.
3258 */
3259 status=MagickTrue;
3260 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003261 image_view=AcquireCacheView(image);
3262 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003263#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003264 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003265#endif
cristybb503372010-05-27 20:51:26 +00003266 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003267 {
cristy3ed852e2009-09-05 21:47:34 +00003268 MagickRealType
3269 distance;
3270
3271 PointInfo
3272 delta;
3273
cristy6d188022011-09-12 13:23:33 +00003274 register const Quantum
3275 *restrict p;
3276
cristybb503372010-05-27 20:51:26 +00003277 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003278 x;
3279
cristy4c08aed2011-07-01 19:47:50 +00003280 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003281 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003282
3283 if (status == MagickFalse)
3284 continue;
cristy6d188022011-09-12 13:23:33 +00003285 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003286 q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003287 exception);
cristy6d188022011-09-12 13:23:33 +00003288 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003289 {
3290 status=MagickFalse;
3291 continue;
3292 }
cristy3ed852e2009-09-05 21:47:34 +00003293 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00003294 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003295 {
cristy6d188022011-09-12 13:23:33 +00003296 register ssize_t
3297 i;
3298
cristy3ed852e2009-09-05 21:47:34 +00003299 /*
3300 Determine if the pixel is within an ellipse.
3301 */
cristy10a6c612012-01-29 21:41:05 +00003302 if (GetPixelMask(image,p) != 0)
3303 {
3304 p+=GetPixelChannels(image);
3305 q+=GetPixelChannels(implode_image);
3306 continue;
3307 }
cristy3ed852e2009-09-05 21:47:34 +00003308 delta.x=scale.x*(double) (x-center.x);
3309 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00003310 if (distance >= (radius*radius))
cristya6d7a9b2012-01-18 20:04:48 +00003311 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy6d188022011-09-12 13:23:33 +00003312 {
cristya6d7a9b2012-01-18 20:04:48 +00003313 PixelChannel
3314 channel;
3315
cristy1707c6c2012-01-18 23:30:54 +00003316 PixelTrait
3317 implode_traits,
3318 traits;
3319
cristya6d7a9b2012-01-18 20:04:48 +00003320 channel=GetPixelChannelMapChannel(image,i);
cristy1707c6c2012-01-18 23:30:54 +00003321 traits=GetPixelChannelMapTraits(image,channel);
3322 implode_traits=GetPixelChannelMapTraits(implode_image,channel);
3323 if ((traits == UndefinedPixelTrait) ||
3324 (implode_traits == UndefinedPixelTrait))
3325 continue;
cristya6d7a9b2012-01-18 20:04:48 +00003326 SetPixelChannel(implode_image,channel,p[i],q);
cristy6d188022011-09-12 13:23:33 +00003327 }
3328 else
cristy3ed852e2009-09-05 21:47:34 +00003329 {
3330 double
3331 factor;
3332
3333 /*
3334 Implode the pixel.
3335 */
3336 factor=1.0;
3337 if (distance > 0.0)
cristy1707c6c2012-01-18 23:30:54 +00003338 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/radius/
3339 2)),-amount);
cristy76f512e2011-09-12 01:26:56 +00003340 status=InterpolatePixelChannels(image,image_view,implode_image,method,
3341 (double) (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3342 scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00003343 }
cristy6d188022011-09-12 13:23:33 +00003344 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003345 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003346 }
3347 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3348 status=MagickFalse;
3349 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3350 {
3351 MagickBooleanType
3352 proceed;
3353
cristyb5d5f722009-11-04 03:03:49 +00003354#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003355 #pragma omp critical (MagickCore_ImplodeImage)
cristy3ed852e2009-09-05 21:47:34 +00003356#endif
3357 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3358 if (proceed == MagickFalse)
3359 status=MagickFalse;
3360 }
3361 }
3362 implode_view=DestroyCacheView(implode_view);
3363 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003364 if (status == MagickFalse)
3365 implode_image=DestroyImage(implode_image);
3366 return(implode_image);
3367}
3368
3369/*
3370%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3371% %
3372% %
3373% %
3374% M o r p h I m a g e s %
3375% %
3376% %
3377% %
3378%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3379%
3380% The MorphImages() method requires a minimum of two images. The first
3381% image is transformed into the second by a number of intervening images
3382% as specified by frames.
3383%
3384% The format of the MorphImage method is:
3385%
cristybb503372010-05-27 20:51:26 +00003386% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003387% ExceptionInfo *exception)
3388%
3389% A description of each parameter follows:
3390%
3391% o image: the image.
3392%
3393% o number_frames: Define the number of in-between image to generate.
3394% The more in-between frames, the smoother the morph.
3395%
3396% o exception: return any errors or warnings in this structure.
3397%
3398*/
3399MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003400 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003401{
3402#define MorphImageTag "Morph/Image"
3403
3404 Image
3405 *morph_image,
3406 *morph_images;
3407
cristy9d314ff2011-03-09 01:30:28 +00003408 MagickBooleanType
3409 status;
cristy3ed852e2009-09-05 21:47:34 +00003410
3411 MagickOffsetType
3412 scene;
3413
3414 MagickRealType
3415 alpha,
3416 beta;
3417
3418 register const Image
3419 *next;
3420
cristybb503372010-05-27 20:51:26 +00003421 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003422 i;
3423
cristy9d314ff2011-03-09 01:30:28 +00003424 ssize_t
3425 y;
cristy3ed852e2009-09-05 21:47:34 +00003426
3427 /*
3428 Clone first frame in sequence.
3429 */
3430 assert(image != (Image *) NULL);
3431 assert(image->signature == MagickSignature);
3432 if (image->debug != MagickFalse)
3433 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3434 assert(exception != (ExceptionInfo *) NULL);
3435 assert(exception->signature == MagickSignature);
3436 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3437 if (morph_images == (Image *) NULL)
3438 return((Image *) NULL);
3439 if (GetNextImageInList(image) == (Image *) NULL)
3440 {
3441 /*
3442 Morph single image.
3443 */
cristybb503372010-05-27 20:51:26 +00003444 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003445 {
3446 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3447 if (morph_image == (Image *) NULL)
3448 {
3449 morph_images=DestroyImageList(morph_images);
3450 return((Image *) NULL);
3451 }
3452 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003453 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003454 {
cristy8b27a6d2010-02-14 03:31:15 +00003455 MagickBooleanType
3456 proceed;
3457
cristybb503372010-05-27 20:51:26 +00003458 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3459 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003460 if (proceed == MagickFalse)
3461 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003462 }
3463 }
3464 return(GetFirstImageInList(morph_images));
3465 }
3466 /*
3467 Morph image sequence.
3468 */
3469 status=MagickTrue;
3470 scene=0;
3471 next=image;
3472 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3473 {
cristybb503372010-05-27 20:51:26 +00003474 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003475 {
3476 CacheView
3477 *image_view,
3478 *morph_view;
3479
3480 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3481 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003482 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristyaa2c16c2012-03-25 22:21:35 +00003483 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*next->rows+beta*
3484 GetNextImageInList(next)->rows+0.5),next->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00003485 if (morph_image == (Image *) NULL)
3486 {
3487 morph_images=DestroyImageList(morph_images);
3488 return((Image *) NULL);
3489 }
cristy1707c6c2012-01-18 23:30:54 +00003490 status=SetImageStorageClass(morph_image,DirectClass,exception);
3491 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003492 {
cristy3ed852e2009-09-05 21:47:34 +00003493 morph_image=DestroyImage(morph_image);
3494 return((Image *) NULL);
3495 }
3496 AppendImageToList(&morph_images,morph_image);
3497 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003498 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
cristyaa2c16c2012-03-25 22:21:35 +00003499 morph_images->rows,GetNextImageInList(next)->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00003500 if (morph_image == (Image *) NULL)
3501 {
3502 morph_images=DestroyImageList(morph_images);
3503 return((Image *) NULL);
3504 }
3505 image_view=AcquireCacheView(morph_image);
3506 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003507#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003508 #pragma omp parallel for schedule(static,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003509#endif
cristybb503372010-05-27 20:51:26 +00003510 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003511 {
3512 MagickBooleanType
3513 sync;
3514
cristy4c08aed2011-07-01 19:47:50 +00003515 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003516 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003517
cristybb503372010-05-27 20:51:26 +00003518 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003519 x;
3520
cristy4c08aed2011-07-01 19:47:50 +00003521 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003522 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003523
3524 if (status == MagickFalse)
3525 continue;
3526 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3527 exception);
3528 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3529 exception);
cristy4c08aed2011-07-01 19:47:50 +00003530 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003531 {
3532 status=MagickFalse;
3533 continue;
3534 }
cristybb503372010-05-27 20:51:26 +00003535 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003536 {
cristy1707c6c2012-01-18 23:30:54 +00003537 register ssize_t
3538 i;
3539
cristy10a6c612012-01-29 21:41:05 +00003540 for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
cristy1707c6c2012-01-18 23:30:54 +00003541 {
3542 PixelChannel
3543 channel;
3544
3545 PixelTrait
3546 morph_traits,
3547 traits;
3548
3549 channel=GetPixelChannelMapChannel(image,i);
3550 traits=GetPixelChannelMapTraits(image,channel);
3551 morph_traits=GetPixelChannelMapTraits(morph_image,channel);
3552 if ((traits == UndefinedPixelTrait) ||
3553 (morph_traits == UndefinedPixelTrait))
3554 continue;
cristyd09f8802012-02-04 16:44:10 +00003555 if (((morph_traits & CopyPixelTrait) != 0) ||
3556 (GetPixelMask(image,p) != 0))
cristy1707c6c2012-01-18 23:30:54 +00003557 {
3558 SetPixelChannel(morph_image,channel,p[i],q);
3559 continue;
3560 }
3561 SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3562 GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3563 }
cristyed231572011-07-14 02:18:59 +00003564 p+=GetPixelChannels(morph_image);
3565 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003566 }
3567 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3568 if (sync == MagickFalse)
3569 status=MagickFalse;
3570 }
3571 morph_view=DestroyCacheView(morph_view);
3572 image_view=DestroyCacheView(image_view);
3573 morph_image=DestroyImage(morph_image);
3574 }
cristybb503372010-05-27 20:51:26 +00003575 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003576 break;
3577 /*
3578 Clone last frame in sequence.
3579 */
3580 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3581 if (morph_image == (Image *) NULL)
3582 {
3583 morph_images=DestroyImageList(morph_images);
3584 return((Image *) NULL);
3585 }
3586 AppendImageToList(&morph_images,morph_image);
3587 morph_images=GetLastImageInList(morph_images);
3588 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3589 {
3590 MagickBooleanType
3591 proceed;
3592
cristyb5d5f722009-11-04 03:03:49 +00003593#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003594 #pragma omp critical (MagickCore_MorphImages)
cristy3ed852e2009-09-05 21:47:34 +00003595#endif
3596 proceed=SetImageProgress(image,MorphImageTag,scene,
3597 GetImageListLength(image));
3598 if (proceed == MagickFalse)
3599 status=MagickFalse;
3600 }
3601 scene++;
3602 }
3603 if (GetNextImageInList(next) != (Image *) NULL)
3604 {
3605 morph_images=DestroyImageList(morph_images);
3606 return((Image *) NULL);
3607 }
3608 return(GetFirstImageInList(morph_images));
3609}
3610
3611/*
3612%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3613% %
3614% %
3615% %
3616% P l a s m a I m a g e %
3617% %
3618% %
3619% %
3620%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3621%
3622% PlasmaImage() initializes an image with plasma fractal values. The image
3623% must be initialized with a base color and the random number generator
3624% seeded before this method is called.
3625%
3626% The format of the PlasmaImage method is:
3627%
3628% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristy5cbc0162011-08-29 00:36:28 +00003629% size_t attenuate,size_t depth,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003630%
3631% A description of each parameter follows:
3632%
3633% o image: the image.
3634%
3635% o segment: Define the region to apply plasma fractals values.
3636%
glennrp7dae1ca2010-09-16 12:17:35 +00003637% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003638%
3639% o depth: Limit the plasma recursion depth.
3640%
cristy5cbc0162011-08-29 00:36:28 +00003641% o exception: return any errors or warnings in this structure.
3642%
cristy3ed852e2009-09-05 21:47:34 +00003643*/
3644
3645static inline Quantum PlasmaPixel(RandomInfo *random_info,
3646 const MagickRealType pixel,const MagickRealType noise)
3647{
3648 Quantum
3649 plasma;
3650
cristyce70c172010-01-07 17:15:30 +00003651 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003652 noise/2.0);
3653 return(plasma);
3654}
3655
cristyda1f9c12011-10-02 21:39:49 +00003656static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
3657 CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3658 const SegmentInfo *segment,size_t attenuate,size_t depth,
3659 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003660{
cristy3ed852e2009-09-05 21:47:34 +00003661 MagickRealType
3662 plasma;
3663
cristyda1f9c12011-10-02 21:39:49 +00003664 PixelChannel
3665 channel;
3666
3667 PixelTrait
3668 traits;
3669
3670 register const Quantum
3671 *restrict u,
3672 *restrict v;
3673
3674 register Quantum
3675 *restrict q;
3676
3677 register ssize_t
3678 i;
cristy3ed852e2009-09-05 21:47:34 +00003679
cristy9d314ff2011-03-09 01:30:28 +00003680 ssize_t
3681 x,
3682 x_mid,
3683 y,
3684 y_mid;
3685
cristy3ed852e2009-09-05 21:47:34 +00003686 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3687 return(MagickTrue);
3688 if (depth != 0)
3689 {
3690 SegmentInfo
3691 local_info;
3692
3693 /*
3694 Divide the area into quadrants and recurse.
3695 */
3696 depth--;
3697 attenuate++;
cristybb503372010-05-27 20:51:26 +00003698 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3699 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003700 local_info=(*segment);
3701 local_info.x2=(double) x_mid;
3702 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003703 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3704 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003705 local_info=(*segment);
3706 local_info.y1=(double) y_mid;
3707 local_info.x2=(double) x_mid;
cristyda1f9c12011-10-02 21:39:49 +00003708 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3709 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003710 local_info=(*segment);
3711 local_info.x1=(double) x_mid;
3712 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003713 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3714 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003715 local_info=(*segment);
3716 local_info.x1=(double) x_mid;
3717 local_info.y1=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003718 return(PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3719 &local_info,attenuate,depth,exception));
cristy3ed852e2009-09-05 21:47:34 +00003720 }
cristybb503372010-05-27 20:51:26 +00003721 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3722 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003723 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3724 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3725 return(MagickFalse);
3726 /*
3727 Average pixels and apply plasma.
3728 */
cristy3ed852e2009-09-05 21:47:34 +00003729 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3730 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3731 {
cristy3ed852e2009-09-05 21:47:34 +00003732 /*
3733 Left pixel.
3734 */
cristybb503372010-05-27 20:51:26 +00003735 x=(ssize_t) ceil(segment->x1-0.5);
cristy1707c6c2012-01-18 23:30:54 +00003736 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3737 exception);
3738 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3739 exception);
cristyc5c6f662010-09-22 14:23:02 +00003740 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003741 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3742 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003743 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003744 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3745 {
cristye2a912b2011-12-05 20:02:07 +00003746 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003747 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003748 if (traits == UndefinedPixelTrait)
3749 continue;
3750 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3751 }
cristyc5c6f662010-09-22 14:23:02 +00003752 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003753 if (segment->x1 != segment->x2)
3754 {
3755 /*
3756 Right pixel.
3757 */
cristybb503372010-05-27 20:51:26 +00003758 x=(ssize_t) ceil(segment->x2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003759 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3760 1,1,exception);
3761 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3762 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003763 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003764 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3765 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003766 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003767 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3768 {
cristye2a912b2011-12-05 20:02:07 +00003769 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003770 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003771 if (traits == UndefinedPixelTrait)
3772 continue;
3773 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3774 }
cristyc5c6f662010-09-22 14:23:02 +00003775 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003776 }
3777 }
3778 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3779 {
3780 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3781 {
cristy3ed852e2009-09-05 21:47:34 +00003782 /*
3783 Bottom pixel.
3784 */
cristybb503372010-05-27 20:51:26 +00003785 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003786 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3787 1,1,exception);
3788 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3789 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003790 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003791 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3792 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003793 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003794 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3795 {
cristye2a912b2011-12-05 20:02:07 +00003796 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003797 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003798 if (traits == UndefinedPixelTrait)
3799 continue;
3800 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3801 }
cristyc5c6f662010-09-22 14:23:02 +00003802 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003803 }
3804 if (segment->y1 != segment->y2)
3805 {
cristy3ed852e2009-09-05 21:47:34 +00003806 /*
3807 Top pixel.
3808 */
cristybb503372010-05-27 20:51:26 +00003809 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003810 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3811 1,1,exception);
3812 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3813 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003814 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003815 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3816 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003817 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003818 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3819 {
cristye2a912b2011-12-05 20:02:07 +00003820 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003821 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003822 if (traits == UndefinedPixelTrait)
3823 continue;
3824 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3825 }
cristyc5c6f662010-09-22 14:23:02 +00003826 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003827 }
3828 }
3829 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3830 {
cristy3ed852e2009-09-05 21:47:34 +00003831 /*
3832 Middle pixel.
3833 */
cristybb503372010-05-27 20:51:26 +00003834 x=(ssize_t) ceil(segment->x1-0.5);
3835 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003836 u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
cristybb503372010-05-27 20:51:26 +00003837 x=(ssize_t) ceil(segment->x2-0.5);
3838 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003839 v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003840 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003841 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3842 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003843 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003844 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3845 {
cristye2a912b2011-12-05 20:02:07 +00003846 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003847 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003848 if (traits == UndefinedPixelTrait)
3849 continue;
3850 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3851 }
cristyc5c6f662010-09-22 14:23:02 +00003852 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003853 }
3854 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3855 return(MagickTrue);
3856 return(MagickFalse);
3857}
cristyda1f9c12011-10-02 21:39:49 +00003858
cristy3ed852e2009-09-05 21:47:34 +00003859MagickExport MagickBooleanType PlasmaImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00003860 const SegmentInfo *segment,size_t attenuate,size_t depth,
3861 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003862{
cristyc5c6f662010-09-22 14:23:02 +00003863 CacheView
cristyda1f9c12011-10-02 21:39:49 +00003864 *image_view,
3865 *u_view,
3866 *v_view;
cristyc5c6f662010-09-22 14:23:02 +00003867
cristy3ed852e2009-09-05 21:47:34 +00003868 MagickBooleanType
3869 status;
3870
3871 RandomInfo
3872 *random_info;
3873
3874 if (image->debug != MagickFalse)
3875 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3876 assert(image != (Image *) NULL);
3877 assert(image->signature == MagickSignature);
3878 if (image->debug != MagickFalse)
3879 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy5cbc0162011-08-29 00:36:28 +00003880 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristyc5c6f662010-09-22 14:23:02 +00003881 return(MagickFalse);
3882 image_view=AcquireCacheView(image);
cristyda1f9c12011-10-02 21:39:49 +00003883 u_view=AcquireCacheView(image);
3884 v_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003885 random_info=AcquireRandomInfo();
cristyda1f9c12011-10-02 21:39:49 +00003886 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
3887 attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003888 random_info=DestroyRandomInfo(random_info);
cristyda1f9c12011-10-02 21:39:49 +00003889 v_view=DestroyCacheView(v_view);
3890 u_view=DestroyCacheView(u_view);
cristyc5c6f662010-09-22 14:23:02 +00003891 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003892 return(status);
3893}
3894
3895/*
3896%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3897% %
3898% %
3899% %
3900% P o l a r o i d I m a g e %
3901% %
3902% %
3903% %
3904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3905%
3906% PolaroidImage() simulates a Polaroid picture.
3907%
3908% The format of the AnnotateImage method is:
3909%
3910% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003911% const char *caption,const double angle,
3912% const PixelInterpolateMethod method,ExceptionInfo exception)
cristy3ed852e2009-09-05 21:47:34 +00003913%
3914% A description of each parameter follows:
3915%
3916% o image: the image.
3917%
3918% o draw_info: the draw info.
3919%
cristye9e3d382011-12-14 01:50:13 +00003920% o caption: the Polaroid caption.
3921%
cristycee97112010-05-28 00:44:52 +00003922% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003923%
cristy5c4e2582011-09-11 19:21:03 +00003924% o method: the pixel interpolation method.
3925%
cristy3ed852e2009-09-05 21:47:34 +00003926% o exception: return any errors or warnings in this structure.
3927%
3928*/
3929MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003930 const char *caption,const double angle,const PixelInterpolateMethod method,
cristy5c4e2582011-09-11 19:21:03 +00003931 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003932{
cristy3ed852e2009-09-05 21:47:34 +00003933 Image
3934 *bend_image,
3935 *caption_image,
3936 *flop_image,
3937 *picture_image,
3938 *polaroid_image,
3939 *rotate_image,
3940 *trim_image;
3941
cristybb503372010-05-27 20:51:26 +00003942 size_t
cristy3ed852e2009-09-05 21:47:34 +00003943 height;
3944
cristy9d314ff2011-03-09 01:30:28 +00003945 ssize_t
3946 quantum;
3947
cristy3ed852e2009-09-05 21:47:34 +00003948 /*
3949 Simulate a Polaroid picture.
3950 */
3951 assert(image != (Image *) NULL);
3952 assert(image->signature == MagickSignature);
3953 if (image->debug != MagickFalse)
3954 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3955 assert(exception != (ExceptionInfo *) NULL);
3956 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003957 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003958 image->rows)/25.0,10.0);
3959 height=image->rows+2*quantum;
3960 caption_image=(Image *) NULL;
cristye9e3d382011-12-14 01:50:13 +00003961 if (caption != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003962 {
3963 char
cristye9e3d382011-12-14 01:50:13 +00003964 geometry[MaxTextExtent],
3965 *text;
cristy3ed852e2009-09-05 21:47:34 +00003966
3967 DrawInfo
3968 *annotate_info;
3969
cristy3ed852e2009-09-05 21:47:34 +00003970 MagickBooleanType
3971 status;
3972
cristy9d314ff2011-03-09 01:30:28 +00003973 ssize_t
3974 count;
3975
cristy3ed852e2009-09-05 21:47:34 +00003976 TypeMetric
3977 metrics;
3978
3979 /*
3980 Generate caption image.
3981 */
3982 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3983 if (caption_image == (Image *) NULL)
3984 return((Image *) NULL);
3985 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
cristye9e3d382011-12-14 01:50:13 +00003986 text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
3987 exception);
3988 (void) CloneString(&annotate_info->text,text);
cristy6b1d05e2010-09-22 19:17:27 +00003989 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
cristye9e3d382011-12-14 01:50:13 +00003990 &text,exception);
3991 status=SetImageExtent(caption_image,image->columns,(size_t) ((count+1)*
3992 (metrics.ascent-metrics.descent)+0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00003993 if (status == MagickFalse)
3994 caption_image=DestroyImage(caption_image);
3995 else
3996 {
3997 caption_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00003998 (void) SetImageBackgroundColor(caption_image,exception);
cristye9e3d382011-12-14 01:50:13 +00003999 (void) CloneString(&annotate_info->text,text);
cristyb51dff52011-05-19 16:55:47 +00004000 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00004001 metrics.ascent);
4002 if (annotate_info->gravity == UndefinedGravity)
4003 (void) CloneString(&annotate_info->geometry,AcquireString(
4004 geometry));
cristy5cbc0162011-08-29 00:36:28 +00004005 (void) AnnotateImage(caption_image,annotate_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004006 height+=caption_image->rows;
4007 }
4008 annotate_info=DestroyDrawInfo(annotate_info);
cristye9e3d382011-12-14 01:50:13 +00004009 text=DestroyString(text);
cristy3ed852e2009-09-05 21:47:34 +00004010 }
4011 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4012 exception);
4013 if (picture_image == (Image *) NULL)
4014 {
4015 if (caption_image != (Image *) NULL)
4016 caption_image=DestroyImage(caption_image);
4017 return((Image *) NULL);
4018 }
4019 picture_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004020 (void) SetImageBackgroundColor(picture_image,exception);
cristye941a752011-10-15 01:52:48 +00004021 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum,
4022 exception);
cristy3ed852e2009-09-05 21:47:34 +00004023 if (caption_image != (Image *) NULL)
4024 {
cristy1707c6c2012-01-18 23:30:54 +00004025 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,quantum,
4026 (ssize_t) (image->rows+3*quantum/2),exception);
cristy3ed852e2009-09-05 21:47:34 +00004027 caption_image=DestroyImage(caption_image);
4028 }
cristy9950d572011-10-01 18:22:35 +00004029 (void) QueryColorCompliance("none",AllCompliance,
4030 &picture_image->background_color,exception);
cristy63240882011-08-05 19:05:27 +00004031 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004032 rotate_image=RotateImage(picture_image,90.0,exception);
4033 picture_image=DestroyImage(picture_image);
4034 if (rotate_image == (Image *) NULL)
4035 return((Image *) NULL);
4036 picture_image=rotate_image;
4037 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
cristy5c4e2582011-09-11 19:21:03 +00004038 picture_image->columns,method,exception);
cristy3ed852e2009-09-05 21:47:34 +00004039 picture_image=DestroyImage(picture_image);
4040 if (bend_image == (Image *) NULL)
4041 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004042 picture_image=bend_image;
4043 rotate_image=RotateImage(picture_image,-90.0,exception);
4044 picture_image=DestroyImage(picture_image);
4045 if (rotate_image == (Image *) NULL)
4046 return((Image *) NULL);
4047 picture_image=rotate_image;
4048 picture_image->background_color=image->background_color;
anthonyf46d4262012-03-26 03:30:34 +00004049 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
cristy3ed852e2009-09-05 21:47:34 +00004050 exception);
4051 if (polaroid_image == (Image *) NULL)
4052 {
4053 picture_image=DestroyImage(picture_image);
4054 return(picture_image);
4055 }
4056 flop_image=FlopImage(polaroid_image,exception);
4057 polaroid_image=DestroyImage(polaroid_image);
4058 if (flop_image == (Image *) NULL)
4059 {
4060 picture_image=DestroyImage(picture_image);
4061 return(picture_image);
4062 }
4063 polaroid_image=flop_image;
cristy1707c6c2012-01-18 23:30:54 +00004064 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,(ssize_t)
4065 (-0.01*picture_image->columns/2.0),0L,exception);
cristy3ed852e2009-09-05 21:47:34 +00004066 picture_image=DestroyImage(picture_image);
cristy9950d572011-10-01 18:22:35 +00004067 (void) QueryColorCompliance("none",AllCompliance,
4068 &polaroid_image->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00004069 rotate_image=RotateImage(polaroid_image,angle,exception);
4070 polaroid_image=DestroyImage(polaroid_image);
4071 if (rotate_image == (Image *) NULL)
4072 return((Image *) NULL);
4073 polaroid_image=rotate_image;
4074 trim_image=TrimImage(polaroid_image,exception);
4075 polaroid_image=DestroyImage(polaroid_image);
4076 if (trim_image == (Image *) NULL)
4077 return((Image *) NULL);
4078 polaroid_image=trim_image;
4079 return(polaroid_image);
4080}
4081
4082/*
4083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4084% %
4085% %
4086% %
cristy3ed852e2009-09-05 21:47:34 +00004087% S e p i a T o n e I m a g e %
4088% %
4089% %
4090% %
4091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4092%
4093% MagickSepiaToneImage() applies a special effect to the image, similar to the
4094% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4095% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4096% threshold of 80% is a good starting point for a reasonable tone.
4097%
4098% The format of the SepiaToneImage method is:
4099%
4100% Image *SepiaToneImage(const Image *image,const double threshold,
4101% ExceptionInfo *exception)
4102%
4103% A description of each parameter follows:
4104%
4105% o image: the image.
4106%
4107% o threshold: the tone threshold.
4108%
4109% o exception: return any errors or warnings in this structure.
4110%
4111*/
4112MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4113 ExceptionInfo *exception)
4114{
4115#define SepiaToneImageTag "SepiaTone/Image"
4116
cristyc4c8d132010-01-07 01:58:38 +00004117 CacheView
4118 *image_view,
4119 *sepia_view;
4120
cristy3ed852e2009-09-05 21:47:34 +00004121 Image
4122 *sepia_image;
4123
cristy3ed852e2009-09-05 21:47:34 +00004124 MagickBooleanType
4125 status;
4126
cristybb503372010-05-27 20:51:26 +00004127 MagickOffsetType
4128 progress;
4129
4130 ssize_t
4131 y;
4132
cristy3ed852e2009-09-05 21:47:34 +00004133 /*
4134 Initialize sepia-toned image attributes.
4135 */
4136 assert(image != (const Image *) NULL);
4137 assert(image->signature == MagickSignature);
4138 if (image->debug != MagickFalse)
4139 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4140 assert(exception != (ExceptionInfo *) NULL);
4141 assert(exception->signature == MagickSignature);
cristy1707c6c2012-01-18 23:30:54 +00004142 sepia_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004143 if (sepia_image == (Image *) NULL)
4144 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004145 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004146 {
cristy3ed852e2009-09-05 21:47:34 +00004147 sepia_image=DestroyImage(sepia_image);
4148 return((Image *) NULL);
4149 }
4150 /*
4151 Tone each row of the image.
4152 */
4153 status=MagickTrue;
4154 progress=0;
4155 image_view=AcquireCacheView(image);
4156 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004157#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004158 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004159#endif
cristybb503372010-05-27 20:51:26 +00004160 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004161 {
cristy4c08aed2011-07-01 19:47:50 +00004162 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004163 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004164
cristybb503372010-05-27 20:51:26 +00004165 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004166 x;
4167
cristy4c08aed2011-07-01 19:47:50 +00004168 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004169 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004170
4171 if (status == MagickFalse)
4172 continue;
4173 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00004174 q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004175 exception);
cristy4c08aed2011-07-01 19:47:50 +00004176 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004177 {
4178 status=MagickFalse;
4179 continue;
4180 }
cristybb503372010-05-27 20:51:26 +00004181 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004182 {
4183 MagickRealType
4184 intensity,
4185 tone;
4186
cristy4c08aed2011-07-01 19:47:50 +00004187 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004188 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4189 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004190 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004191 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4192 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004193 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004194 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004195 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004196 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004197 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4198 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4199 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4200 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004201 p+=GetPixelChannels(image);
4202 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004203 }
4204 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4205 status=MagickFalse;
4206 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4207 {
4208 MagickBooleanType
4209 proceed;
4210
cristyb5d5f722009-11-04 03:03:49 +00004211#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004212 #pragma omp critical (MagickCore_SepiaToneImage)
cristy3ed852e2009-09-05 21:47:34 +00004213#endif
4214 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4215 image->rows);
4216 if (proceed == MagickFalse)
4217 status=MagickFalse;
4218 }
4219 }
4220 sepia_view=DestroyCacheView(sepia_view);
4221 image_view=DestroyCacheView(image_view);
cristye23ec9d2011-08-16 18:15:40 +00004222 (void) NormalizeImage(sepia_image,exception);
4223 (void) ContrastImage(sepia_image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004224 if (status == MagickFalse)
4225 sepia_image=DestroyImage(sepia_image);
4226 return(sepia_image);
4227}
4228
4229/*
4230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4231% %
4232% %
4233% %
4234% S h a d o w I m a g e %
4235% %
4236% %
4237% %
4238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4239%
4240% ShadowImage() simulates a shadow from the specified image and returns it.
4241%
4242% The format of the ShadowImage method is:
4243%
cristy70cddf72011-12-10 22:42:42 +00004244% Image *ShadowImage(const Image *image,const double alpha,
cristyaa2c16c2012-03-25 22:21:35 +00004245% const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4246% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004247%
4248% A description of each parameter follows:
4249%
4250% o image: the image.
4251%
cristy70cddf72011-12-10 22:42:42 +00004252% o alpha: percentage transparency.
cristy3ed852e2009-09-05 21:47:34 +00004253%
4254% o sigma: the standard deviation of the Gaussian, in pixels.
4255%
4256% o x_offset: the shadow x-offset.
4257%
4258% o y_offset: the shadow y-offset.
4259%
4260% o exception: return any errors or warnings in this structure.
4261%
4262*/
cristy70cddf72011-12-10 22:42:42 +00004263MagickExport Image *ShadowImage(const Image *image,const double alpha,
cristyaa2c16c2012-03-25 22:21:35 +00004264 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4265 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004266{
4267#define ShadowImageTag "Shadow/Image"
4268
cristy70cddf72011-12-10 22:42:42 +00004269 CacheView
4270 *image_view;
4271
cristybd5a96c2011-08-21 00:04:26 +00004272 ChannelType
4273 channel_mask;
4274
cristy3ed852e2009-09-05 21:47:34 +00004275 Image
4276 *border_image,
4277 *clone_image,
4278 *shadow_image;
4279
cristy70cddf72011-12-10 22:42:42 +00004280 MagickBooleanType
4281 status;
4282
cristy3ed852e2009-09-05 21:47:34 +00004283 RectangleInfo
4284 border_info;
4285
cristy70cddf72011-12-10 22:42:42 +00004286 ssize_t
4287 y;
4288
cristy3ed852e2009-09-05 21:47:34 +00004289 assert(image != (Image *) NULL);
4290 assert(image->signature == MagickSignature);
4291 if (image->debug != MagickFalse)
4292 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4293 assert(exception != (ExceptionInfo *) NULL);
4294 assert(exception->signature == MagickSignature);
4295 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4296 if (clone_image == (Image *) NULL)
4297 return((Image *) NULL);
cristy387430f2012-02-07 13:09:46 +00004298 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod,
4299 exception);
cristybb503372010-05-27 20:51:26 +00004300 border_info.width=(size_t) floor(2.0*sigma+0.5);
4301 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004302 border_info.x=0;
4303 border_info.y=0;
cristy9950d572011-10-01 18:22:35 +00004304 (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4305 exception);
cristy70cddf72011-12-10 22:42:42 +00004306 clone_image->matte=MagickTrue;
4307 border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
cristy3ed852e2009-09-05 21:47:34 +00004308 clone_image=DestroyImage(clone_image);
4309 if (border_image == (Image *) NULL)
4310 return((Image *) NULL);
4311 if (border_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004312 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004313 /*
4314 Shadow image.
4315 */
cristy70cddf72011-12-10 22:42:42 +00004316 status=MagickTrue;
4317 image_view=AcquireCacheView(border_image);
4318 for (y=0; y < (ssize_t) border_image->rows; y++)
4319 {
4320 PixelInfo
4321 background_color;
4322
4323 register Quantum
4324 *restrict q;
4325
4326 register ssize_t
4327 x;
4328
4329 if (status == MagickFalse)
4330 continue;
4331 q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4332 exception);
4333 if (q == (Quantum *) NULL)
4334 {
4335 status=MagickFalse;
4336 continue;
4337 }
4338 background_color=border_image->background_color;
4339 background_color.matte=MagickTrue;
4340 for (x=0; x < (ssize_t) border_image->columns; x++)
4341 {
4342 if (border_image->matte != MagickFalse)
4343 background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4344 SetPixelInfoPixel(border_image,&background_color,q);
4345 q+=GetPixelChannels(border_image);
4346 }
4347 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4348 status=MagickFalse;
4349 }
4350 image_view=DestroyCacheView(image_view);
4351 if (status == MagickFalse)
4352 {
4353 border_image=DestroyImage(border_image);
4354 return((Image *) NULL);
4355 }
cristybd5a96c2011-08-21 00:04:26 +00004356 channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
cristyaa2c16c2012-03-25 22:21:35 +00004357 shadow_image=BlurImage(border_image,0.0,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00004358 border_image=DestroyImage(border_image);
4359 if (shadow_image == (Image *) NULL)
4360 return((Image *) NULL);
cristyae1969f2011-12-10 03:07:36 +00004361 (void) SetPixelChannelMapMask(shadow_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004362 if (shadow_image->page.width == 0)
4363 shadow_image->page.width=shadow_image->columns;
4364 if (shadow_image->page.height == 0)
4365 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004366 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4367 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4368 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4369 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004370 return(shadow_image);
4371}
4372
4373/*
4374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4375% %
4376% %
4377% %
4378% S k e t c h I m a g e %
4379% %
4380% %
4381% %
4382%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4383%
4384% SketchImage() simulates a pencil sketch. We convolve the image with a
4385% Gaussian operator of the given radius and standard deviation (sigma). For
4386% reasonable results, radius should be larger than sigma. Use a radius of 0
4387% and SketchImage() selects a suitable radius for you. Angle gives the angle
4388% of the sketch.
4389%
4390% The format of the SketchImage method is:
4391%
4392% Image *SketchImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00004393% const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004394%
4395% A description of each parameter follows:
4396%
4397% o image: the image.
4398%
cristy574cc262011-08-05 01:23:58 +00004399% o radius: the radius of the Gaussian, in pixels, not counting the
4400% center pixel.
cristy3ed852e2009-09-05 21:47:34 +00004401%
4402% o sigma: the standard deviation of the Gaussian, in pixels.
4403%
cristyf7ef0252011-09-09 14:50:06 +00004404% o angle: apply the effect along this angle.
4405%
cristy3ed852e2009-09-05 21:47:34 +00004406% o exception: return any errors or warnings in this structure.
4407%
4408*/
4409MagickExport Image *SketchImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00004410 const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004411{
cristyfa112112010-01-04 17:48:07 +00004412 CacheView
4413 *random_view;
4414
cristy3ed852e2009-09-05 21:47:34 +00004415 Image
4416 *blend_image,
4417 *blur_image,
4418 *dodge_image,
4419 *random_image,
4420 *sketch_image;
4421
cristy3ed852e2009-09-05 21:47:34 +00004422 MagickBooleanType
4423 status;
4424
cristy3ed852e2009-09-05 21:47:34 +00004425 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004426 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004427
cristy9d314ff2011-03-09 01:30:28 +00004428 ssize_t
4429 y;
4430
cristy3ed852e2009-09-05 21:47:34 +00004431 /*
4432 Sketch image.
4433 */
4434 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4435 MagickTrue,exception);
4436 if (random_image == (Image *) NULL)
4437 return((Image *) NULL);
4438 status=MagickTrue;
cristy1b784432009-12-19 02:20:40 +00004439 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004440 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004441#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004442 #pragma omp parallel for schedule(static,4) shared(status)
cristy1b784432009-12-19 02:20:40 +00004443#endif
cristybb503372010-05-27 20:51:26 +00004444 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004445 {
cristy5c9e6f22010-09-17 17:31:01 +00004446 const int
4447 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004448
cristybb503372010-05-27 20:51:26 +00004449 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004450 x;
4451
cristy4c08aed2011-07-01 19:47:50 +00004452 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004453 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004454
cristy1b784432009-12-19 02:20:40 +00004455 if (status == MagickFalse)
4456 continue;
cristy3ed852e2009-09-05 21:47:34 +00004457 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4458 exception);
cristyacd2ed22011-08-30 01:44:23 +00004459 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004460 {
4461 status=MagickFalse;
4462 continue;
4463 }
cristybb503372010-05-27 20:51:26 +00004464 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004465 {
cristy76f512e2011-09-12 01:26:56 +00004466 MagickRealType
4467 value;
4468
4469 register ssize_t
4470 i;
4471
cristy10a6c612012-01-29 21:41:05 +00004472 if (GetPixelMask(random_image,q) != 0)
4473 {
4474 q+=GetPixelChannels(random_image);
4475 continue;
4476 }
cristy76f512e2011-09-12 01:26:56 +00004477 value=GetPseudoRandomValue(random_info[id]);
4478 for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4479 {
cristyabace412011-12-11 15:56:53 +00004480 PixelChannel
4481 channel;
4482
cristy76f512e2011-09-12 01:26:56 +00004483 PixelTrait
4484 traits;
4485
cristyabace412011-12-11 15:56:53 +00004486 channel=GetPixelChannelMapChannel(image,i);
4487 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004488 if (traits == UndefinedPixelTrait)
4489 continue;
4490 q[i]=ClampToQuantum(QuantumRange*value);
4491 }
cristyed231572011-07-14 02:18:59 +00004492 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004493 }
4494 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4495 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004496 }
4497 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004498 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004499 if (status == MagickFalse)
4500 {
4501 random_image=DestroyImage(random_image);
4502 return(random_image);
4503 }
cristyaa2c16c2012-03-25 22:21:35 +00004504 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
cristy3ed852e2009-09-05 21:47:34 +00004505 random_image=DestroyImage(random_image);
4506 if (blur_image == (Image *) NULL)
4507 return((Image *) NULL);
cristy6bfd6902011-12-09 01:33:45 +00004508 dodge_image=EdgeImage(blur_image,radius,1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004509 blur_image=DestroyImage(blur_image);
4510 if (dodge_image == (Image *) NULL)
4511 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +00004512 (void) NormalizeImage(dodge_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00004513 (void) NegateImage(dodge_image,MagickFalse,exception);
cristye941a752011-10-15 01:52:48 +00004514 (void) TransformImage(&dodge_image,(char *) NULL,"50%",exception);
cristy3ed852e2009-09-05 21:47:34 +00004515 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4516 if (sketch_image == (Image *) NULL)
4517 {
4518 dodge_image=DestroyImage(dodge_image);
4519 return((Image *) NULL);
4520 }
cristye941a752011-10-15 01:52:48 +00004521 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0,
4522 exception);
cristy3ed852e2009-09-05 21:47:34 +00004523 dodge_image=DestroyImage(dodge_image);
4524 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4525 if (blend_image == (Image *) NULL)
4526 {
4527 sketch_image=DestroyImage(sketch_image);
4528 return((Image *) NULL);
4529 }
4530 (void) SetImageArtifact(blend_image,"compose:args","20x80");
cristye941a752011-10-15 01:52:48 +00004531 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0,
4532 exception);
cristy3ed852e2009-09-05 21:47:34 +00004533 blend_image=DestroyImage(blend_image);
4534 return(sketch_image);
4535}
4536
4537/*
4538%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4539% %
4540% %
4541% %
4542% S o l a r i z e I m a g e %
4543% %
4544% %
4545% %
4546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4547%
4548% SolarizeImage() applies a special effect to the image, similar to the effect
4549% achieved in a photo darkroom by selectively exposing areas of photo
4550% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4551% measure of the extent of the solarization.
4552%
4553% The format of the SolarizeImage method is:
4554%
cristy5cbc0162011-08-29 00:36:28 +00004555% MagickBooleanType SolarizeImage(Image *image,const double threshold,
4556% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004557%
4558% A description of each parameter follows:
4559%
4560% o image: the image.
4561%
4562% o threshold: Define the extent of the solarization.
4563%
cristy5cbc0162011-08-29 00:36:28 +00004564% o exception: return any errors or warnings in this structure.
4565%
cristy3ed852e2009-09-05 21:47:34 +00004566*/
4567MagickExport MagickBooleanType SolarizeImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00004568 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004569{
4570#define SolarizeImageTag "Solarize/Image"
4571
cristyc4c8d132010-01-07 01:58:38 +00004572 CacheView
4573 *image_view;
4574
cristy3ed852e2009-09-05 21:47:34 +00004575 MagickBooleanType
4576 status;
4577
cristybb503372010-05-27 20:51:26 +00004578 MagickOffsetType
4579 progress;
4580
4581 ssize_t
4582 y;
4583
cristy3ed852e2009-09-05 21:47:34 +00004584 assert(image != (Image *) NULL);
4585 assert(image->signature == MagickSignature);
4586 if (image->debug != MagickFalse)
4587 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4588 if (image->storage_class == PseudoClass)
4589 {
cristybb503372010-05-27 20:51:26 +00004590 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004591 i;
4592
4593 /*
4594 Solarize colormap.
4595 */
cristybb503372010-05-27 20:51:26 +00004596 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004597 {
4598 if ((MagickRealType) image->colormap[i].red > threshold)
4599 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4600 if ((MagickRealType) image->colormap[i].green > threshold)
4601 image->colormap[i].green=(Quantum) QuantumRange-
4602 image->colormap[i].green;
4603 if ((MagickRealType) image->colormap[i].blue > threshold)
4604 image->colormap[i].blue=(Quantum) QuantumRange-
4605 image->colormap[i].blue;
4606 }
4607 }
4608 /*
4609 Solarize image.
4610 */
4611 status=MagickTrue;
4612 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00004613 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004614#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004615 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004616#endif
cristybb503372010-05-27 20:51:26 +00004617 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004618 {
cristybb503372010-05-27 20:51:26 +00004619 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004620 x;
4621
cristy4c08aed2011-07-01 19:47:50 +00004622 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004623 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004624
4625 if (status == MagickFalse)
4626 continue;
cristy5cbc0162011-08-29 00:36:28 +00004627 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00004628 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004629 {
4630 status=MagickFalse;
4631 continue;
4632 }
cristybb503372010-05-27 20:51:26 +00004633 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004634 {
cristy76f512e2011-09-12 01:26:56 +00004635 register ssize_t
4636 i;
4637
cristy10a6c612012-01-29 21:41:05 +00004638 if (GetPixelMask(image,q) != 0)
4639 {
4640 q+=GetPixelChannels(image);
4641 continue;
4642 }
cristy76f512e2011-09-12 01:26:56 +00004643 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4644 {
cristyabace412011-12-11 15:56:53 +00004645 PixelChannel
4646 channel;
4647
cristy76f512e2011-09-12 01:26:56 +00004648 PixelTrait
4649 traits;
4650
cristyabace412011-12-11 15:56:53 +00004651 channel=GetPixelChannelMapChannel(image,i);
4652 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004653 if ((traits == UndefinedPixelTrait) ||
4654 ((traits & CopyPixelTrait) != 0))
4655 continue;
4656 if ((MagickRealType) q[i] > threshold)
4657 q[i]=QuantumRange-q[i];
4658 }
cristyed231572011-07-14 02:18:59 +00004659 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004660 }
4661 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4662 status=MagickFalse;
4663 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4664 {
4665 MagickBooleanType
4666 proceed;
4667
cristyb5d5f722009-11-04 03:03:49 +00004668#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004669 #pragma omp critical (MagickCore_SolarizeImage)
cristy3ed852e2009-09-05 21:47:34 +00004670#endif
4671 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4672 if (proceed == MagickFalse)
4673 status=MagickFalse;
4674 }
4675 }
4676 image_view=DestroyCacheView(image_view);
4677 return(status);
4678}
4679
4680/*
4681%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4682% %
4683% %
4684% %
4685% S t e g a n o I m a g e %
4686% %
4687% %
4688% %
4689%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4690%
4691% SteganoImage() hides a digital watermark within the image. Recover
4692% the hidden watermark later to prove that the authenticity of an image.
4693% Offset defines the start position within the image to hide the watermark.
4694%
4695% The format of the SteganoImage method is:
4696%
4697% Image *SteganoImage(const Image *image,Image *watermark,
4698% ExceptionInfo *exception)
4699%
4700% A description of each parameter follows:
4701%
4702% o image: the image.
4703%
4704% o watermark: the watermark image.
4705%
4706% o exception: return any errors or warnings in this structure.
4707%
4708*/
4709MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4710 ExceptionInfo *exception)
4711{
cristye1bf8ad2010-09-19 17:07:03 +00004712#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004713#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004714 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004715#define SteganoImageTag "Stegano/Image"
4716
cristyb0d3bb92010-09-22 14:37:58 +00004717 CacheView
4718 *stegano_view,
4719 *watermark_view;
4720
cristy3ed852e2009-09-05 21:47:34 +00004721 Image
4722 *stegano_image;
4723
4724 int
4725 c;
4726
cristy3ed852e2009-09-05 21:47:34 +00004727 MagickBooleanType
4728 status;
4729
cristy101ab702011-10-13 13:06:32 +00004730 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004731 pixel;
4732
cristy4c08aed2011-07-01 19:47:50 +00004733 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004734 *q;
4735
cristye1bf8ad2010-09-19 17:07:03 +00004736 register ssize_t
4737 x;
4738
cristybb503372010-05-27 20:51:26 +00004739 size_t
cristyeaedf062010-05-29 22:36:02 +00004740 depth,
4741 one;
cristy3ed852e2009-09-05 21:47:34 +00004742
cristye1bf8ad2010-09-19 17:07:03 +00004743 ssize_t
4744 i,
4745 j,
4746 k,
4747 y;
4748
cristy3ed852e2009-09-05 21:47:34 +00004749 /*
4750 Initialize steganographic image attributes.
4751 */
4752 assert(image != (const Image *) NULL);
4753 assert(image->signature == MagickSignature);
4754 if (image->debug != MagickFalse)
4755 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4756 assert(watermark != (const Image *) NULL);
4757 assert(watermark->signature == MagickSignature);
4758 assert(exception != (ExceptionInfo *) NULL);
4759 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004760 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004761 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4762 if (stegano_image == (Image *) NULL)
4763 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004764 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004765 {
cristy3ed852e2009-09-05 21:47:34 +00004766 stegano_image=DestroyImage(stegano_image);
4767 return((Image *) NULL);
4768 }
4769 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4770 /*
4771 Hide watermark in low-order bits of image.
4772 */
4773 c=0;
4774 i=0;
4775 j=0;
4776 depth=stegano_image->depth;
4777 k=image->offset;
cristyda16f162011-02-19 23:52:17 +00004778 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004779 watermark_view=AcquireCacheView(watermark);
4780 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004781 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004782 {
cristybb503372010-05-27 20:51:26 +00004783 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004784 {
cristybb503372010-05-27 20:51:26 +00004785 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004786 {
cristy1707c6c2012-01-18 23:30:54 +00004787 ssize_t
4788 offset;
4789
cristyf05d4942012-03-17 16:26:09 +00004790 (void) GetOneCacheViewVirtualPixelInfo(watermark_view,x,y,&pixel,
cristyda1f9c12011-10-02 21:39:49 +00004791 exception);
cristy1707c6c2012-01-18 23:30:54 +00004792 offset=k/(ssize_t) stegano_image->columns;
4793 if (offset >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004794 break;
cristyb0d3bb92010-09-22 14:37:58 +00004795 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4796 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4797 exception);
cristyacd2ed22011-08-30 01:44:23 +00004798 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004799 break;
4800 switch (c)
4801 {
4802 case 0:
4803 {
cristy4c08aed2011-07-01 19:47:50 +00004804 SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004805 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004806 break;
4807 }
4808 case 1:
4809 {
cristy4c08aed2011-07-01 19:47:50 +00004810 SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004811 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004812 break;
4813 }
4814 case 2:
4815 {
cristy4c08aed2011-07-01 19:47:50 +00004816 SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004817 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004818 break;
4819 }
4820 }
cristyb0d3bb92010-09-22 14:37:58 +00004821 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004822 break;
4823 c++;
4824 if (c == 3)
4825 c=0;
4826 k++;
cristybb503372010-05-27 20:51:26 +00004827 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004828 k=0;
4829 if (k == image->offset)
4830 j++;
4831 }
4832 }
cristy8b27a6d2010-02-14 03:31:15 +00004833 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004834 {
cristy8b27a6d2010-02-14 03:31:15 +00004835 MagickBooleanType
4836 proceed;
4837
4838 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4839 (depth-i),depth);
4840 if (proceed == MagickFalse)
4841 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004842 }
4843 }
cristyb0d3bb92010-09-22 14:37:58 +00004844 stegano_view=DestroyCacheView(stegano_view);
4845 watermark_view=DestroyCacheView(watermark_view);
cristy3ed852e2009-09-05 21:47:34 +00004846 if (stegano_image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00004847 (void) SyncImage(stegano_image,exception);
cristyda16f162011-02-19 23:52:17 +00004848 if (status == MagickFalse)
4849 {
4850 stegano_image=DestroyImage(stegano_image);
4851 return((Image *) NULL);
4852 }
cristy3ed852e2009-09-05 21:47:34 +00004853 return(stegano_image);
4854}
4855
4856/*
4857%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4858% %
4859% %
4860% %
4861% S t e r e o A n a g l y p h I m a g e %
4862% %
4863% %
4864% %
4865%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4866%
4867% StereoAnaglyphImage() combines two images and produces a single image that
4868% is the composite of a left and right image of a stereo pair. Special
4869% red-green stereo glasses are required to view this effect.
4870%
4871% The format of the StereoAnaglyphImage method is:
4872%
4873% Image *StereoImage(const Image *left_image,const Image *right_image,
4874% ExceptionInfo *exception)
4875% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004876% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004877% ExceptionInfo *exception)
4878%
4879% A description of each parameter follows:
4880%
4881% o left_image: the left image.
4882%
4883% o right_image: the right image.
4884%
4885% o exception: return any errors or warnings in this structure.
4886%
4887% o x_offset: amount, in pixels, by which the left image is offset to the
4888% right of the right image.
4889%
4890% o y_offset: amount, in pixels, by which the left image is offset to the
4891% bottom of the right image.
4892%
4893%
4894*/
4895MagickExport Image *StereoImage(const Image *left_image,
4896 const Image *right_image,ExceptionInfo *exception)
4897{
4898 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4899}
4900
4901MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004902 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004903 ExceptionInfo *exception)
4904{
4905#define StereoImageTag "Stereo/Image"
4906
4907 const Image
4908 *image;
4909
4910 Image
4911 *stereo_image;
4912
cristy3ed852e2009-09-05 21:47:34 +00004913 MagickBooleanType
4914 status;
4915
cristy9d314ff2011-03-09 01:30:28 +00004916 ssize_t
4917 y;
4918
cristy3ed852e2009-09-05 21:47:34 +00004919 assert(left_image != (const Image *) NULL);
4920 assert(left_image->signature == MagickSignature);
4921 if (left_image->debug != MagickFalse)
4922 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4923 left_image->filename);
4924 assert(right_image != (const Image *) NULL);
4925 assert(right_image->signature == MagickSignature);
4926 assert(exception != (ExceptionInfo *) NULL);
4927 assert(exception->signature == MagickSignature);
4928 assert(right_image != (const Image *) NULL);
4929 image=left_image;
4930 if ((left_image->columns != right_image->columns) ||
4931 (left_image->rows != right_image->rows))
4932 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4933 /*
4934 Initialize stereo image attributes.
4935 */
4936 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4937 MagickTrue,exception);
4938 if (stereo_image == (Image *) NULL)
4939 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004940 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004941 {
cristy3ed852e2009-09-05 21:47:34 +00004942 stereo_image=DestroyImage(stereo_image);
4943 return((Image *) NULL);
4944 }
4945 /*
4946 Copy left image to red channel and right image to blue channel.
4947 */
cristyda16f162011-02-19 23:52:17 +00004948 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004949 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004950 {
cristy4c08aed2011-07-01 19:47:50 +00004951 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004952 *restrict p,
4953 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004954
cristybb503372010-05-27 20:51:26 +00004955 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004956 x;
4957
cristy4c08aed2011-07-01 19:47:50 +00004958 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004959 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004960
4961 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4962 exception);
4963 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4964 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy76f512e2011-09-12 01:26:56 +00004965 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
4966 (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004967 break;
cristybb503372010-05-27 20:51:26 +00004968 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004969 {
cristy4c08aed2011-07-01 19:47:50 +00004970 SetPixelRed(image,GetPixelRed(left_image,p),r);
cristy76f512e2011-09-12 01:26:56 +00004971 SetPixelGreen(image,GetPixelGreen(right_image,q),r);
4972 SetPixelBlue(image,GetPixelBlue(right_image,q),r);
4973 if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
4974 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4975 GetPixelAlpha(right_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00004976 p+=GetPixelChannels(left_image);
cristy76f512e2011-09-12 01:26:56 +00004977 q+=GetPixelChannels(right_image);
4978 r+=GetPixelChannels(stereo_image);
cristy3ed852e2009-09-05 21:47:34 +00004979 }
4980 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4981 break;
cristy8b27a6d2010-02-14 03:31:15 +00004982 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004983 {
cristy8b27a6d2010-02-14 03:31:15 +00004984 MagickBooleanType
4985 proceed;
4986
cristybb503372010-05-27 20:51:26 +00004987 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4988 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004989 if (proceed == MagickFalse)
4990 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004991 }
4992 }
cristyda16f162011-02-19 23:52:17 +00004993 if (status == MagickFalse)
4994 {
4995 stereo_image=DestroyImage(stereo_image);
4996 return((Image *) NULL);
4997 }
cristy3ed852e2009-09-05 21:47:34 +00004998 return(stereo_image);
4999}
5000
5001/*
5002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5003% %
5004% %
5005% %
5006% S w i r l I m a g e %
5007% %
5008% %
5009% %
5010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5011%
5012% SwirlImage() swirls the pixels about the center of the image, where
5013% degrees indicates the sweep of the arc through which each pixel is moved.
5014% You get a more dramatic effect as the degrees move from 1 to 360.
5015%
5016% The format of the SwirlImage method is:
5017%
5018% Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005019% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005020%
5021% A description of each parameter follows:
5022%
5023% o image: the image.
5024%
5025% o degrees: Define the tightness of the swirling effect.
5026%
cristy76f512e2011-09-12 01:26:56 +00005027% o method: the pixel interpolation method.
5028%
cristy3ed852e2009-09-05 21:47:34 +00005029% o exception: return any errors or warnings in this structure.
5030%
5031*/
5032MagickExport Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005033 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005034{
5035#define SwirlImageTag "Swirl/Image"
5036
cristyfa112112010-01-04 17:48:07 +00005037 CacheView
5038 *image_view,
5039 *swirl_view;
5040
cristy3ed852e2009-09-05 21:47:34 +00005041 Image
5042 *swirl_image;
5043
cristy3ed852e2009-09-05 21:47:34 +00005044 MagickBooleanType
5045 status;
5046
cristybb503372010-05-27 20:51:26 +00005047 MagickOffsetType
5048 progress;
5049
cristy3ed852e2009-09-05 21:47:34 +00005050 MagickRealType
5051 radius;
5052
5053 PointInfo
5054 center,
5055 scale;
5056
cristybb503372010-05-27 20:51:26 +00005057 ssize_t
5058 y;
5059
cristy3ed852e2009-09-05 21:47:34 +00005060 /*
5061 Initialize swirl image attributes.
5062 */
5063 assert(image != (const Image *) NULL);
5064 assert(image->signature == MagickSignature);
5065 if (image->debug != MagickFalse)
5066 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5067 assert(exception != (ExceptionInfo *) NULL);
5068 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00005069 swirl_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005070 if (swirl_image == (Image *) NULL)
5071 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005072 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005073 {
cristy3ed852e2009-09-05 21:47:34 +00005074 swirl_image=DestroyImage(swirl_image);
5075 return((Image *) NULL);
5076 }
cristy4c08aed2011-07-01 19:47:50 +00005077 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005078 swirl_image->matte=MagickTrue;
5079 /*
5080 Compute scaling factor.
5081 */
5082 center.x=(double) image->columns/2.0;
5083 center.y=(double) image->rows/2.0;
5084 radius=MagickMax(center.x,center.y);
5085 scale.x=1.0;
5086 scale.y=1.0;
5087 if (image->columns > image->rows)
5088 scale.y=(double) image->columns/(double) image->rows;
5089 else
5090 if (image->columns < image->rows)
5091 scale.x=(double) image->rows/(double) image->columns;
5092 degrees=(double) DegreesToRadians(degrees);
5093 /*
5094 Swirl image.
5095 */
5096 status=MagickTrue;
5097 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00005098 image_view=AcquireCacheView(image);
5099 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005100#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005101 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005102#endif
cristybb503372010-05-27 20:51:26 +00005103 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005104 {
cristy3ed852e2009-09-05 21:47:34 +00005105 MagickRealType
5106 distance;
5107
5108 PointInfo
5109 delta;
5110
cristy6d188022011-09-12 13:23:33 +00005111 register const Quantum
5112 *restrict p;
5113
cristybb503372010-05-27 20:51:26 +00005114 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005115 x;
5116
cristy4c08aed2011-07-01 19:47:50 +00005117 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005118 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005119
5120 if (status == MagickFalse)
5121 continue;
cristy6d188022011-09-12 13:23:33 +00005122 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00005123 q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00005124 exception);
cristy6d188022011-09-12 13:23:33 +00005125 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005126 {
5127 status=MagickFalse;
5128 continue;
5129 }
cristy3ed852e2009-09-05 21:47:34 +00005130 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00005131 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005132 {
5133 /*
5134 Determine if the pixel is within an ellipse.
5135 */
cristy10a6c612012-01-29 21:41:05 +00005136 if (GetPixelMask(image,p) != 0)
5137 {
5138 p+=GetPixelChannels(image);
5139 q+=GetPixelChannels(swirl_image);
5140 continue;
5141 }
cristy3ed852e2009-09-05 21:47:34 +00005142 delta.x=scale.x*(double) (x-center.x);
5143 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00005144 if (distance >= (radius*radius))
5145 {
cristy1707c6c2012-01-18 23:30:54 +00005146 register ssize_t
5147 i;
5148
cristy6d188022011-09-12 13:23:33 +00005149 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy1707c6c2012-01-18 23:30:54 +00005150 {
5151 PixelChannel
5152 channel;
5153
5154 PixelTrait
5155 swirl_traits,
5156 traits;
5157
5158 channel=GetPixelChannelMapChannel(image,i);
5159 traits=GetPixelChannelMapTraits(image,channel);
5160 swirl_traits=GetPixelChannelMapTraits(swirl_image,channel);
5161 if ((traits == UndefinedPixelTrait) ||
5162 (swirl_traits == UndefinedPixelTrait))
5163 continue;
5164 SetPixelChannel(swirl_image,channel,p[i],q);
5165 }
cristy6d188022011-09-12 13:23:33 +00005166 }
5167 else
cristy3ed852e2009-09-05 21:47:34 +00005168 {
5169 MagickRealType
5170 cosine,
5171 factor,
5172 sine;
5173
5174 /*
5175 Swirl the pixel.
5176 */
5177 factor=1.0-sqrt((double) distance)/radius;
5178 sine=sin((double) (degrees*factor*factor));
5179 cosine=cos((double) (degrees*factor*factor));
cristy76f512e2011-09-12 01:26:56 +00005180 status=InterpolatePixelChannels(image,image_view,swirl_image,method,
5181 ((cosine*delta.x-sine*delta.y)/scale.x+center.x),(double)
5182 ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00005183 }
cristy6d188022011-09-12 13:23:33 +00005184 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00005185 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00005186 }
5187 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5188 status=MagickFalse;
5189 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5190 {
5191 MagickBooleanType
5192 proceed;
5193
cristyb5d5f722009-11-04 03:03:49 +00005194#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005195 #pragma omp critical (MagickCore_SwirlImage)
cristy3ed852e2009-09-05 21:47:34 +00005196#endif
5197 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5198 if (proceed == MagickFalse)
5199 status=MagickFalse;
5200 }
5201 }
5202 swirl_view=DestroyCacheView(swirl_view);
5203 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005204 if (status == MagickFalse)
5205 swirl_image=DestroyImage(swirl_image);
5206 return(swirl_image);
5207}
5208
5209/*
5210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5211% %
5212% %
5213% %
5214% T i n t I m a g e %
5215% %
5216% %
5217% %
5218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5219%
5220% TintImage() applies a color vector to each pixel in the image. The length
5221% of the vector is 0 for black and white and at its maximum for the midtones.
5222% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5223%
5224% The format of the TintImage method is:
5225%
cristyb817c3f2011-10-03 14:00:35 +00005226% Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005227% const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005228%
5229% A description of each parameter follows:
5230%
5231% o image: the image.
5232%
cristyb817c3f2011-10-03 14:00:35 +00005233% o blend: A color value used for tinting.
cristy3ed852e2009-09-05 21:47:34 +00005234%
5235% o tint: A color value used for tinting.
5236%
5237% o exception: return any errors or warnings in this structure.
5238%
5239*/
cristyb817c3f2011-10-03 14:00:35 +00005240MagickExport Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005241 const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005242{
5243#define TintImageTag "Tint/Image"
5244
cristyc4c8d132010-01-07 01:58:38 +00005245 CacheView
5246 *image_view,
5247 *tint_view;
5248
cristy3ed852e2009-09-05 21:47:34 +00005249 GeometryInfo
5250 geometry_info;
5251
5252 Image
5253 *tint_image;
5254
cristy3ed852e2009-09-05 21:47:34 +00005255 MagickBooleanType
5256 status;
5257
cristybb503372010-05-27 20:51:26 +00005258 MagickOffsetType
5259 progress;
cristy3ed852e2009-09-05 21:47:34 +00005260
cristy28474bf2011-09-11 23:32:52 +00005261 MagickRealType
5262 intensity;
5263
cristy4c08aed2011-07-01 19:47:50 +00005264 PixelInfo
cristy1707c6c2012-01-18 23:30:54 +00005265 color_vector;
cristy3ed852e2009-09-05 21:47:34 +00005266
cristybb503372010-05-27 20:51:26 +00005267 MagickStatusType
5268 flags;
5269
5270 ssize_t
5271 y;
5272
cristy3ed852e2009-09-05 21:47:34 +00005273 /*
5274 Allocate tint image.
5275 */
5276 assert(image != (const Image *) NULL);
5277 assert(image->signature == MagickSignature);
5278 if (image->debug != MagickFalse)
5279 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5280 assert(exception != (ExceptionInfo *) NULL);
5281 assert(exception->signature == MagickSignature);
5282 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5283 if (tint_image == (Image *) NULL)
5284 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005285 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005286 {
cristy3ed852e2009-09-05 21:47:34 +00005287 tint_image=DestroyImage(tint_image);
5288 return((Image *) NULL);
5289 }
cristyaed9c382011-10-03 17:54:21 +00005290 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005291 return(tint_image);
5292 /*
5293 Determine RGB values of the color.
5294 */
cristy1707c6c2012-01-18 23:30:54 +00005295 GetPixelInfo(image,&color_vector);
cristyb817c3f2011-10-03 14:00:35 +00005296 flags=ParseGeometry(blend,&geometry_info);
cristy1707c6c2012-01-18 23:30:54 +00005297 color_vector.red=geometry_info.rho;
5298 color_vector.green=geometry_info.rho;
5299 color_vector.blue=geometry_info.rho;
5300 color_vector.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005301 if ((flags & SigmaValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005302 color_vector.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00005303 if ((flags & XiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005304 color_vector.blue=geometry_info.xi;
cristyb817c3f2011-10-03 14:00:35 +00005305 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005306 color_vector.alpha=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005307 if (image->colorspace == CMYKColorspace)
5308 {
cristy1707c6c2012-01-18 23:30:54 +00005309 color_vector.black=geometry_info.rho;
cristy76f512e2011-09-12 01:26:56 +00005310 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005311 color_vector.black=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005312 if ((flags & ChiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005313 color_vector.alpha=geometry_info.chi;
cristy76f512e2011-09-12 01:26:56 +00005314 }
cristy28474bf2011-09-11 23:32:52 +00005315 intensity=(MagickRealType) GetPixelInfoIntensity(tint);
cristy1707c6c2012-01-18 23:30:54 +00005316 color_vector.red=(MagickRealType) (color_vector.red*tint->red/100.0-
5317 intensity);
5318 color_vector.green=(MagickRealType) (color_vector.green*tint->green/100.0-
5319 intensity);
5320 color_vector.blue=(MagickRealType) (color_vector.blue*tint->blue/100.0-
5321 intensity);
5322 color_vector.black=(MagickRealType) (color_vector.black*tint->black/100.0-
5323 intensity);
5324 color_vector.alpha=(MagickRealType) (color_vector.alpha*tint->alpha/100.0-
5325 intensity);
cristy3ed852e2009-09-05 21:47:34 +00005326 /*
5327 Tint image.
5328 */
5329 status=MagickTrue;
5330 progress=0;
5331 image_view=AcquireCacheView(image);
5332 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005333#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005334 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005335#endif
cristybb503372010-05-27 20:51:26 +00005336 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005337 {
cristy4c08aed2011-07-01 19:47:50 +00005338 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005339 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005340
cristy4c08aed2011-07-01 19:47:50 +00005341 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005342 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005343
cristy6b91acb2011-04-19 12:23:54 +00005344 register ssize_t
5345 x;
5346
cristy3ed852e2009-09-05 21:47:34 +00005347 if (status == MagickFalse)
5348 continue;
5349 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5350 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5351 exception);
cristy4c08aed2011-07-01 19:47:50 +00005352 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005353 {
5354 status=MagickFalse;
5355 continue;
5356 }
cristybb503372010-05-27 20:51:26 +00005357 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005358 {
cristy4c08aed2011-07-01 19:47:50 +00005359 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005360 pixel;
5361
5362 MagickRealType
5363 weight;
5364
cristy1707c6c2012-01-18 23:30:54 +00005365 register ssize_t
5366 i;
5367
cristy10a6c612012-01-29 21:41:05 +00005368 if (GetPixelMask(image,p) != 0)
5369 {
5370 p+=GetPixelChannels(image);
5371 q+=GetPixelChannels(tint_image);
5372 continue;
5373 }
cristy1707c6c2012-01-18 23:30:54 +00005374 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
5375 {
5376 PixelChannel
5377 channel;
5378
5379 PixelTrait
5380 tint_traits,
5381 traits;
5382
5383 channel=GetPixelChannelMapChannel(image,i);
5384 traits=GetPixelChannelMapTraits(image,channel);
5385 tint_traits=GetPixelChannelMapTraits(tint_image,channel);
5386 if ((traits == UndefinedPixelTrait) ||
5387 (tint_traits == UndefinedPixelTrait))
5388 continue;
5389 if ((tint_traits & CopyPixelTrait) != 0)
5390 {
5391 SetPixelChannel(tint_image,channel,p[i],q);
5392 continue;
5393 }
5394 }
5395 GetPixelInfo(image,&pixel);
5396 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5397 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5398 (1.0-(4.0*(weight*weight)));
5399 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5400 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5401 (1.0-(4.0*(weight*weight)));
5402 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5403 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5404 (1.0-(4.0*(weight*weight)));
5405 weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5406 pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5407 (1.0-(4.0*(weight*weight)));
5408 SetPixelInfoPixel(tint_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005409 p+=GetPixelChannels(image);
5410 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005411 }
5412 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5413 status=MagickFalse;
5414 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5415 {
5416 MagickBooleanType
5417 proceed;
5418
cristyb5d5f722009-11-04 03:03:49 +00005419#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005420 #pragma omp critical (MagickCore_TintImage)
cristy3ed852e2009-09-05 21:47:34 +00005421#endif
5422 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5423 if (proceed == MagickFalse)
5424 status=MagickFalse;
5425 }
5426 }
5427 tint_view=DestroyCacheView(tint_view);
5428 image_view=DestroyCacheView(image_view);
5429 if (status == MagickFalse)
5430 tint_image=DestroyImage(tint_image);
5431 return(tint_image);
5432}
5433
5434/*
5435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5436% %
5437% %
5438% %
5439% V i g n e t t e I m a g e %
5440% %
5441% %
5442% %
5443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5444%
5445% VignetteImage() softens the edges of the image in vignette style.
5446%
5447% The format of the VignetteImage method is:
5448%
5449% Image *VignetteImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00005450% const double sigma,const ssize_t x,const ssize_t y,
cristy05c0c9a2011-09-05 23:16:13 +00005451% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005452%
5453% A description of each parameter follows:
5454%
5455% o image: the image.
5456%
5457% o radius: the radius of the pixel neighborhood.
5458%
5459% o sigma: the standard deviation of the Gaussian, in pixels.
5460%
5461% o x, y: Define the x and y ellipse offset.
5462%
5463% o exception: return any errors or warnings in this structure.
5464%
5465*/
5466MagickExport Image *VignetteImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00005467 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005468{
5469 char
5470 ellipse[MaxTextExtent];
5471
5472 DrawInfo
5473 *draw_info;
5474
5475 Image
5476 *canvas_image,
5477 *blur_image,
5478 *oval_image,
5479 *vignette_image;
5480
5481 assert(image != (Image *) NULL);
5482 assert(image->signature == MagickSignature);
5483 if (image->debug != MagickFalse)
5484 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5485 assert(exception != (ExceptionInfo *) NULL);
5486 assert(exception->signature == MagickSignature);
5487 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5488 if (canvas_image == (Image *) NULL)
5489 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005490 if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005491 {
cristy3ed852e2009-09-05 21:47:34 +00005492 canvas_image=DestroyImage(canvas_image);
5493 return((Image *) NULL);
5494 }
5495 canvas_image->matte=MagickTrue;
cristy98621462011-12-31 22:31:11 +00005496 oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
5497 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005498 if (oval_image == (Image *) NULL)
5499 {
5500 canvas_image=DestroyImage(canvas_image);
5501 return((Image *) NULL);
5502 }
cristy9950d572011-10-01 18:22:35 +00005503 (void) QueryColorCompliance("#000000",AllCompliance,
5504 &oval_image->background_color,exception);
cristyea1a8aa2011-10-20 13:24:06 +00005505 (void) SetImageBackgroundColor(oval_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005506 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00005507 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5508 exception);
5509 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5510 exception);
cristy1707c6c2012-01-18 23:30:54 +00005511 (void) FormatLocaleString(ellipse,MaxTextExtent,"ellipse %g,%g,%g,%g,"
5512 "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5513 image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005514 draw_info->primitive=AcquireString(ellipse);
cristy018f07f2011-09-04 21:15:19 +00005515 (void) DrawImage(oval_image,draw_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005516 draw_info=DestroyDrawInfo(draw_info);
cristyaa2c16c2012-03-25 22:21:35 +00005517 blur_image=BlurImage(oval_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00005518 oval_image=DestroyImage(oval_image);
5519 if (blur_image == (Image *) NULL)
5520 {
5521 canvas_image=DestroyImage(canvas_image);
5522 return((Image *) NULL);
5523 }
5524 blur_image->matte=MagickFalse;
cristy98621462011-12-31 22:31:11 +00005525 (void) CompositeImage(canvas_image,IntensityCompositeOp,blur_image,0,0,
cristye941a752011-10-15 01:52:48 +00005526 exception);
cristy3ed852e2009-09-05 21:47:34 +00005527 blur_image=DestroyImage(blur_image);
5528 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5529 canvas_image=DestroyImage(canvas_image);
5530 return(vignette_image);
5531}
5532
5533/*
5534%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5535% %
5536% %
5537% %
5538% W a v e I m a g e %
5539% %
5540% %
5541% %
5542%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5543%
5544% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005545% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005546% by the given parameters.
5547%
5548% The format of the WaveImage method is:
5549%
5550% Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005551% const double wave_length,const PixelInterpolateMethod method,
5552% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005553%
5554% A description of each parameter follows:
5555%
5556% o image: the image.
5557%
5558% o amplitude, wave_length: Define the amplitude and wave length of the
5559% sine wave.
5560%
cristy5c4e2582011-09-11 19:21:03 +00005561% o interpolate: the pixel interpolation method.
5562%
cristy3ed852e2009-09-05 21:47:34 +00005563% o exception: return any errors or warnings in this structure.
5564%
5565*/
5566MagickExport Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005567 const double wave_length,const PixelInterpolateMethod method,
5568 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005569{
5570#define WaveImageTag "Wave/Image"
5571
cristyfa112112010-01-04 17:48:07 +00005572 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005573 *image_view,
cristyfa112112010-01-04 17:48:07 +00005574 *wave_view;
5575
cristy3ed852e2009-09-05 21:47:34 +00005576 Image
5577 *wave_image;
5578
cristy3ed852e2009-09-05 21:47:34 +00005579 MagickBooleanType
5580 status;
5581
cristybb503372010-05-27 20:51:26 +00005582 MagickOffsetType
5583 progress;
5584
cristy3ed852e2009-09-05 21:47:34 +00005585 MagickRealType
5586 *sine_map;
5587
cristybb503372010-05-27 20:51:26 +00005588 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005589 i;
5590
cristybb503372010-05-27 20:51:26 +00005591 ssize_t
5592 y;
5593
cristy3ed852e2009-09-05 21:47:34 +00005594 /*
5595 Initialize wave image attributes.
5596 */
5597 assert(image != (Image *) NULL);
5598 assert(image->signature == MagickSignature);
5599 if (image->debug != MagickFalse)
5600 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5601 assert(exception != (ExceptionInfo *) NULL);
5602 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005603 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005604 fabs(amplitude)),MagickTrue,exception);
5605 if (wave_image == (Image *) NULL)
5606 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005607 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005608 {
cristy3ed852e2009-09-05 21:47:34 +00005609 wave_image=DestroyImage(wave_image);
5610 return((Image *) NULL);
5611 }
cristy4c08aed2011-07-01 19:47:50 +00005612 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005613 wave_image->matte=MagickTrue;
5614 /*
5615 Allocate sine map.
5616 */
5617 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5618 sizeof(*sine_map));
5619 if (sine_map == (MagickRealType *) NULL)
5620 {
5621 wave_image=DestroyImage(wave_image);
5622 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5623 }
cristybb503372010-05-27 20:51:26 +00005624 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005625 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5626 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005627 /*
5628 Wave image.
5629 */
5630 status=MagickTrue;
5631 progress=0;
cristyd76c51e2011-03-26 00:21:26 +00005632 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005633 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005634 (void) SetCacheViewVirtualPixelMethod(image_view,
5635 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005636#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005637 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005638#endif
cristybb503372010-05-27 20:51:26 +00005639 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005640 {
cristy4c08aed2011-07-01 19:47:50 +00005641 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005642 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005643
cristye97bb922011-04-03 01:36:52 +00005644 register ssize_t
5645 x;
5646
cristy3ed852e2009-09-05 21:47:34 +00005647 if (status == MagickFalse)
5648 continue;
5649 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5650 exception);
cristyacd2ed22011-08-30 01:44:23 +00005651 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005652 {
5653 status=MagickFalse;
5654 continue;
5655 }
cristybb503372010-05-27 20:51:26 +00005656 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005657 {
cristy5c4e2582011-09-11 19:21:03 +00005658 status=InterpolatePixelChannels(image,image_view,wave_image,method,
5659 (double) x,(double) (y-sine_map[x]),q,exception);
cristyed231572011-07-14 02:18:59 +00005660 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005661 }
5662 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5663 status=MagickFalse;
5664 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5665 {
5666 MagickBooleanType
5667 proceed;
5668
cristyb5d5f722009-11-04 03:03:49 +00005669#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005670 #pragma omp critical (MagickCore_WaveImage)
cristy3ed852e2009-09-05 21:47:34 +00005671#endif
5672 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5673 if (proceed == MagickFalse)
5674 status=MagickFalse;
5675 }
5676 }
5677 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005678 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005679 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5680 if (status == MagickFalse)
5681 wave_image=DestroyImage(wave_image);
5682 return(wave_image);
5683}