blob: 99e509fdee23161a4acb4c5cbf0bb68b4c414f4a [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"
cristya50544c2012-04-07 18:25:41 +0000645#define Colorize(pixel,fill_color,colorize) \
646 (pixel)=((pixel)*(100.0-(fill_color))+(colorize)*(fill_color))/100.0;
cristy3ed852e2009-09-05 21:47:34 +0000647
cristyc4c8d132010-01-07 01:58:38 +0000648 CacheView
649 *colorize_view,
650 *image_view;
651
cristy3ed852e2009-09-05 21:47:34 +0000652 GeometryInfo
653 geometry_info;
654
655 Image
656 *colorize_image;
657
cristy3ed852e2009-09-05 21:47:34 +0000658 MagickBooleanType
659 status;
660
cristybb503372010-05-27 20:51:26 +0000661 MagickOffsetType
662 progress;
663
cristy3ed852e2009-09-05 21:47:34 +0000664 MagickStatusType
665 flags;
666
cristyc7e6ff62011-10-03 13:46:11 +0000667 PixelInfo
cristyf9bf43e2012-04-07 18:13:07 +0000668 fill_color;
cristyc7e6ff62011-10-03 13:46:11 +0000669
cristybb503372010-05-27 20:51:26 +0000670 ssize_t
671 y;
672
cristy3ed852e2009-09-05 21:47:34 +0000673 /*
674 Allocate colorized image.
675 */
676 assert(image != (const Image *) NULL);
677 assert(image->signature == MagickSignature);
678 if (image->debug != MagickFalse)
679 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
680 assert(exception != (ExceptionInfo *) NULL);
681 assert(exception->signature == MagickSignature);
682 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
683 exception);
684 if (colorize_image == (Image *) NULL)
685 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000686 if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000687 {
cristy3ed852e2009-09-05 21:47:34 +0000688 colorize_image=DestroyImage(colorize_image);
689 return((Image *) NULL);
690 }
cristy5b67d4e2012-02-07 19:43:53 +0000691 if ((colorize->matte != MagickFalse) &&
692 (colorize_image->matte == MagickFalse))
693 (void) SetImageAlpha(colorize_image,OpaqueAlpha,exception);
cristyc7e6ff62011-10-03 13:46:11 +0000694 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000695 return(colorize_image);
696 /*
anthonyfd706f92012-01-19 04:22:02 +0000697 Determine RGB values of the fill color for pixel
cristy3ed852e2009-09-05 21:47:34 +0000698 */
cristyf9bf43e2012-04-07 18:13:07 +0000699 GetPixelInfo(image,&fill_color);
cristyb817c3f2011-10-03 14:00:35 +0000700 flags=ParseGeometry(blend,&geometry_info);
cristyf9bf43e2012-04-07 18:13:07 +0000701 fill_color.red=geometry_info.rho;
702 fill_color.green=geometry_info.rho;
703 fill_color.blue=geometry_info.rho;
704 fill_color.black=geometry_info.rho;
705 fill_color.alpha=100.0;
cristy3ed852e2009-09-05 21:47:34 +0000706 if ((flags & SigmaValue) != 0)
cristyf9bf43e2012-04-07 18:13:07 +0000707 fill_color.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +0000708 if ((flags & XiValue) != 0)
cristyf9bf43e2012-04-07 18:13:07 +0000709 fill_color.blue=geometry_info.xi;
cristy3ed852e2009-09-05 21:47:34 +0000710 if ((flags & PsiValue) != 0)
cristyf9bf43e2012-04-07 18:13:07 +0000711 fill_color.alpha=geometry_info.psi;
712 if (fill_color.colorspace == CMYKColorspace)
cristyc7e6ff62011-10-03 13:46:11 +0000713 {
cristyc7e6ff62011-10-03 13:46:11 +0000714 if ((flags & PsiValue) != 0)
cristyf9bf43e2012-04-07 18:13:07 +0000715 fill_color.black=geometry_info.psi;
cristyc7e6ff62011-10-03 13:46:11 +0000716 if ((flags & ChiValue) != 0)
cristyf9bf43e2012-04-07 18:13:07 +0000717 fill_color.alpha=geometry_info.chi;
cristyc7e6ff62011-10-03 13:46:11 +0000718 }
cristyf9bf43e2012-04-07 18:13:07 +0000719 if ((image->colorspace == GRAYColorspace) &&
720 (IsPixelInfoGray(&fill_color) != MagickFalse))
721 colorize_image->colorspace=sRGBColorspace;
cristy3ed852e2009-09-05 21:47:34 +0000722 /*
723 Colorize DirectClass image.
724 */
725 status=MagickTrue;
726 progress=0;
727 image_view=AcquireCacheView(image);
728 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000729#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000730 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000731#endif
cristybb503372010-05-27 20:51:26 +0000732 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000733 {
734 MagickBooleanType
735 sync;
736
cristyf9bf43e2012-04-07 18:13:07 +0000737 PixelInfo
738 pixel;
739
cristy4c08aed2011-07-01 19:47:50 +0000740 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000741 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000742
cristybb503372010-05-27 20:51:26 +0000743 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000744 x;
745
cristy4c08aed2011-07-01 19:47:50 +0000746 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000747 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000748
749 if (status == MagickFalse)
750 continue;
751 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
752 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
753 exception);
cristy4c08aed2011-07-01 19:47:50 +0000754 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000755 {
756 status=MagickFalse;
757 continue;
758 }
cristyf9bf43e2012-04-07 18:13:07 +0000759 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000760 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000761 {
cristyf9bf43e2012-04-07 18:13:07 +0000762 GetPixelInfoPixel(image,p,&pixel);
cristya50544c2012-04-07 18:25:41 +0000763 Colorize(pixel.red,fill_color.red,colorize->red);
764 Colorize(pixel.green,fill_color.green,colorize->green);
765 Colorize(pixel.blue,fill_color.blue,colorize->blue);
766 Colorize(pixel.black,fill_color.black,colorize->black);
767 Colorize(pixel.alpha,fill_color.alpha,colorize->alpha);
cristyf9bf43e2012-04-07 18:13:07 +0000768 SetPixelInfoPixel(colorize_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000769 p+=GetPixelChannels(image);
770 q+=GetPixelChannels(colorize_image);
cristy3ed852e2009-09-05 21:47:34 +0000771 }
772 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
773 if (sync == MagickFalse)
774 status=MagickFalse;
775 if (image->progress_monitor != (MagickProgressMonitor) NULL)
776 {
777 MagickBooleanType
778 proceed;
779
cristy319a1e72010-02-21 15:13:11 +0000780#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000781 #pragma omp critical (MagickCore_ColorizeImage)
cristy3ed852e2009-09-05 21:47:34 +0000782#endif
783 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
784 if (proceed == MagickFalse)
785 status=MagickFalse;
786 }
787 }
788 image_view=DestroyCacheView(image_view);
789 colorize_view=DestroyCacheView(colorize_view);
790 if (status == MagickFalse)
791 colorize_image=DestroyImage(colorize_image);
792 return(colorize_image);
793}
794
795/*
796%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
797% %
798% %
799% %
cristye6365592010-04-02 17:31:23 +0000800% C o l o r M a t r i x I m a g e %
801% %
802% %
803% %
804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
805%
806% ColorMatrixImage() applies color transformation to an image. This method
807% permits saturation changes, hue rotation, luminance to alpha, and various
808% other effects. Although variable-sized transformation matrices can be used,
809% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
810% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
811% except offsets are in column 6 rather than 5 (in support of CMYKA images)
812% and offsets are normalized (divide Flash offset by 255).
813%
814% The format of the ColorMatrixImage method is:
815%
816% Image *ColorMatrixImage(const Image *image,
817% const KernelInfo *color_matrix,ExceptionInfo *exception)
818%
819% A description of each parameter follows:
820%
821% o image: the image.
822%
823% o color_matrix: the color matrix.
824%
825% o exception: return any errors or warnings in this structure.
826%
827*/
anthonyfd706f92012-01-19 04:22:02 +0000828/* FUTURE: modify to make use of a MagickMatrix Mutliply function
829 That should be provided in "matrix.c"
830 (ASIDE: actually distorts should do this too but currently doesn't)
831*/
832
cristye6365592010-04-02 17:31:23 +0000833MagickExport Image *ColorMatrixImage(const Image *image,
834 const KernelInfo *color_matrix,ExceptionInfo *exception)
835{
836#define ColorMatrixImageTag "ColorMatrix/Image"
837
838 CacheView
839 *color_view,
840 *image_view;
841
842 double
843 ColorMatrix[6][6] =
844 {
845 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
846 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
847 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
848 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
849 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
850 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
851 };
852
853 Image
854 *color_image;
855
cristye6365592010-04-02 17:31:23 +0000856 MagickBooleanType
857 status;
858
cristybb503372010-05-27 20:51:26 +0000859 MagickOffsetType
860 progress;
861
862 register ssize_t
cristye6365592010-04-02 17:31:23 +0000863 i;
864
cristybb503372010-05-27 20:51:26 +0000865 ssize_t
866 u,
867 v,
868 y;
869
cristye6365592010-04-02 17:31:23 +0000870 /*
anthonyfd706f92012-01-19 04:22:02 +0000871 Map given color_matrix, into a 6x6 matrix RGBKA and a constant
cristye6365592010-04-02 17:31:23 +0000872 */
873 assert(image != (Image *) NULL);
874 assert(image->signature == MagickSignature);
875 if (image->debug != MagickFalse)
876 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
877 assert(exception != (ExceptionInfo *) NULL);
878 assert(exception->signature == MagickSignature);
879 i=0;
cristybb503372010-05-27 20:51:26 +0000880 for (v=0; v < (ssize_t) color_matrix->height; v++)
881 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000882 {
883 if ((v < 6) && (u < 6))
884 ColorMatrix[v][u]=color_matrix->values[i];
885 i++;
886 }
887 /*
888 Initialize color image.
889 */
cristy12550e62010-06-07 12:46:40 +0000890 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000891 if (color_image == (Image *) NULL)
892 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000893 if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
cristye6365592010-04-02 17:31:23 +0000894 {
cristye6365592010-04-02 17:31:23 +0000895 color_image=DestroyImage(color_image);
896 return((Image *) NULL);
897 }
898 if (image->debug != MagickFalse)
899 {
900 char
901 format[MaxTextExtent],
902 *message;
903
904 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
905 " ColorMatrix image with color matrix:");
906 message=AcquireString("");
907 for (v=0; v < 6; v++)
908 {
909 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000910 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000911 (void) ConcatenateString(&message,format);
912 for (u=0; u < 6; u++)
913 {
cristyb51dff52011-05-19 16:55:47 +0000914 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
cristye6365592010-04-02 17:31:23 +0000915 ColorMatrix[v][u]);
916 (void) ConcatenateString(&message,format);
917 }
918 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
919 }
920 message=DestroyString(message);
921 }
922 /*
anthonyfd706f92012-01-19 04:22:02 +0000923 Apply the ColorMatrix to image.
cristye6365592010-04-02 17:31:23 +0000924 */
925 status=MagickTrue;
926 progress=0;
927 image_view=AcquireCacheView(image);
928 color_view=AcquireCacheView(color_image);
929#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000930 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye6365592010-04-02 17:31:23 +0000931#endif
cristybb503372010-05-27 20:51:26 +0000932 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000933 {
cristyfcc25d92012-02-19 23:06:48 +0000934 PixelInfo
cristye6365592010-04-02 17:31:23 +0000935 pixel;
936
cristy4c08aed2011-07-01 19:47:50 +0000937 register const Quantum
cristye6365592010-04-02 17:31:23 +0000938 *restrict p;
939
cristy4c08aed2011-07-01 19:47:50 +0000940 register Quantum
941 *restrict q;
942
cristybb503372010-05-27 20:51:26 +0000943 register ssize_t
cristye6365592010-04-02 17:31:23 +0000944 x;
945
cristye6365592010-04-02 17:31:23 +0000946 if (status == MagickFalse)
947 continue;
948 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
949 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
950 exception);
cristy4c08aed2011-07-01 19:47:50 +0000951 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristye6365592010-04-02 17:31:23 +0000952 {
953 status=MagickFalse;
954 continue;
955 }
cristyfcc25d92012-02-19 23:06:48 +0000956 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000957 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +0000958 {
cristybb503372010-05-27 20:51:26 +0000959 register ssize_t
cristye6365592010-04-02 17:31:23 +0000960 v;
961
cristybb503372010-05-27 20:51:26 +0000962 size_t
cristye6365592010-04-02 17:31:23 +0000963 height;
964
cristyfcc25d92012-02-19 23:06:48 +0000965 GetPixelInfoPixel(image,p,&pixel);
cristye6365592010-04-02 17:31:23 +0000966 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +0000967 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +0000968 {
cristyfcc25d92012-02-19 23:06:48 +0000969 MagickRealType
970 sum;
971
972 sum=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
cristy4c08aed2011-07-01 19:47:50 +0000973 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +0000974 if (image->colorspace == CMYKColorspace)
cristyfcc25d92012-02-19 23:06:48 +0000975 sum+=ColorMatrix[v][3]*GetPixelBlack(image,p);
cristy4c08aed2011-07-01 19:47:50 +0000976 if (image->matte != MagickFalse)
cristyfcc25d92012-02-19 23:06:48 +0000977 sum+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
978 sum+=QuantumRange*ColorMatrix[v][5];
cristye6365592010-04-02 17:31:23 +0000979 switch (v)
980 {
cristyfcc25d92012-02-19 23:06:48 +0000981 case 0: pixel.red=sum; break;
982 case 1: pixel.green=sum; break;
983 case 2: pixel.blue=sum; break;
984 case 3: pixel.black=sum; break;
985 case 4: pixel.alpha=sum; break;
986 default: break;
cristye6365592010-04-02 17:31:23 +0000987 }
988 }
cristyfcc25d92012-02-19 23:06:48 +0000989 SetPixelInfoPixel(color_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000990 p+=GetPixelChannels(image);
991 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +0000992 }
993 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
994 status=MagickFalse;
995 if (image->progress_monitor != (MagickProgressMonitor) NULL)
996 {
997 MagickBooleanType
998 proceed;
999
1000#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00001001 #pragma omp critical (MagickCore_ColorMatrixImage)
cristye6365592010-04-02 17:31:23 +00001002#endif
1003 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1004 image->rows);
1005 if (proceed == MagickFalse)
1006 status=MagickFalse;
1007 }
1008 }
1009 color_view=DestroyCacheView(color_view);
1010 image_view=DestroyCacheView(image_view);
1011 if (status == MagickFalse)
1012 color_image=DestroyImage(color_image);
1013 return(color_image);
1014}
1015
1016/*
1017%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1018% %
1019% %
1020% %
cristy3ed852e2009-09-05 21:47:34 +00001021+ D e s t r o y F x I n f o %
1022% %
1023% %
1024% %
1025%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1026%
1027% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1028%
1029% The format of the DestroyFxInfo method is:
1030%
1031% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1032%
1033% A description of each parameter follows:
1034%
1035% o fx_info: the fx info.
1036%
1037*/
cristy7832dc22011-09-05 01:21:53 +00001038MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
cristy3ed852e2009-09-05 21:47:34 +00001039{
cristybb503372010-05-27 20:51:26 +00001040 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001041 i;
1042
1043 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1044 fx_info->expression=DestroyString(fx_info->expression);
1045 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1046 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001047 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001048 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1049 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001050 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1051 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1052 return(fx_info);
1053}
1054
1055/*
1056%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1057% %
1058% %
1059% %
cristy3ed852e2009-09-05 21:47:34 +00001060+ 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 %
1061% %
1062% %
1063% %
1064%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1065%
1066% FxEvaluateChannelExpression() evaluates an expression and returns the
1067% results.
1068%
1069% The format of the FxEvaluateExpression method is:
1070%
1071% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001072% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001073% MagickRealType *alpha,Exceptioninfo *exception)
1074% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1075% MagickRealType *alpha,Exceptioninfo *exception)
1076%
1077% A description of each parameter follows:
1078%
1079% o fx_info: the fx info.
1080%
1081% o channel: the channel.
1082%
1083% o x,y: the pixel position.
1084%
1085% o alpha: the result.
1086%
1087% o exception: return any errors or warnings in this structure.
1088%
1089*/
1090
cristy351842f2010-03-07 15:27:38 +00001091static inline double MagickMax(const double x,const double y)
1092{
1093 if (x > y)
1094 return(x);
1095 return(y);
1096}
1097
1098static inline double MagickMin(const double x,const double y)
1099{
1100 if (x < y)
1101 return(x);
1102 return(y);
1103}
1104
cristy3ed852e2009-09-05 21:47:34 +00001105static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001106 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 char
1109 key[MaxTextExtent],
1110 statistic[MaxTextExtent];
1111
1112 const char
1113 *value;
1114
1115 register const char
1116 *p;
1117
1118 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1119 if (*p == '.')
1120 switch (*++p) /* e.g. depth.r */
1121 {
cristy541ae572011-08-05 19:08:59 +00001122 case 'r': channel=RedPixelChannel; break;
1123 case 'g': channel=GreenPixelChannel; break;
1124 case 'b': channel=BluePixelChannel; break;
1125 case 'c': channel=CyanPixelChannel; break;
1126 case 'm': channel=MagentaPixelChannel; break;
1127 case 'y': channel=YellowPixelChannel; break;
1128 case 'k': channel=BlackPixelChannel; break;
cristy3ed852e2009-09-05 21:47:34 +00001129 default: break;
1130 }
cristyb51dff52011-05-19 16:55:47 +00001131 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001132 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001133 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1134 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001135 return(QuantumScale*StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001136 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1137 if (LocaleNCompare(symbol,"depth",5) == 0)
1138 {
cristybb503372010-05-27 20:51:26 +00001139 size_t
cristy3ed852e2009-09-05 21:47:34 +00001140 depth;
1141
cristyfefab1b2011-07-05 00:33:22 +00001142 depth=GetImageDepth(image,exception);
1143 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001144 }
1145 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1146 {
1147 double
1148 kurtosis,
1149 skewness;
1150
cristyd42d9952011-07-08 14:21:50 +00001151 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001152 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001153 }
1154 if (LocaleNCompare(symbol,"maxima",6) == 0)
1155 {
1156 double
1157 maxima,
1158 minima;
1159
cristyd42d9952011-07-08 14:21:50 +00001160 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001161 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001162 }
1163 if (LocaleNCompare(symbol,"mean",4) == 0)
1164 {
1165 double
1166 mean,
1167 standard_deviation;
1168
cristyd42d9952011-07-08 14:21:50 +00001169 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001170 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001171 }
1172 if (LocaleNCompare(symbol,"minima",6) == 0)
1173 {
1174 double
1175 maxima,
1176 minima;
1177
cristyd42d9952011-07-08 14:21:50 +00001178 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001179 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001180 }
1181 if (LocaleNCompare(symbol,"skewness",8) == 0)
1182 {
1183 double
1184 kurtosis,
1185 skewness;
1186
cristyd42d9952011-07-08 14:21:50 +00001187 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001188 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001189 }
1190 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1191 {
1192 double
1193 mean,
1194 standard_deviation;
1195
cristyd42d9952011-07-08 14:21:50 +00001196 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001197 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001198 standard_deviation);
1199 }
1200 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1201 ConstantString(statistic));
cristydbdd0e32011-11-04 23:29:40 +00001202 return(QuantumScale*StringToDouble(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001203}
1204
1205static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001206 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001207 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001208
cristyb0aad4c2011-11-02 19:30:35 +00001209static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
1210{
1211 if (beta != 0)
1212 return(FxGCD(beta,alpha % beta));
1213 return(alpha);
1214}
1215
cristy3ed852e2009-09-05 21:47:34 +00001216static inline const char *FxSubexpression(const char *expression,
1217 ExceptionInfo *exception)
1218{
1219 const char
1220 *subexpression;
1221
cristybb503372010-05-27 20:51:26 +00001222 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001223 level;
1224
1225 level=0;
1226 subexpression=expression;
1227 while ((*subexpression != '\0') &&
1228 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1229 {
1230 if (strchr("(",(int) *subexpression) != (char *) NULL)
1231 level++;
1232 else
1233 if (strchr(")",(int) *subexpression) != (char *) NULL)
1234 level--;
1235 subexpression++;
1236 }
1237 if (*subexpression == '\0')
1238 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1239 "UnbalancedParenthesis","`%s'",expression);
1240 return(subexpression);
1241}
1242
cristy0568ffc2011-07-25 16:54:14 +00001243static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001244 const ssize_t x,const ssize_t y,const char *expression,
1245 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001246{
1247 char
1248 *q,
1249 subexpression[MaxTextExtent],
1250 symbol[MaxTextExtent];
1251
1252 const char
1253 *p,
1254 *value;
1255
1256 Image
1257 *image;
1258
cristy4c08aed2011-07-01 19:47:50 +00001259 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001260 pixel;
1261
1262 MagickRealType
1263 alpha,
1264 beta;
1265
1266 PointInfo
1267 point;
1268
cristybb503372010-05-27 20:51:26 +00001269 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001270 i;
1271
1272 size_t
cristy1707c6c2012-01-18 23:30:54 +00001273 length,
cristy3ed852e2009-09-05 21:47:34 +00001274 level;
1275
1276 p=expression;
1277 i=GetImageIndexInList(fx_info->images);
1278 level=0;
1279 point.x=(double) x;
1280 point.y=(double) y;
1281 if (isalpha((int) *(p+1)) == 0)
1282 {
1283 if (strchr("suv",(int) *p) != (char *) NULL)
1284 {
1285 switch (*p)
1286 {
1287 case 's':
1288 default:
1289 {
1290 i=GetImageIndexInList(fx_info->images);
1291 break;
1292 }
1293 case 'u': i=0; break;
1294 case 'v': i=1; break;
1295 }
1296 p++;
1297 if (*p == '[')
1298 {
1299 level++;
1300 q=subexpression;
1301 for (p++; *p != '\0'; )
1302 {
1303 if (*p == '[')
1304 level++;
1305 else
1306 if (*p == ']')
1307 {
1308 level--;
1309 if (level == 0)
1310 break;
1311 }
1312 *q++=(*p++);
1313 }
1314 *q='\0';
1315 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1316 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001317 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001318 p++;
1319 }
1320 if (*p == '.')
1321 p++;
1322 }
1323 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1324 {
1325 p++;
1326 if (*p == '{')
1327 {
1328 level++;
1329 q=subexpression;
1330 for (p++; *p != '\0'; )
1331 {
1332 if (*p == '{')
1333 level++;
1334 else
1335 if (*p == '}')
1336 {
1337 level--;
1338 if (level == 0)
1339 break;
1340 }
1341 *q++=(*p++);
1342 }
1343 *q='\0';
1344 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1345 &beta,exception);
1346 point.x=alpha;
1347 point.y=beta;
1348 p++;
1349 }
1350 else
1351 if (*p == '[')
1352 {
1353 level++;
1354 q=subexpression;
1355 for (p++; *p != '\0'; )
1356 {
1357 if (*p == '[')
1358 level++;
1359 else
1360 if (*p == ']')
1361 {
1362 level--;
1363 if (level == 0)
1364 break;
1365 }
1366 *q++=(*p++);
1367 }
1368 *q='\0';
1369 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1370 &beta,exception);
1371 point.x+=alpha;
1372 point.y+=beta;
1373 p++;
1374 }
1375 if (*p == '.')
1376 p++;
1377 }
1378 }
1379 length=GetImageListLength(fx_info->images);
1380 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001381 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001382 i%=length;
1383 image=GetImageFromList(fx_info->images,i);
1384 if (image == (Image *) NULL)
1385 {
1386 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1387 "NoSuchImage","`%s'",expression);
1388 return(0.0);
1389 }
cristy4c08aed2011-07-01 19:47:50 +00001390 GetPixelInfo(image,&pixel);
1391 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001392 point.x,point.y,&pixel,exception);
cristy1707c6c2012-01-18 23:30:54 +00001393 if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1394 (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001395 (LocaleCompare(p,"saturation") != 0) &&
1396 (LocaleCompare(p,"lightness") != 0))
1397 {
1398 char
1399 name[MaxTextExtent];
1400
1401 (void) CopyMagickString(name,p,MaxTextExtent);
1402 for (q=name+(strlen(name)-1); q > name; q--)
1403 {
1404 if (*q == ')')
1405 break;
1406 if (*q == '.')
1407 {
1408 *q='\0';
1409 break;
1410 }
1411 }
1412 if ((strlen(name) > 2) &&
1413 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1414 {
cristy4c08aed2011-07-01 19:47:50 +00001415 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001416 *color;
1417
cristy4c08aed2011-07-01 19:47:50 +00001418 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1419 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001420 {
1421 pixel=(*color);
1422 p+=strlen(name);
1423 }
1424 else
cristy1707c6c2012-01-18 23:30:54 +00001425 {
1426 MagickBooleanType
1427 status;
1428
1429 status=QueryColorCompliance(name,AllCompliance,&pixel,
1430 fx_info->exception);
1431 if (status != MagickFalse)
1432 {
1433 (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1434 name),ClonePixelInfo(&pixel));
1435 p+=strlen(name);
1436 }
1437 }
cristy3ed852e2009-09-05 21:47:34 +00001438 }
1439 }
1440 (void) CopyMagickString(symbol,p,MaxTextExtent);
1441 StripString(symbol);
1442 if (*symbol == '\0')
1443 {
1444 switch (channel)
1445 {
cristy0568ffc2011-07-25 16:54:14 +00001446 case RedPixelChannel: return(QuantumScale*pixel.red);
1447 case GreenPixelChannel: return(QuantumScale*pixel.green);
1448 case BluePixelChannel: return(QuantumScale*pixel.blue);
1449 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001450 {
1451 if (image->colorspace != CMYKColorspace)
1452 {
1453 (void) ThrowMagickException(exception,GetMagickModule(),
cristy1a020e42011-12-06 18:13:23 +00001454 ImageError,"ColorSeparatedImageRequired","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00001455 image->filename);
1456 return(0.0);
1457 }
cristy4c08aed2011-07-01 19:47:50 +00001458 return(QuantumScale*pixel.black);
1459 }
cristy0568ffc2011-07-25 16:54:14 +00001460 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001461 {
1462 MagickRealType
1463 alpha;
1464
1465 if (pixel.matte == MagickFalse)
1466 return(1.0);
1467 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1468 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001469 }
cristya382aca2011-12-06 18:22:48 +00001470 case IndexPixelChannel:
1471 return(0.0);
cristyb3a73b52011-07-26 01:34:43 +00001472 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001473 {
cristy4c08aed2011-07-01 19:47:50 +00001474 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001475 }
cristy3ed852e2009-09-05 21:47:34 +00001476 default:
1477 break;
1478 }
1479 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1480 "UnableToParseExpression","`%s'",p);
1481 return(0.0);
1482 }
1483 switch (*symbol)
1484 {
1485 case 'A':
1486 case 'a':
1487 {
1488 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001489 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001490 break;
1491 }
1492 case 'B':
1493 case 'b':
1494 {
1495 if (LocaleCompare(symbol,"b") == 0)
1496 return(QuantumScale*pixel.blue);
1497 break;
1498 }
1499 case 'C':
1500 case 'c':
1501 {
1502 if (LocaleNCompare(symbol,"channel",7) == 0)
1503 {
1504 GeometryInfo
1505 channel_info;
1506
1507 MagickStatusType
1508 flags;
1509
1510 flags=ParseGeometry(symbol+7,&channel_info);
1511 if (image->colorspace == CMYKColorspace)
1512 switch (channel)
1513 {
cristy0568ffc2011-07-25 16:54:14 +00001514 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001515 {
1516 if ((flags & RhoValue) == 0)
1517 return(0.0);
1518 return(channel_info.rho);
1519 }
cristy0568ffc2011-07-25 16:54:14 +00001520 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001521 {
1522 if ((flags & SigmaValue) == 0)
1523 return(0.0);
1524 return(channel_info.sigma);
1525 }
cristy0568ffc2011-07-25 16:54:14 +00001526 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001527 {
1528 if ((flags & XiValue) == 0)
1529 return(0.0);
1530 return(channel_info.xi);
1531 }
cristy0568ffc2011-07-25 16:54:14 +00001532 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001533 {
1534 if ((flags & PsiValue) == 0)
1535 return(0.0);
1536 return(channel_info.psi);
1537 }
cristy0568ffc2011-07-25 16:54:14 +00001538 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001539 {
1540 if ((flags & ChiValue) == 0)
1541 return(0.0);
1542 return(channel_info.chi);
1543 }
1544 default:
1545 return(0.0);
1546 }
1547 switch (channel)
1548 {
cristy0568ffc2011-07-25 16:54:14 +00001549 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001550 {
1551 if ((flags & RhoValue) == 0)
1552 return(0.0);
1553 return(channel_info.rho);
1554 }
cristy0568ffc2011-07-25 16:54:14 +00001555 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001556 {
1557 if ((flags & SigmaValue) == 0)
1558 return(0.0);
1559 return(channel_info.sigma);
1560 }
cristy0568ffc2011-07-25 16:54:14 +00001561 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001562 {
1563 if ((flags & XiValue) == 0)
1564 return(0.0);
1565 return(channel_info.xi);
1566 }
cristy0568ffc2011-07-25 16:54:14 +00001567 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001568 {
1569 if ((flags & ChiValue) == 0)
1570 return(0.0);
1571 return(channel_info.chi);
1572 }
cristy0568ffc2011-07-25 16:54:14 +00001573 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001574 {
1575 if ((flags & PsiValue) == 0)
1576 return(0.0);
1577 return(channel_info.psi);
1578 }
cristy3ed852e2009-09-05 21:47:34 +00001579 default:
1580 return(0.0);
1581 }
1582 return(0.0);
1583 }
1584 if (LocaleCompare(symbol,"c") == 0)
1585 return(QuantumScale*pixel.red);
1586 break;
1587 }
1588 case 'D':
1589 case 'd':
1590 {
1591 if (LocaleNCompare(symbol,"depth",5) == 0)
1592 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1593 break;
1594 }
1595 case 'G':
1596 case 'g':
1597 {
1598 if (LocaleCompare(symbol,"g") == 0)
1599 return(QuantumScale*pixel.green);
1600 break;
1601 }
1602 case 'K':
1603 case 'k':
1604 {
1605 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1606 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1607 if (LocaleCompare(symbol,"k") == 0)
1608 {
1609 if (image->colorspace != CMYKColorspace)
1610 {
1611 (void) ThrowMagickException(exception,GetMagickModule(),
1612 OptionError,"ColorSeparatedImageRequired","`%s'",
1613 image->filename);
1614 return(0.0);
1615 }
cristy4c08aed2011-07-01 19:47:50 +00001616 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001617 }
1618 break;
1619 }
1620 case 'H':
1621 case 'h':
1622 {
1623 if (LocaleCompare(symbol,"h") == 0)
1624 return((MagickRealType) image->rows);
1625 if (LocaleCompare(symbol,"hue") == 0)
1626 {
1627 double
1628 hue,
1629 lightness,
1630 saturation;
1631
cristyda1f9c12011-10-02 21:39:49 +00001632 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1633 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001634 return(hue);
1635 }
1636 break;
1637 }
1638 case 'I':
1639 case 'i':
1640 {
1641 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1642 (LocaleCompare(symbol,"image.minima") == 0) ||
1643 (LocaleCompare(symbol,"image.maxima") == 0) ||
1644 (LocaleCompare(symbol,"image.mean") == 0) ||
1645 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1646 (LocaleCompare(symbol,"image.skewness") == 0) ||
1647 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1648 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1649 if (LocaleCompare(symbol,"image.resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001650 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001651 if (LocaleCompare(symbol,"image.resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001652 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001653 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001654 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001655 if (LocaleCompare(symbol,"i") == 0)
1656 return((MagickRealType) x);
1657 break;
1658 }
1659 case 'J':
1660 case 'j':
1661 {
1662 if (LocaleCompare(symbol,"j") == 0)
1663 return((MagickRealType) y);
1664 break;
1665 }
1666 case 'L':
1667 case 'l':
1668 {
1669 if (LocaleCompare(symbol,"lightness") == 0)
1670 {
1671 double
1672 hue,
1673 lightness,
1674 saturation;
1675
cristyda1f9c12011-10-02 21:39:49 +00001676 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1677 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001678 return(lightness);
1679 }
1680 if (LocaleCompare(symbol,"luminance") == 0)
1681 {
1682 double
1683 luminence;
1684
1685 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1686 return(QuantumScale*luminence);
1687 }
1688 break;
1689 }
1690 case 'M':
1691 case 'm':
1692 {
1693 if (LocaleNCompare(symbol,"maxima",6) == 0)
1694 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1695 if (LocaleNCompare(symbol,"mean",4) == 0)
1696 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1697 if (LocaleNCompare(symbol,"minima",6) == 0)
1698 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1699 if (LocaleCompare(symbol,"m") == 0)
1700 return(QuantumScale*pixel.blue);
1701 break;
1702 }
1703 case 'N':
1704 case 'n':
1705 {
1706 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001707 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001708 break;
1709 }
1710 case 'O':
1711 case 'o':
1712 {
1713 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001714 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001715 break;
1716 }
1717 case 'P':
1718 case 'p':
1719 {
1720 if (LocaleCompare(symbol,"page.height") == 0)
1721 return((MagickRealType) image->page.height);
1722 if (LocaleCompare(symbol,"page.width") == 0)
1723 return((MagickRealType) image->page.width);
1724 if (LocaleCompare(symbol,"page.x") == 0)
1725 return((MagickRealType) image->page.x);
1726 if (LocaleCompare(symbol,"page.y") == 0)
1727 return((MagickRealType) image->page.y);
1728 break;
1729 }
1730 case 'R':
1731 case 'r':
1732 {
1733 if (LocaleCompare(symbol,"resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001734 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001735 if (LocaleCompare(symbol,"resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001736 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001737 if (LocaleCompare(symbol,"r") == 0)
1738 return(QuantumScale*pixel.red);
1739 break;
1740 }
1741 case 'S':
1742 case 's':
1743 {
1744 if (LocaleCompare(symbol,"saturation") == 0)
1745 {
1746 double
1747 hue,
1748 lightness,
1749 saturation;
1750
cristyda1f9c12011-10-02 21:39:49 +00001751 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1752 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001753 return(saturation);
1754 }
1755 if (LocaleNCompare(symbol,"skewness",8) == 0)
1756 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1757 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1758 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1759 break;
1760 }
1761 case 'T':
1762 case 't':
1763 {
1764 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001765 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001766 break;
1767 }
1768 case 'W':
1769 case 'w':
1770 {
1771 if (LocaleCompare(symbol,"w") == 0)
1772 return((MagickRealType) image->columns);
1773 break;
1774 }
1775 case 'Y':
1776 case 'y':
1777 {
1778 if (LocaleCompare(symbol,"y") == 0)
1779 return(QuantumScale*pixel.green);
1780 break;
1781 }
1782 case 'Z':
1783 case 'z':
1784 {
1785 if (LocaleCompare(symbol,"z") == 0)
1786 {
1787 MagickRealType
1788 depth;
1789
cristyfefab1b2011-07-05 00:33:22 +00001790 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001791 return(depth);
1792 }
1793 break;
1794 }
1795 default:
1796 break;
1797 }
1798 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1799 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001800 return((MagickRealType) StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001801 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1802 "UnableToParseExpression","`%s'",symbol);
1803 return(0.0);
1804}
1805
1806static const char *FxOperatorPrecedence(const char *expression,
1807 ExceptionInfo *exception)
1808{
1809 typedef enum
1810 {
1811 UndefinedPrecedence,
1812 NullPrecedence,
1813 BitwiseComplementPrecedence,
1814 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001815 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001816 MultiplyPrecedence,
1817 AdditionPrecedence,
1818 ShiftPrecedence,
1819 RelationalPrecedence,
1820 EquivalencyPrecedence,
1821 BitwiseAndPrecedence,
1822 BitwiseOrPrecedence,
1823 LogicalAndPrecedence,
1824 LogicalOrPrecedence,
1825 TernaryPrecedence,
1826 AssignmentPrecedence,
1827 CommaPrecedence,
1828 SeparatorPrecedence
1829 } FxPrecedence;
1830
1831 FxPrecedence
1832 precedence,
1833 target;
1834
1835 register const char
1836 *subexpression;
1837
1838 register int
1839 c;
1840
cristybb503372010-05-27 20:51:26 +00001841 size_t
cristy3ed852e2009-09-05 21:47:34 +00001842 level;
1843
1844 c=0;
1845 level=0;
1846 subexpression=(const char *) NULL;
1847 target=NullPrecedence;
1848 while (*expression != '\0')
1849 {
1850 precedence=UndefinedPrecedence;
1851 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1852 {
1853 expression++;
1854 continue;
1855 }
cristy488fa882010-03-01 22:34:24 +00001856 switch (*expression)
1857 {
1858 case 'A':
1859 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001860 {
cristyb33454f2011-08-03 02:10:45 +00001861#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001862 if (LocaleNCompare(expression,"acosh",5) == 0)
1863 {
1864 expression+=5;
1865 break;
1866 }
cristyb33454f2011-08-03 02:10:45 +00001867#endif
1868#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001869 if (LocaleNCompare(expression,"asinh",5) == 0)
1870 {
1871 expression+=5;
1872 break;
1873 }
cristyb33454f2011-08-03 02:10:45 +00001874#endif
1875#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001876 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001877 {
1878 expression+=5;
1879 break;
1880 }
cristyb33454f2011-08-03 02:10:45 +00001881#endif
cristy488fa882010-03-01 22:34:24 +00001882 break;
cristy3ed852e2009-09-05 21:47:34 +00001883 }
cristy62d455f2011-11-03 11:42:28 +00001884 case 'E':
1885 case 'e':
1886 {
1887 if ((LocaleNCompare(expression,"E+",2) == 0) ||
1888 (LocaleNCompare(expression,"E-",2) == 0))
1889 {
1890 expression+=2; /* scientific notation */
1891 break;
1892 }
1893 }
cristy488fa882010-03-01 22:34:24 +00001894 case 'J':
1895 case 'j':
1896 {
1897 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1898 (LocaleNCompare(expression,"j1",2) == 0))
1899 {
1900 expression+=2;
1901 break;
1902 }
1903 break;
1904 }
cristy2def9322010-06-18 23:59:37 +00001905 case '#':
1906 {
1907 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1908 expression++;
1909 break;
1910 }
cristy488fa882010-03-01 22:34:24 +00001911 default:
1912 break;
1913 }
cristy3ed852e2009-09-05 21:47:34 +00001914 if ((c == (int) '{') || (c == (int) '['))
1915 level++;
1916 else
1917 if ((c == (int) '}') || (c == (int) ']'))
1918 level--;
1919 if (level == 0)
1920 switch ((unsigned char) *expression)
1921 {
1922 case '~':
1923 case '!':
1924 {
1925 precedence=BitwiseComplementPrecedence;
1926 break;
1927 }
1928 case '^':
cristy6621e252010-08-13 00:42:57 +00001929 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001930 {
1931 precedence=ExponentPrecedence;
1932 break;
1933 }
1934 default:
1935 {
1936 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1937 (strchr(")",c) != (char *) NULL))) &&
1938 (((islower((int) ((char) *expression)) != 0) ||
1939 (strchr("(",(int) *expression) != (char *) NULL)) ||
1940 ((isdigit((int) ((char) c)) == 0) &&
1941 (isdigit((int) ((char) *expression)) != 0))) &&
1942 (strchr("xy",(int) *expression) == (char *) NULL))
1943 precedence=MultiplyPrecedence;
1944 break;
1945 }
1946 case '*':
1947 case '/':
1948 case '%':
1949 {
1950 precedence=MultiplyPrecedence;
1951 break;
1952 }
1953 case '+':
1954 case '-':
1955 {
1956 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1957 (isalpha(c) != 0))
1958 precedence=AdditionPrecedence;
1959 break;
1960 }
1961 case LeftShiftOperator:
1962 case RightShiftOperator:
1963 {
1964 precedence=ShiftPrecedence;
1965 break;
1966 }
1967 case '<':
1968 case LessThanEqualOperator:
1969 case GreaterThanEqualOperator:
1970 case '>':
1971 {
1972 precedence=RelationalPrecedence;
1973 break;
1974 }
1975 case EqualOperator:
1976 case NotEqualOperator:
1977 {
1978 precedence=EquivalencyPrecedence;
1979 break;
1980 }
1981 case '&':
1982 {
1983 precedence=BitwiseAndPrecedence;
1984 break;
1985 }
1986 case '|':
1987 {
1988 precedence=BitwiseOrPrecedence;
1989 break;
1990 }
1991 case LogicalAndOperator:
1992 {
1993 precedence=LogicalAndPrecedence;
1994 break;
1995 }
1996 case LogicalOrOperator:
1997 {
1998 precedence=LogicalOrPrecedence;
1999 break;
2000 }
cristy116af162010-08-13 01:25:47 +00002001 case ExponentialNotation:
2002 {
2003 precedence=ExponentialNotationPrecedence;
2004 break;
2005 }
cristy3ed852e2009-09-05 21:47:34 +00002006 case ':':
2007 case '?':
2008 {
2009 precedence=TernaryPrecedence;
2010 break;
2011 }
2012 case '=':
2013 {
2014 precedence=AssignmentPrecedence;
2015 break;
2016 }
2017 case ',':
2018 {
2019 precedence=CommaPrecedence;
2020 break;
2021 }
2022 case ';':
2023 {
2024 precedence=SeparatorPrecedence;
2025 break;
2026 }
2027 }
2028 if ((precedence == BitwiseComplementPrecedence) ||
2029 (precedence == TernaryPrecedence) ||
2030 (precedence == AssignmentPrecedence))
2031 {
2032 if (precedence > target)
2033 {
2034 /*
2035 Right-to-left associativity.
2036 */
2037 target=precedence;
2038 subexpression=expression;
2039 }
2040 }
2041 else
2042 if (precedence >= target)
2043 {
2044 /*
2045 Left-to-right associativity.
2046 */
2047 target=precedence;
2048 subexpression=expression;
2049 }
2050 if (strchr("(",(int) *expression) != (char *) NULL)
2051 expression=FxSubexpression(expression,exception);
2052 c=(int) (*expression++);
2053 }
2054 return(subexpression);
2055}
2056
2057static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002058 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002059 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002060{
2061 char
2062 *q,
2063 subexpression[MaxTextExtent];
2064
2065 MagickRealType
2066 alpha,
2067 gamma;
2068
2069 register const char
2070 *p;
2071
2072 *beta=0.0;
2073 if (exception->severity != UndefinedException)
2074 return(0.0);
2075 while (isspace((int) *expression) != 0)
2076 expression++;
2077 if (*expression == '\0')
2078 {
2079 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2080 "MissingExpression","`%s'",expression);
2081 return(0.0);
2082 }
cristy66322f02010-05-17 11:40:48 +00002083 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002084 p=FxOperatorPrecedence(expression,exception);
2085 if (p != (const char *) NULL)
2086 {
2087 (void) CopyMagickString(subexpression,expression,(size_t)
2088 (p-expression+1));
2089 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2090 exception);
2091 switch ((unsigned char) *p)
2092 {
2093 case '~':
2094 {
2095 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002096 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002097 return(*beta);
2098 }
2099 case '!':
2100 {
2101 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2102 return(*beta == 0.0 ? 1.0 : 0.0);
2103 }
2104 case '^':
2105 {
2106 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2107 channel,x,y,++p,beta,exception));
2108 return(*beta);
2109 }
2110 case '*':
cristy116af162010-08-13 01:25:47 +00002111 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002112 {
2113 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2114 return(alpha*(*beta));
2115 }
2116 case '/':
2117 {
2118 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2119 if (*beta == 0.0)
2120 {
2121 if (exception->severity == UndefinedException)
2122 (void) ThrowMagickException(exception,GetMagickModule(),
2123 OptionError,"DivideByZero","`%s'",expression);
2124 return(0.0);
2125 }
2126 return(alpha/(*beta));
2127 }
2128 case '%':
2129 {
2130 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2131 *beta=fabs(floor(((double) *beta)+0.5));
2132 if (*beta == 0.0)
2133 {
2134 (void) ThrowMagickException(exception,GetMagickModule(),
2135 OptionError,"DivideByZero","`%s'",expression);
2136 return(0.0);
2137 }
2138 return(fmod((double) alpha,(double) *beta));
2139 }
2140 case '+':
2141 {
2142 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2143 return(alpha+(*beta));
2144 }
2145 case '-':
2146 {
2147 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2148 return(alpha-(*beta));
2149 }
2150 case LeftShiftOperator:
2151 {
2152 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002153 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002154 return(*beta);
2155 }
2156 case RightShiftOperator:
2157 {
2158 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002159 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002160 return(*beta);
2161 }
2162 case '<':
2163 {
2164 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2165 return(alpha < *beta ? 1.0 : 0.0);
2166 }
2167 case LessThanEqualOperator:
2168 {
2169 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2170 return(alpha <= *beta ? 1.0 : 0.0);
2171 }
2172 case '>':
2173 {
2174 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2175 return(alpha > *beta ? 1.0 : 0.0);
2176 }
2177 case GreaterThanEqualOperator:
2178 {
2179 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2180 return(alpha >= *beta ? 1.0 : 0.0);
2181 }
2182 case EqualOperator:
2183 {
2184 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2185 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2186 }
2187 case NotEqualOperator:
2188 {
2189 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2190 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2191 }
2192 case '&':
2193 {
2194 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002195 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002196 return(*beta);
2197 }
2198 case '|':
2199 {
2200 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002201 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002202 return(*beta);
2203 }
2204 case LogicalAndOperator:
2205 {
2206 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2207 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2208 return(*beta);
2209 }
2210 case LogicalOrOperator:
2211 {
2212 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2213 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2214 return(*beta);
2215 }
2216 case '?':
2217 {
2218 MagickRealType
2219 gamma;
2220
2221 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2222 q=subexpression;
2223 p=StringToken(":",&q);
2224 if (q == (char *) NULL)
2225 {
2226 (void) ThrowMagickException(exception,GetMagickModule(),
2227 OptionError,"UnableToParseExpression","`%s'",subexpression);
2228 return(0.0);
2229 }
2230 if (fabs((double) alpha) > MagickEpsilon)
2231 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2232 else
2233 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2234 return(gamma);
2235 }
2236 case '=':
2237 {
2238 char
2239 numeric[MaxTextExtent];
2240
2241 q=subexpression;
2242 while (isalpha((int) ((unsigned char) *q)) != 0)
2243 q++;
2244 if (*q != '\0')
2245 {
2246 (void) ThrowMagickException(exception,GetMagickModule(),
2247 OptionError,"UnableToParseExpression","`%s'",subexpression);
2248 return(0.0);
2249 }
2250 ClearMagickException(exception);
2251 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002252 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002253 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002254 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2255 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2256 subexpression),ConstantString(numeric));
2257 return(*beta);
2258 }
2259 case ',':
2260 {
2261 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2262 return(alpha);
2263 }
2264 case ';':
2265 {
2266 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2267 return(*beta);
2268 }
2269 default:
2270 {
2271 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2272 exception);
2273 return(gamma);
2274 }
2275 }
2276 }
2277 if (strchr("(",(int) *expression) != (char *) NULL)
2278 {
2279 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2280 subexpression[strlen(subexpression)-1]='\0';
2281 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2282 exception);
2283 return(gamma);
2284 }
cristy8b8a3ae2010-10-23 18:49:46 +00002285 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002286 {
2287 case '+':
2288 {
2289 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2290 exception);
2291 return(1.0*gamma);
2292 }
2293 case '-':
2294 {
2295 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2296 exception);
2297 return(-1.0*gamma);
2298 }
2299 case '~':
2300 {
2301 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2302 exception);
cristybb503372010-05-27 20:51:26 +00002303 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002304 }
2305 case 'A':
2306 case 'a':
2307 {
2308 if (LocaleNCompare(expression,"abs",3) == 0)
2309 {
2310 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2311 exception);
2312 return((MagickRealType) fabs((double) alpha));
2313 }
cristyb33454f2011-08-03 02:10:45 +00002314#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002315 if (LocaleNCompare(expression,"acosh",5) == 0)
2316 {
2317 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2318 exception);
2319 return((MagickRealType) acosh((double) alpha));
2320 }
cristyb33454f2011-08-03 02:10:45 +00002321#endif
cristy3ed852e2009-09-05 21:47:34 +00002322 if (LocaleNCompare(expression,"acos",4) == 0)
2323 {
2324 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2325 exception);
2326 return((MagickRealType) acos((double) alpha));
2327 }
cristy43c22f42010-03-30 12:34:07 +00002328#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002329 if (LocaleNCompare(expression,"airy",4) == 0)
2330 {
2331 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2332 exception);
2333 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002334 return(1.0);
2335 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002336 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002337 }
cristy43c22f42010-03-30 12:34:07 +00002338#endif
cristyb33454f2011-08-03 02:10:45 +00002339#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002340 if (LocaleNCompare(expression,"asinh",5) == 0)
2341 {
2342 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2343 exception);
2344 return((MagickRealType) asinh((double) alpha));
2345 }
cristyb33454f2011-08-03 02:10:45 +00002346#endif
cristy3ed852e2009-09-05 21:47:34 +00002347 if (LocaleNCompare(expression,"asin",4) == 0)
2348 {
2349 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2350 exception);
2351 return((MagickRealType) asin((double) alpha));
2352 }
2353 if (LocaleNCompare(expression,"alt",3) == 0)
2354 {
2355 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2356 exception);
cristybb503372010-05-27 20:51:26 +00002357 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002358 }
2359 if (LocaleNCompare(expression,"atan2",5) == 0)
2360 {
2361 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2362 exception);
2363 return((MagickRealType) atan2((double) alpha,(double) *beta));
2364 }
cristyb33454f2011-08-03 02:10:45 +00002365#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002366 if (LocaleNCompare(expression,"atanh",5) == 0)
2367 {
2368 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2369 exception);
2370 return((MagickRealType) atanh((double) alpha));
2371 }
cristyb33454f2011-08-03 02:10:45 +00002372#endif
cristy3ed852e2009-09-05 21:47:34 +00002373 if (LocaleNCompare(expression,"atan",4) == 0)
2374 {
2375 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2376 exception);
2377 return((MagickRealType) atan((double) alpha));
2378 }
2379 if (LocaleCompare(expression,"a") == 0)
2380 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2381 break;
2382 }
2383 case 'B':
2384 case 'b':
2385 {
2386 if (LocaleCompare(expression,"b") == 0)
2387 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2388 break;
2389 }
2390 case 'C':
2391 case 'c':
2392 {
2393 if (LocaleNCompare(expression,"ceil",4) == 0)
2394 {
2395 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2396 exception);
2397 return((MagickRealType) ceil((double) alpha));
2398 }
2399 if (LocaleNCompare(expression,"cosh",4) == 0)
2400 {
2401 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2402 exception);
2403 return((MagickRealType) cosh((double) alpha));
2404 }
2405 if (LocaleNCompare(expression,"cos",3) == 0)
2406 {
2407 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2408 exception);
2409 return((MagickRealType) cos((double) alpha));
2410 }
2411 if (LocaleCompare(expression,"c") == 0)
2412 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2413 break;
2414 }
2415 case 'D':
2416 case 'd':
2417 {
2418 if (LocaleNCompare(expression,"debug",5) == 0)
2419 {
2420 const char
2421 *type;
2422
2423 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2424 exception);
2425 if (fx_info->images->colorspace == CMYKColorspace)
2426 switch (channel)
2427 {
cristy0568ffc2011-07-25 16:54:14 +00002428 case CyanPixelChannel: type="cyan"; break;
2429 case MagentaPixelChannel: type="magenta"; break;
2430 case YellowPixelChannel: type="yellow"; break;
2431 case AlphaPixelChannel: type="opacity"; break;
2432 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002433 default: type="unknown"; break;
2434 }
2435 else
2436 switch (channel)
2437 {
cristy0568ffc2011-07-25 16:54:14 +00002438 case RedPixelChannel: type="red"; break;
2439 case GreenPixelChannel: type="green"; break;
2440 case BluePixelChannel: type="blue"; break;
2441 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002442 default: type="unknown"; break;
2443 }
2444 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2445 if (strlen(subexpression) > 1)
2446 subexpression[strlen(subexpression)-1]='\0';
2447 if (fx_info->file != (FILE *) NULL)
cristy1707c6c2012-01-18 23:30:54 +00002448 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2449 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2450 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002451 return(0.0);
2452 }
cristy5597a8d2011-11-04 00:25:32 +00002453 if (LocaleNCompare(expression,"drc",3) == 0)
2454 {
2455 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2456 exception);
2457 return((MagickRealType) (alpha/(*beta*(alpha-1.0)+1.0)));
2458 }
cristy3ed852e2009-09-05 21:47:34 +00002459 break;
2460 }
2461 case 'E':
2462 case 'e':
2463 {
2464 if (LocaleCompare(expression,"epsilon") == 0)
2465 return((MagickRealType) MagickEpsilon);
2466 if (LocaleNCompare(expression,"exp",3) == 0)
2467 {
2468 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2469 exception);
2470 return((MagickRealType) exp((double) alpha));
2471 }
2472 if (LocaleCompare(expression,"e") == 0)
2473 return((MagickRealType) 2.7182818284590452354);
2474 break;
2475 }
2476 case 'F':
2477 case 'f':
2478 {
2479 if (LocaleNCompare(expression,"floor",5) == 0)
2480 {
2481 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2482 exception);
2483 return((MagickRealType) floor((double) alpha));
2484 }
2485 break;
2486 }
2487 case 'G':
2488 case 'g':
2489 {
cristy9eeedea2011-11-02 19:04:05 +00002490 if (LocaleNCompare(expression,"gauss",5) == 0)
2491 {
2492 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2493 exception);
2494 gamma=exp((double) (-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2495 return((MagickRealType) gamma);
2496 }
cristyb0aad4c2011-11-02 19:30:35 +00002497 if (LocaleNCompare(expression,"gcd",3) == 0)
2498 {
2499 MagickOffsetType
2500 gcd;
2501
2502 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2503 exception);
cristy1707c6c2012-01-18 23:30:54 +00002504 gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2505 0.5));
cristyb0aad4c2011-11-02 19:30:35 +00002506 return((MagickRealType) gcd);
2507 }
cristy3ed852e2009-09-05 21:47:34 +00002508 if (LocaleCompare(expression,"g") == 0)
2509 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2510 break;
2511 }
2512 case 'H':
2513 case 'h':
2514 {
2515 if (LocaleCompare(expression,"h") == 0)
2516 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2517 if (LocaleCompare(expression,"hue") == 0)
2518 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2519 if (LocaleNCompare(expression,"hypot",5) == 0)
2520 {
2521 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2522 exception);
2523 return((MagickRealType) hypot((double) alpha,(double) *beta));
2524 }
2525 break;
2526 }
2527 case 'K':
2528 case 'k':
2529 {
2530 if (LocaleCompare(expression,"k") == 0)
2531 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2532 break;
2533 }
2534 case 'I':
2535 case 'i':
2536 {
2537 if (LocaleCompare(expression,"intensity") == 0)
2538 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2539 if (LocaleNCompare(expression,"int",3) == 0)
2540 {
2541 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2542 exception);
cristy16788e42010-08-13 13:44:26 +00002543 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002544 }
cristy82b20722011-11-05 21:52:36 +00002545#if defined(MAGICKCORE_HAVE_ISNAN)
cristy639399c2011-11-02 19:16:15 +00002546 if (LocaleNCompare(expression,"isnan",5) == 0)
2547 {
2548 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2549 exception);
cristy17a10202011-11-02 19:17:04 +00002550 return((MagickRealType) !!isnan((double) alpha));
cristy639399c2011-11-02 19:16:15 +00002551 }
cristy82b20722011-11-05 21:52:36 +00002552#endif
cristy3ed852e2009-09-05 21:47:34 +00002553 if (LocaleCompare(expression,"i") == 0)
2554 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2555 break;
2556 }
2557 case 'J':
2558 case 'j':
2559 {
2560 if (LocaleCompare(expression,"j") == 0)
2561 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002562#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002563 if (LocaleNCompare(expression,"j0",2) == 0)
2564 {
2565 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2566 exception);
2567 return((MagickRealType) j0((double) alpha));
2568 }
cristy161b9262010-03-20 19:34:32 +00002569#endif
2570#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002571 if (LocaleNCompare(expression,"j1",2) == 0)
2572 {
2573 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2574 exception);
2575 return((MagickRealType) j1((double) alpha));
2576 }
cristy161b9262010-03-20 19:34:32 +00002577#endif
cristyaa018fa2010-04-08 23:03:54 +00002578#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002579 if (LocaleNCompare(expression,"jinc",4) == 0)
2580 {
2581 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2582 exception);
cristy0946a822010-03-12 17:14:58 +00002583 if (alpha == 0.0)
2584 return(1.0);
cristy1707c6c2012-01-18 23:30:54 +00002585 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/(MagickPI*
2586 alpha));
cristyfce2f7b2010-03-12 00:29:49 +00002587 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002588 }
cristyaa018fa2010-04-08 23:03:54 +00002589#endif
cristy3ed852e2009-09-05 21:47:34 +00002590 break;
2591 }
2592 case 'L':
2593 case 'l':
2594 {
2595 if (LocaleNCompare(expression,"ln",2) == 0)
2596 {
2597 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2598 exception);
2599 return((MagickRealType) log((double) alpha));
2600 }
cristyc8ed5322010-08-31 12:07:59 +00002601 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002602 {
cristyc8ed5322010-08-31 12:07:59 +00002603 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002604 exception);
2605 return((MagickRealType) log10((double) alpha))/log10(2.0);
2606 }
2607 if (LocaleNCompare(expression,"log",3) == 0)
2608 {
2609 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2610 exception);
2611 return((MagickRealType) log10((double) alpha));
2612 }
2613 if (LocaleCompare(expression,"lightness") == 0)
2614 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2615 break;
2616 }
2617 case 'M':
2618 case 'm':
2619 {
2620 if (LocaleCompare(expression,"MaxRGB") == 0)
2621 return((MagickRealType) QuantumRange);
2622 if (LocaleNCompare(expression,"maxima",6) == 0)
2623 break;
2624 if (LocaleNCompare(expression,"max",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002625 {
2626 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2627 exception);
2628 return(alpha > *beta ? alpha : *beta);
2629 }
cristy3ed852e2009-09-05 21:47:34 +00002630 if (LocaleNCompare(expression,"minima",6) == 0)
2631 break;
2632 if (LocaleNCompare(expression,"min",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002633 {
2634 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2635 exception);
2636 return(alpha < *beta ? alpha : *beta);
2637 }
cristy3ed852e2009-09-05 21:47:34 +00002638 if (LocaleNCompare(expression,"mod",3) == 0)
2639 {
2640 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2641 exception);
cristy984049c2011-11-03 18:34:58 +00002642 gamma=alpha-floor((double) (alpha/(*beta)))*(*beta);
2643 return(gamma);
cristy3ed852e2009-09-05 21:47:34 +00002644 }
2645 if (LocaleCompare(expression,"m") == 0)
2646 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2647 break;
2648 }
2649 case 'N':
2650 case 'n':
2651 {
cristyad3502e2011-11-02 19:10:45 +00002652 if (LocaleNCompare(expression,"not",3) == 0)
2653 {
2654 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2655 exception);
2656 return((MagickRealType) (alpha < MagickEpsilon));
2657 }
cristy3ed852e2009-09-05 21:47:34 +00002658 if (LocaleCompare(expression,"n") == 0)
2659 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2660 break;
2661 }
2662 case 'O':
2663 case 'o':
2664 {
2665 if (LocaleCompare(expression,"Opaque") == 0)
2666 return(1.0);
2667 if (LocaleCompare(expression,"o") == 0)
2668 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2669 break;
2670 }
2671 case 'P':
2672 case 'p':
2673 {
cristy670aa3c2011-11-03 00:54:00 +00002674 if (LocaleCompare(expression,"phi") == 0)
2675 return((MagickRealType) MagickPHI);
cristy3ed852e2009-09-05 21:47:34 +00002676 if (LocaleCompare(expression,"pi") == 0)
2677 return((MagickRealType) MagickPI);
2678 if (LocaleNCompare(expression,"pow",3) == 0)
2679 {
2680 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2681 exception);
2682 return((MagickRealType) pow((double) alpha,(double) *beta));
2683 }
2684 if (LocaleCompare(expression,"p") == 0)
2685 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2686 break;
2687 }
2688 case 'Q':
2689 case 'q':
2690 {
2691 if (LocaleCompare(expression,"QuantumRange") == 0)
2692 return((MagickRealType) QuantumRange);
2693 if (LocaleCompare(expression,"QuantumScale") == 0)
2694 return((MagickRealType) QuantumScale);
2695 break;
2696 }
2697 case 'R':
2698 case 'r':
2699 {
2700 if (LocaleNCompare(expression,"rand",4) == 0)
2701 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2702 if (LocaleNCompare(expression,"round",5) == 0)
2703 {
2704 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2705 exception);
cristy16788e42010-08-13 13:44:26 +00002706 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002707 }
2708 if (LocaleCompare(expression,"r") == 0)
2709 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2710 break;
2711 }
2712 case 'S':
2713 case 's':
2714 {
2715 if (LocaleCompare(expression,"saturation") == 0)
2716 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2717 if (LocaleNCompare(expression,"sign",4) == 0)
2718 {
2719 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2720 exception);
2721 return(alpha < 0.0 ? -1.0 : 1.0);
2722 }
cristya6a09e72010-03-02 14:51:02 +00002723 if (LocaleNCompare(expression,"sinc",4) == 0)
2724 {
2725 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2726 exception);
2727 if (alpha == 0)
2728 return(1.0);
2729 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2730 (MagickPI*alpha));
2731 return(gamma);
2732 }
cristy3ed852e2009-09-05 21:47:34 +00002733 if (LocaleNCompare(expression,"sinh",4) == 0)
2734 {
2735 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2736 exception);
2737 return((MagickRealType) sinh((double) alpha));
2738 }
2739 if (LocaleNCompare(expression,"sin",3) == 0)
2740 {
2741 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2742 exception);
2743 return((MagickRealType) sin((double) alpha));
2744 }
2745 if (LocaleNCompare(expression,"sqrt",4) == 0)
2746 {
2747 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2748 exception);
2749 return((MagickRealType) sqrt((double) alpha));
2750 }
cristy9eeedea2011-11-02 19:04:05 +00002751 if (LocaleNCompare(expression,"squish",6) == 0)
2752 {
2753 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2754 exception);
2755 return((MagickRealType) (1.0/(1.0+exp((double) (4.0*alpha)))));
2756 }
cristy3ed852e2009-09-05 21:47:34 +00002757 if (LocaleCompare(expression,"s") == 0)
2758 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2759 break;
2760 }
2761 case 'T':
2762 case 't':
2763 {
2764 if (LocaleNCompare(expression,"tanh",4) == 0)
2765 {
2766 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2767 exception);
2768 return((MagickRealType) tanh((double) alpha));
2769 }
2770 if (LocaleNCompare(expression,"tan",3) == 0)
2771 {
2772 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2773 exception);
2774 return((MagickRealType) tan((double) alpha));
2775 }
2776 if (LocaleCompare(expression,"Transparent") == 0)
2777 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002778 if (LocaleNCompare(expression,"trunc",5) == 0)
2779 {
2780 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2781 exception);
2782 if (alpha >= 0.0)
2783 return((MagickRealType) floor((double) alpha));
2784 return((MagickRealType) ceil((double) alpha));
2785 }
cristy3ed852e2009-09-05 21:47:34 +00002786 if (LocaleCompare(expression,"t") == 0)
2787 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2788 break;
2789 }
2790 case 'U':
2791 case 'u':
2792 {
2793 if (LocaleCompare(expression,"u") == 0)
2794 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2795 break;
2796 }
2797 case 'V':
2798 case 'v':
2799 {
2800 if (LocaleCompare(expression,"v") == 0)
2801 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2802 break;
2803 }
2804 case 'W':
2805 case 'w':
2806 {
cristy9eeedea2011-11-02 19:04:05 +00002807 if (LocaleNCompare(expression,"while",5) == 0)
2808 {
2809 do
2810 {
2811 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2812 exception);
2813 } while (fabs((double) alpha) >= MagickEpsilon);
2814 return((MagickRealType) *beta);
2815 }
cristy3ed852e2009-09-05 21:47:34 +00002816 if (LocaleCompare(expression,"w") == 0)
2817 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2818 break;
2819 }
2820 case 'Y':
2821 case 'y':
2822 {
2823 if (LocaleCompare(expression,"y") == 0)
2824 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2825 break;
2826 }
2827 case 'Z':
2828 case 'z':
2829 {
2830 if (LocaleCompare(expression,"z") == 0)
2831 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2832 break;
2833 }
2834 default:
2835 break;
2836 }
2837 q=(char *) expression;
cristydbdd0e32011-11-04 23:29:40 +00002838 alpha=InterpretSiPrefixValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002839 if (q == expression)
2840 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2841 return(alpha);
2842}
2843
cristy7832dc22011-09-05 01:21:53 +00002844MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
cristy3ed852e2009-09-05 21:47:34 +00002845 MagickRealType *alpha,ExceptionInfo *exception)
2846{
2847 MagickBooleanType
2848 status;
2849
cristy541ae572011-08-05 19:08:59 +00002850 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2851 exception);
cristy3ed852e2009-09-05 21:47:34 +00002852 return(status);
2853}
2854
2855MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2856 MagickRealType *alpha,ExceptionInfo *exception)
2857{
2858 FILE
2859 *file;
2860
2861 MagickBooleanType
2862 status;
2863
2864 file=fx_info->file;
2865 fx_info->file=(FILE *) NULL;
cristy541ae572011-08-05 19:08:59 +00002866 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2867 exception);
cristy3ed852e2009-09-05 21:47:34 +00002868 fx_info->file=file;
2869 return(status);
2870}
2871
cristy7832dc22011-09-05 01:21:53 +00002872MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002873 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002874 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002875{
2876 MagickRealType
2877 beta;
2878
2879 beta=0.0;
2880 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2881 exception);
2882 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2883}
2884
2885/*
2886%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2887% %
2888% %
2889% %
2890% F x I m a g e %
2891% %
2892% %
2893% %
2894%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2895%
2896% FxImage() applies a mathematical expression to the specified image.
2897%
2898% The format of the FxImage method is:
2899%
2900% Image *FxImage(const Image *image,const char *expression,
2901% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002902%
2903% A description of each parameter follows:
2904%
2905% o image: the image.
2906%
cristy3ed852e2009-09-05 21:47:34 +00002907% o expression: A mathematical expression.
2908%
2909% o exception: return any errors or warnings in this structure.
2910%
2911*/
2912
2913static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2914{
cristybb503372010-05-27 20:51:26 +00002915 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002916 i;
2917
2918 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002919 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002920 if (fx_info[i] != (FxInfo *) NULL)
2921 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002922 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002923 return(fx_info);
2924}
2925
2926static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2927 ExceptionInfo *exception)
2928{
2929 char
2930 *fx_expression;
2931
2932 FxInfo
2933 **fx_info;
2934
2935 MagickRealType
2936 alpha;
2937
cristybb503372010-05-27 20:51:26 +00002938 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002939 i;
2940
cristybb503372010-05-27 20:51:26 +00002941 size_t
cristy3ed852e2009-09-05 21:47:34 +00002942 number_threads;
2943
2944 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002945 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002946 if (fx_info == (FxInfo **) NULL)
2947 return((FxInfo **) NULL);
2948 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2949 if (*expression != '@')
2950 fx_expression=ConstantString(expression);
2951 else
2952 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00002953 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002954 {
2955 fx_info[i]=AcquireFxInfo(image,fx_expression);
2956 if (fx_info[i] == (FxInfo *) NULL)
2957 return(DestroyFxThreadSet(fx_info));
2958 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2959 }
2960 fx_expression=DestroyString(fx_expression);
2961 return(fx_info);
2962}
2963
2964MagickExport Image *FxImage(const Image *image,const char *expression,
2965 ExceptionInfo *exception)
2966{
cristy3ed852e2009-09-05 21:47:34 +00002967#define FxImageTag "Fx/Image"
2968
cristyfa112112010-01-04 17:48:07 +00002969 CacheView
cristy79cedc72011-07-25 00:41:15 +00002970 *fx_view,
2971 *image_view;
cristyfa112112010-01-04 17:48:07 +00002972
cristy3ed852e2009-09-05 21:47:34 +00002973 FxInfo
cristyfa112112010-01-04 17:48:07 +00002974 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00002975
2976 Image
2977 *fx_image;
2978
cristy3ed852e2009-09-05 21:47:34 +00002979 MagickBooleanType
2980 status;
2981
cristybb503372010-05-27 20:51:26 +00002982 MagickOffsetType
2983 progress;
2984
cristy3ed852e2009-09-05 21:47:34 +00002985 MagickRealType
2986 alpha;
2987
cristybb503372010-05-27 20:51:26 +00002988 ssize_t
2989 y;
2990
cristy3ed852e2009-09-05 21:47:34 +00002991 assert(image != (Image *) NULL);
2992 assert(image->signature == MagickSignature);
2993 if (image->debug != MagickFalse)
2994 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00002995 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00002996 if (fx_image == (Image *) NULL)
2997 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00002998 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002999 {
cristy3ed852e2009-09-05 21:47:34 +00003000 fx_image=DestroyImage(fx_image);
3001 return((Image *) NULL);
3002 }
3003 fx_info=AcquireFxThreadSet(image,expression,exception);
3004 if (fx_info == (FxInfo **) NULL)
3005 {
3006 fx_image=DestroyImage(fx_image);
3007 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3008 }
3009 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3010 if (status == MagickFalse)
3011 {
3012 fx_image=DestroyImage(fx_image);
3013 fx_info=DestroyFxThreadSet(fx_info);
3014 return((Image *) NULL);
3015 }
3016 /*
3017 Fx image.
3018 */
3019 status=MagickTrue;
3020 progress=0;
cristy79cedc72011-07-25 00:41:15 +00003021 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003022 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003023#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003024 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003025#endif
cristybb503372010-05-27 20:51:26 +00003026 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003027 {
cristy5c9e6f22010-09-17 17:31:01 +00003028 const int
3029 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003030
cristy79cedc72011-07-25 00:41:15 +00003031 register const Quantum
3032 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003033
cristy4c08aed2011-07-01 19:47:50 +00003034 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003035 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003036
cristy79cedc72011-07-25 00:41:15 +00003037 register ssize_t
3038 x;
3039
cristy3ed852e2009-09-05 21:47:34 +00003040 if (status == MagickFalse)
3041 continue;
cristy79cedc72011-07-25 00:41:15 +00003042 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003043 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003044 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003045 {
3046 status=MagickFalse;
3047 continue;
3048 }
cristybb503372010-05-27 20:51:26 +00003049 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003050 {
cristy79cedc72011-07-25 00:41:15 +00003051 register ssize_t
3052 i;
3053
3054 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3055 {
3056 MagickRealType
3057 alpha;
3058
3059 PixelChannel
3060 channel;
3061
3062 PixelTrait
3063 fx_traits,
3064 traits;
3065
cristye2a912b2011-12-05 20:02:07 +00003066 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003067 traits=GetPixelChannelMapTraits(image,channel);
cristy79cedc72011-07-25 00:41:15 +00003068 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
cristy010d7d12011-08-31 01:02:48 +00003069 if ((traits == UndefinedPixelTrait) ||
3070 (fx_traits == UndefinedPixelTrait))
cristy79cedc72011-07-25 00:41:15 +00003071 continue;
cristyd09f8802012-02-04 16:44:10 +00003072 if (((fx_traits & CopyPixelTrait) != 0) ||
3073 (GetPixelMask(image,p) != 0))
cristy79cedc72011-07-25 00:41:15 +00003074 {
cristy0beccfa2011-09-25 20:47:53 +00003075 SetPixelChannel(fx_image,channel,p[i],q);
cristy79cedc72011-07-25 00:41:15 +00003076 continue;
3077 }
3078 alpha=0.0;
cristya382aca2011-12-06 18:22:48 +00003079 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3080 exception);
cristyb3a73b52011-07-26 01:34:43 +00003081 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003082 }
3083 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003084 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003085 }
3086 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3087 status=MagickFalse;
3088 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3089 {
3090 MagickBooleanType
3091 proceed;
3092
cristyb5d5f722009-11-04 03:03:49 +00003093#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003094 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003095#endif
3096 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3097 if (proceed == MagickFalse)
3098 status=MagickFalse;
3099 }
3100 }
cristy3ed852e2009-09-05 21:47:34 +00003101 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003102 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003103 fx_info=DestroyFxThreadSet(fx_info);
3104 if (status == MagickFalse)
3105 fx_image=DestroyImage(fx_image);
3106 return(fx_image);
3107}
3108
3109/*
3110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3111% %
3112% %
3113% %
3114% I m p l o d e I m a g e %
3115% %
3116% %
3117% %
3118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3119%
3120% ImplodeImage() creates a new image that is a copy of an existing
3121% one with the image pixels "implode" by the specified percentage. It
3122% allocates the memory necessary for the new Image structure and returns a
3123% pointer to the new image.
3124%
3125% The format of the ImplodeImage method is:
3126%
3127% Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003128% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003129%
3130% A description of each parameter follows:
3131%
3132% o implode_image: Method ImplodeImage returns a pointer to the image
3133% after it is implode. A null image is returned if there is a memory
3134% shortage.
3135%
3136% o image: the image.
3137%
3138% o amount: Define the extent of the implosion.
3139%
cristy76f512e2011-09-12 01:26:56 +00003140% o method: the pixel interpolation method.
3141%
cristy3ed852e2009-09-05 21:47:34 +00003142% o exception: return any errors or warnings in this structure.
3143%
3144*/
3145MagickExport Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003146 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003147{
3148#define ImplodeImageTag "Implode/Image"
3149
cristyfa112112010-01-04 17:48:07 +00003150 CacheView
3151 *image_view,
3152 *implode_view;
3153
cristy3ed852e2009-09-05 21:47:34 +00003154 Image
3155 *implode_image;
3156
cristy3ed852e2009-09-05 21:47:34 +00003157 MagickBooleanType
3158 status;
3159
cristybb503372010-05-27 20:51:26 +00003160 MagickOffsetType
3161 progress;
3162
cristy3ed852e2009-09-05 21:47:34 +00003163 MagickRealType
3164 radius;
3165
3166 PointInfo
3167 center,
3168 scale;
3169
cristybb503372010-05-27 20:51:26 +00003170 ssize_t
3171 y;
3172
cristy3ed852e2009-09-05 21:47:34 +00003173 /*
3174 Initialize implode image attributes.
3175 */
3176 assert(image != (Image *) NULL);
3177 assert(image->signature == MagickSignature);
3178 if (image->debug != MagickFalse)
3179 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3180 assert(exception != (ExceptionInfo *) NULL);
3181 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00003182 implode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3183 exception);
cristy3ed852e2009-09-05 21:47:34 +00003184 if (implode_image == (Image *) NULL)
3185 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003186 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003187 {
cristy3ed852e2009-09-05 21:47:34 +00003188 implode_image=DestroyImage(implode_image);
3189 return((Image *) NULL);
3190 }
cristy4c08aed2011-07-01 19:47:50 +00003191 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003192 implode_image->matte=MagickTrue;
3193 /*
3194 Compute scaling factor.
3195 */
3196 scale.x=1.0;
3197 scale.y=1.0;
3198 center.x=0.5*image->columns;
3199 center.y=0.5*image->rows;
3200 radius=center.x;
3201 if (image->columns > image->rows)
3202 scale.y=(double) image->columns/(double) image->rows;
3203 else
3204 if (image->columns < image->rows)
3205 {
3206 scale.x=(double) image->rows/(double) image->columns;
3207 radius=center.y;
3208 }
3209 /*
3210 Implode image.
3211 */
3212 status=MagickTrue;
3213 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003214 image_view=AcquireCacheView(image);
3215 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003216#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003217 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003218#endif
cristybb503372010-05-27 20:51:26 +00003219 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003220 {
cristy3ed852e2009-09-05 21:47:34 +00003221 MagickRealType
3222 distance;
3223
3224 PointInfo
3225 delta;
3226
cristy6d188022011-09-12 13:23:33 +00003227 register const Quantum
3228 *restrict p;
3229
cristybb503372010-05-27 20:51:26 +00003230 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003231 x;
3232
cristy4c08aed2011-07-01 19:47:50 +00003233 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003234 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003235
3236 if (status == MagickFalse)
3237 continue;
cristy6d188022011-09-12 13:23:33 +00003238 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003239 q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003240 exception);
cristy6d188022011-09-12 13:23:33 +00003241 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003242 {
3243 status=MagickFalse;
3244 continue;
3245 }
cristy3ed852e2009-09-05 21:47:34 +00003246 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00003247 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003248 {
cristy6d188022011-09-12 13:23:33 +00003249 register ssize_t
3250 i;
3251
cristy3ed852e2009-09-05 21:47:34 +00003252 /*
3253 Determine if the pixel is within an ellipse.
3254 */
cristy10a6c612012-01-29 21:41:05 +00003255 if (GetPixelMask(image,p) != 0)
3256 {
3257 p+=GetPixelChannels(image);
3258 q+=GetPixelChannels(implode_image);
3259 continue;
3260 }
cristy3ed852e2009-09-05 21:47:34 +00003261 delta.x=scale.x*(double) (x-center.x);
3262 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00003263 if (distance >= (radius*radius))
cristya6d7a9b2012-01-18 20:04:48 +00003264 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy6d188022011-09-12 13:23:33 +00003265 {
cristya6d7a9b2012-01-18 20:04:48 +00003266 PixelChannel
3267 channel;
3268
cristy1707c6c2012-01-18 23:30:54 +00003269 PixelTrait
3270 implode_traits,
3271 traits;
3272
cristya6d7a9b2012-01-18 20:04:48 +00003273 channel=GetPixelChannelMapChannel(image,i);
cristy1707c6c2012-01-18 23:30:54 +00003274 traits=GetPixelChannelMapTraits(image,channel);
3275 implode_traits=GetPixelChannelMapTraits(implode_image,channel);
3276 if ((traits == UndefinedPixelTrait) ||
3277 (implode_traits == UndefinedPixelTrait))
3278 continue;
cristya6d7a9b2012-01-18 20:04:48 +00003279 SetPixelChannel(implode_image,channel,p[i],q);
cristy6d188022011-09-12 13:23:33 +00003280 }
3281 else
cristy3ed852e2009-09-05 21:47:34 +00003282 {
3283 double
3284 factor;
3285
3286 /*
3287 Implode the pixel.
3288 */
3289 factor=1.0;
3290 if (distance > 0.0)
cristy1707c6c2012-01-18 23:30:54 +00003291 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/radius/
3292 2)),-amount);
cristy76f512e2011-09-12 01:26:56 +00003293 status=InterpolatePixelChannels(image,image_view,implode_image,method,
3294 (double) (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3295 scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00003296 }
cristy6d188022011-09-12 13:23:33 +00003297 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003298 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003299 }
3300 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3301 status=MagickFalse;
3302 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3303 {
3304 MagickBooleanType
3305 proceed;
3306
cristyb5d5f722009-11-04 03:03:49 +00003307#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003308 #pragma omp critical (MagickCore_ImplodeImage)
cristy3ed852e2009-09-05 21:47:34 +00003309#endif
3310 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3311 if (proceed == MagickFalse)
3312 status=MagickFalse;
3313 }
3314 }
3315 implode_view=DestroyCacheView(implode_view);
3316 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003317 if (status == MagickFalse)
3318 implode_image=DestroyImage(implode_image);
3319 return(implode_image);
3320}
3321
3322/*
3323%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3324% %
3325% %
3326% %
3327% M o r p h I m a g e s %
3328% %
3329% %
3330% %
3331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3332%
3333% The MorphImages() method requires a minimum of two images. The first
3334% image is transformed into the second by a number of intervening images
3335% as specified by frames.
3336%
3337% The format of the MorphImage method is:
3338%
cristybb503372010-05-27 20:51:26 +00003339% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003340% ExceptionInfo *exception)
3341%
3342% A description of each parameter follows:
3343%
3344% o image: the image.
3345%
3346% o number_frames: Define the number of in-between image to generate.
3347% The more in-between frames, the smoother the morph.
3348%
3349% o exception: return any errors or warnings in this structure.
3350%
3351*/
3352MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003353 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003354{
3355#define MorphImageTag "Morph/Image"
3356
3357 Image
3358 *morph_image,
3359 *morph_images;
3360
cristy9d314ff2011-03-09 01:30:28 +00003361 MagickBooleanType
3362 status;
cristy3ed852e2009-09-05 21:47:34 +00003363
3364 MagickOffsetType
3365 scene;
3366
3367 MagickRealType
3368 alpha,
3369 beta;
3370
3371 register const Image
3372 *next;
3373
cristybb503372010-05-27 20:51:26 +00003374 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003375 i;
3376
cristy9d314ff2011-03-09 01:30:28 +00003377 ssize_t
3378 y;
cristy3ed852e2009-09-05 21:47:34 +00003379
3380 /*
3381 Clone first frame in sequence.
3382 */
3383 assert(image != (Image *) NULL);
3384 assert(image->signature == MagickSignature);
3385 if (image->debug != MagickFalse)
3386 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3387 assert(exception != (ExceptionInfo *) NULL);
3388 assert(exception->signature == MagickSignature);
3389 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3390 if (morph_images == (Image *) NULL)
3391 return((Image *) NULL);
3392 if (GetNextImageInList(image) == (Image *) NULL)
3393 {
3394 /*
3395 Morph single image.
3396 */
cristybb503372010-05-27 20:51:26 +00003397 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003398 {
3399 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3400 if (morph_image == (Image *) NULL)
3401 {
3402 morph_images=DestroyImageList(morph_images);
3403 return((Image *) NULL);
3404 }
3405 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003406 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003407 {
cristy8b27a6d2010-02-14 03:31:15 +00003408 MagickBooleanType
3409 proceed;
3410
cristybb503372010-05-27 20:51:26 +00003411 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3412 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003413 if (proceed == MagickFalse)
3414 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003415 }
3416 }
3417 return(GetFirstImageInList(morph_images));
3418 }
3419 /*
3420 Morph image sequence.
3421 */
3422 status=MagickTrue;
3423 scene=0;
3424 next=image;
3425 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3426 {
cristybb503372010-05-27 20:51:26 +00003427 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003428 {
3429 CacheView
3430 *image_view,
3431 *morph_view;
3432
3433 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3434 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003435 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristyaa2c16c2012-03-25 22:21:35 +00003436 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*next->rows+beta*
3437 GetNextImageInList(next)->rows+0.5),next->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00003438 if (morph_image == (Image *) NULL)
3439 {
3440 morph_images=DestroyImageList(morph_images);
3441 return((Image *) NULL);
3442 }
cristy1707c6c2012-01-18 23:30:54 +00003443 status=SetImageStorageClass(morph_image,DirectClass,exception);
3444 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003445 {
cristy3ed852e2009-09-05 21:47:34 +00003446 morph_image=DestroyImage(morph_image);
3447 return((Image *) NULL);
3448 }
3449 AppendImageToList(&morph_images,morph_image);
3450 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003451 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
cristyaa2c16c2012-03-25 22:21:35 +00003452 morph_images->rows,GetNextImageInList(next)->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00003453 if (morph_image == (Image *) NULL)
3454 {
3455 morph_images=DestroyImageList(morph_images);
3456 return((Image *) NULL);
3457 }
3458 image_view=AcquireCacheView(morph_image);
3459 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003460#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003461 #pragma omp parallel for schedule(static,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003462#endif
cristybb503372010-05-27 20:51:26 +00003463 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003464 {
3465 MagickBooleanType
3466 sync;
3467
cristy4c08aed2011-07-01 19:47:50 +00003468 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003469 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003470
cristybb503372010-05-27 20:51:26 +00003471 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003472 x;
3473
cristy4c08aed2011-07-01 19:47:50 +00003474 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003475 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003476
3477 if (status == MagickFalse)
3478 continue;
3479 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3480 exception);
3481 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3482 exception);
cristy4c08aed2011-07-01 19:47:50 +00003483 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003484 {
3485 status=MagickFalse;
3486 continue;
3487 }
cristybb503372010-05-27 20:51:26 +00003488 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003489 {
cristy1707c6c2012-01-18 23:30:54 +00003490 register ssize_t
3491 i;
3492
cristy10a6c612012-01-29 21:41:05 +00003493 for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
cristy1707c6c2012-01-18 23:30:54 +00003494 {
3495 PixelChannel
3496 channel;
3497
3498 PixelTrait
3499 morph_traits,
3500 traits;
3501
3502 channel=GetPixelChannelMapChannel(image,i);
3503 traits=GetPixelChannelMapTraits(image,channel);
3504 morph_traits=GetPixelChannelMapTraits(morph_image,channel);
3505 if ((traits == UndefinedPixelTrait) ||
3506 (morph_traits == UndefinedPixelTrait))
3507 continue;
cristyd09f8802012-02-04 16:44:10 +00003508 if (((morph_traits & CopyPixelTrait) != 0) ||
3509 (GetPixelMask(image,p) != 0))
cristy1707c6c2012-01-18 23:30:54 +00003510 {
3511 SetPixelChannel(morph_image,channel,p[i],q);
3512 continue;
3513 }
3514 SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3515 GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3516 }
cristyed231572011-07-14 02:18:59 +00003517 p+=GetPixelChannels(morph_image);
3518 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003519 }
3520 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3521 if (sync == MagickFalse)
3522 status=MagickFalse;
3523 }
3524 morph_view=DestroyCacheView(morph_view);
3525 image_view=DestroyCacheView(image_view);
3526 morph_image=DestroyImage(morph_image);
3527 }
cristybb503372010-05-27 20:51:26 +00003528 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003529 break;
3530 /*
3531 Clone last frame in sequence.
3532 */
3533 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3534 if (morph_image == (Image *) NULL)
3535 {
3536 morph_images=DestroyImageList(morph_images);
3537 return((Image *) NULL);
3538 }
3539 AppendImageToList(&morph_images,morph_image);
3540 morph_images=GetLastImageInList(morph_images);
3541 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3542 {
3543 MagickBooleanType
3544 proceed;
3545
cristyb5d5f722009-11-04 03:03:49 +00003546#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003547 #pragma omp critical (MagickCore_MorphImages)
cristy3ed852e2009-09-05 21:47:34 +00003548#endif
3549 proceed=SetImageProgress(image,MorphImageTag,scene,
3550 GetImageListLength(image));
3551 if (proceed == MagickFalse)
3552 status=MagickFalse;
3553 }
3554 scene++;
3555 }
3556 if (GetNextImageInList(next) != (Image *) NULL)
3557 {
3558 morph_images=DestroyImageList(morph_images);
3559 return((Image *) NULL);
3560 }
3561 return(GetFirstImageInList(morph_images));
3562}
3563
3564/*
3565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3566% %
3567% %
3568% %
3569% P l a s m a I m a g e %
3570% %
3571% %
3572% %
3573%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3574%
3575% PlasmaImage() initializes an image with plasma fractal values. The image
3576% must be initialized with a base color and the random number generator
3577% seeded before this method is called.
3578%
3579% The format of the PlasmaImage method is:
3580%
3581% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristy5cbc0162011-08-29 00:36:28 +00003582% size_t attenuate,size_t depth,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003583%
3584% A description of each parameter follows:
3585%
3586% o image: the image.
3587%
3588% o segment: Define the region to apply plasma fractals values.
3589%
glennrp7dae1ca2010-09-16 12:17:35 +00003590% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003591%
3592% o depth: Limit the plasma recursion depth.
3593%
cristy5cbc0162011-08-29 00:36:28 +00003594% o exception: return any errors or warnings in this structure.
3595%
cristy3ed852e2009-09-05 21:47:34 +00003596*/
3597
3598static inline Quantum PlasmaPixel(RandomInfo *random_info,
3599 const MagickRealType pixel,const MagickRealType noise)
3600{
3601 Quantum
3602 plasma;
3603
cristyce70c172010-01-07 17:15:30 +00003604 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003605 noise/2.0);
3606 return(plasma);
3607}
3608
cristyda1f9c12011-10-02 21:39:49 +00003609static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
3610 CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3611 const SegmentInfo *segment,size_t attenuate,size_t depth,
3612 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003613{
cristy3ed852e2009-09-05 21:47:34 +00003614 MagickRealType
3615 plasma;
3616
cristyda1f9c12011-10-02 21:39:49 +00003617 PixelChannel
3618 channel;
3619
3620 PixelTrait
3621 traits;
3622
3623 register const Quantum
3624 *restrict u,
3625 *restrict v;
3626
3627 register Quantum
3628 *restrict q;
3629
3630 register ssize_t
3631 i;
cristy3ed852e2009-09-05 21:47:34 +00003632
cristy9d314ff2011-03-09 01:30:28 +00003633 ssize_t
3634 x,
3635 x_mid,
3636 y,
3637 y_mid;
3638
cristy3ed852e2009-09-05 21:47:34 +00003639 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3640 return(MagickTrue);
3641 if (depth != 0)
3642 {
3643 SegmentInfo
3644 local_info;
3645
3646 /*
3647 Divide the area into quadrants and recurse.
3648 */
3649 depth--;
3650 attenuate++;
cristybb503372010-05-27 20:51:26 +00003651 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3652 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003653 local_info=(*segment);
3654 local_info.x2=(double) x_mid;
3655 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003656 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3657 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003658 local_info=(*segment);
3659 local_info.y1=(double) y_mid;
3660 local_info.x2=(double) x_mid;
cristyda1f9c12011-10-02 21:39:49 +00003661 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3662 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003663 local_info=(*segment);
3664 local_info.x1=(double) x_mid;
3665 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003666 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3667 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003668 local_info=(*segment);
3669 local_info.x1=(double) x_mid;
3670 local_info.y1=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003671 return(PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3672 &local_info,attenuate,depth,exception));
cristy3ed852e2009-09-05 21:47:34 +00003673 }
cristybb503372010-05-27 20:51:26 +00003674 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3675 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003676 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3677 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3678 return(MagickFalse);
3679 /*
3680 Average pixels and apply plasma.
3681 */
cristy3ed852e2009-09-05 21:47:34 +00003682 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3683 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3684 {
cristy3ed852e2009-09-05 21:47:34 +00003685 /*
3686 Left pixel.
3687 */
cristybb503372010-05-27 20:51:26 +00003688 x=(ssize_t) ceil(segment->x1-0.5);
cristy1707c6c2012-01-18 23:30:54 +00003689 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3690 exception);
3691 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3692 exception);
cristyc5c6f662010-09-22 14:23:02 +00003693 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003694 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3695 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003696 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003697 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3698 {
cristye2a912b2011-12-05 20:02:07 +00003699 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003700 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003701 if (traits == UndefinedPixelTrait)
3702 continue;
3703 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3704 }
cristyc5c6f662010-09-22 14:23:02 +00003705 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003706 if (segment->x1 != segment->x2)
3707 {
3708 /*
3709 Right pixel.
3710 */
cristybb503372010-05-27 20:51:26 +00003711 x=(ssize_t) ceil(segment->x2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003712 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3713 1,1,exception);
3714 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3715 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003716 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003717 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3718 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003719 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003720 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3721 {
cristye2a912b2011-12-05 20:02:07 +00003722 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003723 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003724 if (traits == UndefinedPixelTrait)
3725 continue;
3726 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3727 }
cristyc5c6f662010-09-22 14:23:02 +00003728 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003729 }
3730 }
3731 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3732 {
3733 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3734 {
cristy3ed852e2009-09-05 21:47:34 +00003735 /*
3736 Bottom pixel.
3737 */
cristybb503372010-05-27 20:51:26 +00003738 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003739 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3740 1,1,exception);
3741 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3742 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003743 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003744 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3745 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003746 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003747 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3748 {
cristye2a912b2011-12-05 20:02:07 +00003749 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003750 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003751 if (traits == UndefinedPixelTrait)
3752 continue;
3753 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3754 }
cristyc5c6f662010-09-22 14:23:02 +00003755 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003756 }
3757 if (segment->y1 != segment->y2)
3758 {
cristy3ed852e2009-09-05 21:47:34 +00003759 /*
3760 Top pixel.
3761 */
cristybb503372010-05-27 20:51:26 +00003762 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003763 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3764 1,1,exception);
3765 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3766 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003767 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003768 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3769 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003770 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003771 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3772 {
cristye2a912b2011-12-05 20:02:07 +00003773 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003774 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003775 if (traits == UndefinedPixelTrait)
3776 continue;
3777 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3778 }
cristyc5c6f662010-09-22 14:23:02 +00003779 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003780 }
3781 }
3782 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3783 {
cristy3ed852e2009-09-05 21:47:34 +00003784 /*
3785 Middle pixel.
3786 */
cristybb503372010-05-27 20:51:26 +00003787 x=(ssize_t) ceil(segment->x1-0.5);
3788 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003789 u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
cristybb503372010-05-27 20:51:26 +00003790 x=(ssize_t) ceil(segment->x2-0.5);
3791 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003792 v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003793 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003794 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3795 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003796 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003797 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3798 {
cristye2a912b2011-12-05 20:02:07 +00003799 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003800 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003801 if (traits == UndefinedPixelTrait)
3802 continue;
3803 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3804 }
cristyc5c6f662010-09-22 14:23:02 +00003805 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003806 }
3807 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3808 return(MagickTrue);
3809 return(MagickFalse);
3810}
cristyda1f9c12011-10-02 21:39:49 +00003811
cristy3ed852e2009-09-05 21:47:34 +00003812MagickExport MagickBooleanType PlasmaImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00003813 const SegmentInfo *segment,size_t attenuate,size_t depth,
3814 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003815{
cristyc5c6f662010-09-22 14:23:02 +00003816 CacheView
cristyda1f9c12011-10-02 21:39:49 +00003817 *image_view,
3818 *u_view,
3819 *v_view;
cristyc5c6f662010-09-22 14:23:02 +00003820
cristy3ed852e2009-09-05 21:47:34 +00003821 MagickBooleanType
3822 status;
3823
3824 RandomInfo
3825 *random_info;
3826
3827 if (image->debug != MagickFalse)
3828 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3829 assert(image != (Image *) NULL);
3830 assert(image->signature == MagickSignature);
3831 if (image->debug != MagickFalse)
3832 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy5cbc0162011-08-29 00:36:28 +00003833 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristyc5c6f662010-09-22 14:23:02 +00003834 return(MagickFalse);
3835 image_view=AcquireCacheView(image);
cristyda1f9c12011-10-02 21:39:49 +00003836 u_view=AcquireCacheView(image);
3837 v_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003838 random_info=AcquireRandomInfo();
cristyda1f9c12011-10-02 21:39:49 +00003839 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
3840 attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003841 random_info=DestroyRandomInfo(random_info);
cristyda1f9c12011-10-02 21:39:49 +00003842 v_view=DestroyCacheView(v_view);
3843 u_view=DestroyCacheView(u_view);
cristyc5c6f662010-09-22 14:23:02 +00003844 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003845 return(status);
3846}
3847
3848/*
3849%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3850% %
3851% %
3852% %
3853% P o l a r o i d I m a g e %
3854% %
3855% %
3856% %
3857%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3858%
3859% PolaroidImage() simulates a Polaroid picture.
3860%
3861% The format of the AnnotateImage method is:
3862%
3863% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003864% const char *caption,const double angle,
3865% const PixelInterpolateMethod method,ExceptionInfo exception)
cristy3ed852e2009-09-05 21:47:34 +00003866%
3867% A description of each parameter follows:
3868%
3869% o image: the image.
3870%
3871% o draw_info: the draw info.
3872%
cristye9e3d382011-12-14 01:50:13 +00003873% o caption: the Polaroid caption.
3874%
cristycee97112010-05-28 00:44:52 +00003875% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003876%
cristy5c4e2582011-09-11 19:21:03 +00003877% o method: the pixel interpolation method.
3878%
cristy3ed852e2009-09-05 21:47:34 +00003879% o exception: return any errors or warnings in this structure.
3880%
3881*/
3882MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003883 const char *caption,const double angle,const PixelInterpolateMethod method,
cristy5c4e2582011-09-11 19:21:03 +00003884 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003885{
cristy3ed852e2009-09-05 21:47:34 +00003886 Image
3887 *bend_image,
3888 *caption_image,
3889 *flop_image,
3890 *picture_image,
3891 *polaroid_image,
3892 *rotate_image,
3893 *trim_image;
3894
cristybb503372010-05-27 20:51:26 +00003895 size_t
cristy3ed852e2009-09-05 21:47:34 +00003896 height;
3897
cristy9d314ff2011-03-09 01:30:28 +00003898 ssize_t
3899 quantum;
3900
cristy3ed852e2009-09-05 21:47:34 +00003901 /*
3902 Simulate a Polaroid picture.
3903 */
3904 assert(image != (Image *) NULL);
3905 assert(image->signature == MagickSignature);
3906 if (image->debug != MagickFalse)
3907 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3908 assert(exception != (ExceptionInfo *) NULL);
3909 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003910 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003911 image->rows)/25.0,10.0);
3912 height=image->rows+2*quantum;
3913 caption_image=(Image *) NULL;
cristye9e3d382011-12-14 01:50:13 +00003914 if (caption != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003915 {
3916 char
cristye9e3d382011-12-14 01:50:13 +00003917 geometry[MaxTextExtent],
3918 *text;
cristy3ed852e2009-09-05 21:47:34 +00003919
3920 DrawInfo
3921 *annotate_info;
3922
cristy3ed852e2009-09-05 21:47:34 +00003923 MagickBooleanType
3924 status;
3925
cristy9d314ff2011-03-09 01:30:28 +00003926 ssize_t
3927 count;
3928
cristy3ed852e2009-09-05 21:47:34 +00003929 TypeMetric
3930 metrics;
3931
3932 /*
3933 Generate caption image.
3934 */
3935 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3936 if (caption_image == (Image *) NULL)
3937 return((Image *) NULL);
3938 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
cristye9e3d382011-12-14 01:50:13 +00003939 text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
3940 exception);
3941 (void) CloneString(&annotate_info->text,text);
cristy6b1d05e2010-09-22 19:17:27 +00003942 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
cristye9e3d382011-12-14 01:50:13 +00003943 &text,exception);
3944 status=SetImageExtent(caption_image,image->columns,(size_t) ((count+1)*
3945 (metrics.ascent-metrics.descent)+0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00003946 if (status == MagickFalse)
3947 caption_image=DestroyImage(caption_image);
3948 else
3949 {
3950 caption_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00003951 (void) SetImageBackgroundColor(caption_image,exception);
cristye9e3d382011-12-14 01:50:13 +00003952 (void) CloneString(&annotate_info->text,text);
cristyb51dff52011-05-19 16:55:47 +00003953 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00003954 metrics.ascent);
3955 if (annotate_info->gravity == UndefinedGravity)
3956 (void) CloneString(&annotate_info->geometry,AcquireString(
3957 geometry));
cristy5cbc0162011-08-29 00:36:28 +00003958 (void) AnnotateImage(caption_image,annotate_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003959 height+=caption_image->rows;
3960 }
3961 annotate_info=DestroyDrawInfo(annotate_info);
cristye9e3d382011-12-14 01:50:13 +00003962 text=DestroyString(text);
cristy3ed852e2009-09-05 21:47:34 +00003963 }
3964 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3965 exception);
3966 if (picture_image == (Image *) NULL)
3967 {
3968 if (caption_image != (Image *) NULL)
3969 caption_image=DestroyImage(caption_image);
3970 return((Image *) NULL);
3971 }
3972 picture_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00003973 (void) SetImageBackgroundColor(picture_image,exception);
cristy39172402012-03-30 13:04:39 +00003974 (void) CompositeImage(picture_image,image,OverCompositeOp,MagickTrue,quantum,
cristyfeb3e962012-03-29 17:25:55 +00003975 quantum,exception);
cristy3ed852e2009-09-05 21:47:34 +00003976 if (caption_image != (Image *) NULL)
3977 {
cristyfeb3e962012-03-29 17:25:55 +00003978 (void) CompositeImage(picture_image,caption_image,OverCompositeOp,
cristy39172402012-03-30 13:04:39 +00003979 MagickTrue,quantum,(ssize_t) (image->rows+3*quantum/2),exception);
cristy3ed852e2009-09-05 21:47:34 +00003980 caption_image=DestroyImage(caption_image);
3981 }
cristy9950d572011-10-01 18:22:35 +00003982 (void) QueryColorCompliance("none",AllCompliance,
3983 &picture_image->background_color,exception);
cristy63240882011-08-05 19:05:27 +00003984 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00003985 rotate_image=RotateImage(picture_image,90.0,exception);
3986 picture_image=DestroyImage(picture_image);
3987 if (rotate_image == (Image *) NULL)
3988 return((Image *) NULL);
3989 picture_image=rotate_image;
3990 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
cristy5c4e2582011-09-11 19:21:03 +00003991 picture_image->columns,method,exception);
cristy3ed852e2009-09-05 21:47:34 +00003992 picture_image=DestroyImage(picture_image);
3993 if (bend_image == (Image *) NULL)
3994 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003995 picture_image=bend_image;
3996 rotate_image=RotateImage(picture_image,-90.0,exception);
3997 picture_image=DestroyImage(picture_image);
3998 if (rotate_image == (Image *) NULL)
3999 return((Image *) NULL);
4000 picture_image=rotate_image;
4001 picture_image->background_color=image->background_color;
anthonyf46d4262012-03-26 03:30:34 +00004002 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
cristy3ed852e2009-09-05 21:47:34 +00004003 exception);
4004 if (polaroid_image == (Image *) NULL)
4005 {
4006 picture_image=DestroyImage(picture_image);
4007 return(picture_image);
4008 }
4009 flop_image=FlopImage(polaroid_image,exception);
4010 polaroid_image=DestroyImage(polaroid_image);
4011 if (flop_image == (Image *) NULL)
4012 {
4013 picture_image=DestroyImage(picture_image);
4014 return(picture_image);
4015 }
4016 polaroid_image=flop_image;
cristyfeb3e962012-03-29 17:25:55 +00004017 (void) CompositeImage(polaroid_image,picture_image,OverCompositeOp,
cristy39172402012-03-30 13:04:39 +00004018 MagickTrue,(ssize_t) (-0.01*picture_image->columns/2.0),0L,exception);
cristy3ed852e2009-09-05 21:47:34 +00004019 picture_image=DestroyImage(picture_image);
cristy9950d572011-10-01 18:22:35 +00004020 (void) QueryColorCompliance("none",AllCompliance,
4021 &polaroid_image->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00004022 rotate_image=RotateImage(polaroid_image,angle,exception);
4023 polaroid_image=DestroyImage(polaroid_image);
4024 if (rotate_image == (Image *) NULL)
4025 return((Image *) NULL);
4026 polaroid_image=rotate_image;
4027 trim_image=TrimImage(polaroid_image,exception);
4028 polaroid_image=DestroyImage(polaroid_image);
4029 if (trim_image == (Image *) NULL)
4030 return((Image *) NULL);
4031 polaroid_image=trim_image;
4032 return(polaroid_image);
4033}
4034
4035/*
4036%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4037% %
4038% %
4039% %
cristy3ed852e2009-09-05 21:47:34 +00004040% S e p i a T o n e I m a g e %
4041% %
4042% %
4043% %
4044%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4045%
4046% MagickSepiaToneImage() applies a special effect to the image, similar to the
4047% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4048% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4049% threshold of 80% is a good starting point for a reasonable tone.
4050%
4051% The format of the SepiaToneImage method is:
4052%
4053% Image *SepiaToneImage(const Image *image,const double threshold,
4054% ExceptionInfo *exception)
4055%
4056% A description of each parameter follows:
4057%
4058% o image: the image.
4059%
4060% o threshold: the tone threshold.
4061%
4062% o exception: return any errors or warnings in this structure.
4063%
4064*/
4065MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4066 ExceptionInfo *exception)
4067{
4068#define SepiaToneImageTag "SepiaTone/Image"
4069
cristyc4c8d132010-01-07 01:58:38 +00004070 CacheView
4071 *image_view,
4072 *sepia_view;
4073
cristy3ed852e2009-09-05 21:47:34 +00004074 Image
4075 *sepia_image;
4076
cristy3ed852e2009-09-05 21:47:34 +00004077 MagickBooleanType
4078 status;
4079
cristybb503372010-05-27 20:51:26 +00004080 MagickOffsetType
4081 progress;
4082
4083 ssize_t
4084 y;
4085
cristy3ed852e2009-09-05 21:47:34 +00004086 /*
4087 Initialize sepia-toned image attributes.
4088 */
4089 assert(image != (const Image *) NULL);
4090 assert(image->signature == MagickSignature);
4091 if (image->debug != MagickFalse)
4092 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4093 assert(exception != (ExceptionInfo *) NULL);
4094 assert(exception->signature == MagickSignature);
cristy1707c6c2012-01-18 23:30:54 +00004095 sepia_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004096 if (sepia_image == (Image *) NULL)
4097 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004098 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004099 {
cristy3ed852e2009-09-05 21:47:34 +00004100 sepia_image=DestroyImage(sepia_image);
4101 return((Image *) NULL);
4102 }
4103 /*
4104 Tone each row of the image.
4105 */
4106 status=MagickTrue;
4107 progress=0;
4108 image_view=AcquireCacheView(image);
4109 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004110#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004111 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004112#endif
cristybb503372010-05-27 20:51:26 +00004113 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004114 {
cristy4c08aed2011-07-01 19:47:50 +00004115 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004116 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004117
cristybb503372010-05-27 20:51:26 +00004118 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004119 x;
4120
cristy4c08aed2011-07-01 19:47:50 +00004121 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004122 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004123
4124 if (status == MagickFalse)
4125 continue;
4126 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00004127 q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004128 exception);
cristy4c08aed2011-07-01 19:47:50 +00004129 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004130 {
4131 status=MagickFalse;
4132 continue;
4133 }
cristybb503372010-05-27 20:51:26 +00004134 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004135 {
4136 MagickRealType
4137 intensity,
4138 tone;
4139
cristy4c08aed2011-07-01 19:47:50 +00004140 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004141 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4142 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004143 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004144 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4145 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004146 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004147 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004148 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004149 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004150 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4151 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4152 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4153 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004154 p+=GetPixelChannels(image);
4155 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004156 }
4157 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4158 status=MagickFalse;
4159 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4160 {
4161 MagickBooleanType
4162 proceed;
4163
cristyb5d5f722009-11-04 03:03:49 +00004164#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004165 #pragma omp critical (MagickCore_SepiaToneImage)
cristy3ed852e2009-09-05 21:47:34 +00004166#endif
4167 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4168 image->rows);
4169 if (proceed == MagickFalse)
4170 status=MagickFalse;
4171 }
4172 }
4173 sepia_view=DestroyCacheView(sepia_view);
4174 image_view=DestroyCacheView(image_view);
cristye23ec9d2011-08-16 18:15:40 +00004175 (void) NormalizeImage(sepia_image,exception);
4176 (void) ContrastImage(sepia_image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004177 if (status == MagickFalse)
4178 sepia_image=DestroyImage(sepia_image);
4179 return(sepia_image);
4180}
4181
4182/*
4183%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4184% %
4185% %
4186% %
4187% S h a d o w I m a g e %
4188% %
4189% %
4190% %
4191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4192%
4193% ShadowImage() simulates a shadow from the specified image and returns it.
4194%
4195% The format of the ShadowImage method is:
4196%
cristy70cddf72011-12-10 22:42:42 +00004197% Image *ShadowImage(const Image *image,const double alpha,
cristyaa2c16c2012-03-25 22:21:35 +00004198% const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4199% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004200%
4201% A description of each parameter follows:
4202%
4203% o image: the image.
4204%
cristy70cddf72011-12-10 22:42:42 +00004205% o alpha: percentage transparency.
cristy3ed852e2009-09-05 21:47:34 +00004206%
4207% o sigma: the standard deviation of the Gaussian, in pixels.
4208%
4209% o x_offset: the shadow x-offset.
4210%
4211% o y_offset: the shadow y-offset.
4212%
4213% o exception: return any errors or warnings in this structure.
4214%
4215*/
cristy70cddf72011-12-10 22:42:42 +00004216MagickExport Image *ShadowImage(const Image *image,const double alpha,
cristyaa2c16c2012-03-25 22:21:35 +00004217 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4218 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004219{
4220#define ShadowImageTag "Shadow/Image"
4221
cristy70cddf72011-12-10 22:42:42 +00004222 CacheView
4223 *image_view;
4224
cristybd5a96c2011-08-21 00:04:26 +00004225 ChannelType
4226 channel_mask;
4227
cristy3ed852e2009-09-05 21:47:34 +00004228 Image
4229 *border_image,
4230 *clone_image,
4231 *shadow_image;
4232
cristy70cddf72011-12-10 22:42:42 +00004233 MagickBooleanType
4234 status;
4235
cristy3ed852e2009-09-05 21:47:34 +00004236 RectangleInfo
4237 border_info;
4238
cristy70cddf72011-12-10 22:42:42 +00004239 ssize_t
4240 y;
4241
cristy3ed852e2009-09-05 21:47:34 +00004242 assert(image != (Image *) NULL);
4243 assert(image->signature == MagickSignature);
4244 if (image->debug != MagickFalse)
4245 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4246 assert(exception != (ExceptionInfo *) NULL);
4247 assert(exception->signature == MagickSignature);
4248 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4249 if (clone_image == (Image *) NULL)
4250 return((Image *) NULL);
cristy387430f2012-02-07 13:09:46 +00004251 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod,
4252 exception);
cristybb503372010-05-27 20:51:26 +00004253 border_info.width=(size_t) floor(2.0*sigma+0.5);
4254 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004255 border_info.x=0;
4256 border_info.y=0;
cristy9950d572011-10-01 18:22:35 +00004257 (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4258 exception);
cristy70cddf72011-12-10 22:42:42 +00004259 clone_image->matte=MagickTrue;
4260 border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
cristy3ed852e2009-09-05 21:47:34 +00004261 clone_image=DestroyImage(clone_image);
4262 if (border_image == (Image *) NULL)
4263 return((Image *) NULL);
4264 if (border_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004265 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004266 /*
4267 Shadow image.
4268 */
cristy70cddf72011-12-10 22:42:42 +00004269 status=MagickTrue;
4270 image_view=AcquireCacheView(border_image);
4271 for (y=0; y < (ssize_t) border_image->rows; y++)
4272 {
4273 PixelInfo
4274 background_color;
4275
4276 register Quantum
4277 *restrict q;
4278
4279 register ssize_t
4280 x;
4281
4282 if (status == MagickFalse)
4283 continue;
4284 q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4285 exception);
4286 if (q == (Quantum *) NULL)
4287 {
4288 status=MagickFalse;
4289 continue;
4290 }
4291 background_color=border_image->background_color;
4292 background_color.matte=MagickTrue;
4293 for (x=0; x < (ssize_t) border_image->columns; x++)
4294 {
4295 if (border_image->matte != MagickFalse)
4296 background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4297 SetPixelInfoPixel(border_image,&background_color,q);
4298 q+=GetPixelChannels(border_image);
4299 }
4300 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4301 status=MagickFalse;
4302 }
4303 image_view=DestroyCacheView(image_view);
4304 if (status == MagickFalse)
4305 {
4306 border_image=DestroyImage(border_image);
4307 return((Image *) NULL);
4308 }
cristybd5a96c2011-08-21 00:04:26 +00004309 channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
cristyaa2c16c2012-03-25 22:21:35 +00004310 shadow_image=BlurImage(border_image,0.0,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00004311 border_image=DestroyImage(border_image);
4312 if (shadow_image == (Image *) NULL)
4313 return((Image *) NULL);
cristyae1969f2011-12-10 03:07:36 +00004314 (void) SetPixelChannelMapMask(shadow_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004315 if (shadow_image->page.width == 0)
4316 shadow_image->page.width=shadow_image->columns;
4317 if (shadow_image->page.height == 0)
4318 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004319 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4320 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4321 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4322 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004323 return(shadow_image);
4324}
4325
4326/*
4327%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4328% %
4329% %
4330% %
4331% S k e t c h I m a g e %
4332% %
4333% %
4334% %
4335%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4336%
4337% SketchImage() simulates a pencil sketch. We convolve the image with a
4338% Gaussian operator of the given radius and standard deviation (sigma). For
4339% reasonable results, radius should be larger than sigma. Use a radius of 0
4340% and SketchImage() selects a suitable radius for you. Angle gives the angle
4341% of the sketch.
4342%
4343% The format of the SketchImage method is:
4344%
4345% Image *SketchImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00004346% const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004347%
4348% A description of each parameter follows:
4349%
4350% o image: the image.
4351%
cristy574cc262011-08-05 01:23:58 +00004352% o radius: the radius of the Gaussian, in pixels, not counting the
4353% center pixel.
cristy3ed852e2009-09-05 21:47:34 +00004354%
4355% o sigma: the standard deviation of the Gaussian, in pixels.
4356%
cristyf7ef0252011-09-09 14:50:06 +00004357% o angle: apply the effect along this angle.
4358%
cristy3ed852e2009-09-05 21:47:34 +00004359% o exception: return any errors or warnings in this structure.
4360%
4361*/
4362MagickExport Image *SketchImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00004363 const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004364{
cristyfa112112010-01-04 17:48:07 +00004365 CacheView
4366 *random_view;
4367
cristy3ed852e2009-09-05 21:47:34 +00004368 Image
4369 *blend_image,
4370 *blur_image,
4371 *dodge_image,
4372 *random_image,
4373 *sketch_image;
4374
cristy3ed852e2009-09-05 21:47:34 +00004375 MagickBooleanType
4376 status;
4377
cristy3ed852e2009-09-05 21:47:34 +00004378 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004379 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004380
cristy9d314ff2011-03-09 01:30:28 +00004381 ssize_t
4382 y;
4383
cristy3ed852e2009-09-05 21:47:34 +00004384 /*
4385 Sketch image.
4386 */
4387 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4388 MagickTrue,exception);
4389 if (random_image == (Image *) NULL)
4390 return((Image *) NULL);
4391 status=MagickTrue;
cristy1b784432009-12-19 02:20:40 +00004392 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004393 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004394#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004395 #pragma omp parallel for schedule(static,4) shared(status)
cristy1b784432009-12-19 02:20:40 +00004396#endif
cristybb503372010-05-27 20:51:26 +00004397 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004398 {
cristy5c9e6f22010-09-17 17:31:01 +00004399 const int
4400 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004401
cristybb503372010-05-27 20:51:26 +00004402 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004403 x;
4404
cristy4c08aed2011-07-01 19:47:50 +00004405 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004406 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004407
cristy1b784432009-12-19 02:20:40 +00004408 if (status == MagickFalse)
4409 continue;
cristy3ed852e2009-09-05 21:47:34 +00004410 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4411 exception);
cristyacd2ed22011-08-30 01:44:23 +00004412 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004413 {
4414 status=MagickFalse;
4415 continue;
4416 }
cristybb503372010-05-27 20:51:26 +00004417 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004418 {
cristy76f512e2011-09-12 01:26:56 +00004419 MagickRealType
4420 value;
4421
4422 register ssize_t
4423 i;
4424
cristy10a6c612012-01-29 21:41:05 +00004425 if (GetPixelMask(random_image,q) != 0)
4426 {
4427 q+=GetPixelChannels(random_image);
4428 continue;
4429 }
cristy76f512e2011-09-12 01:26:56 +00004430 value=GetPseudoRandomValue(random_info[id]);
4431 for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4432 {
cristyabace412011-12-11 15:56:53 +00004433 PixelChannel
4434 channel;
4435
cristy76f512e2011-09-12 01:26:56 +00004436 PixelTrait
4437 traits;
4438
cristyabace412011-12-11 15:56:53 +00004439 channel=GetPixelChannelMapChannel(image,i);
4440 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004441 if (traits == UndefinedPixelTrait)
4442 continue;
4443 q[i]=ClampToQuantum(QuantumRange*value);
4444 }
cristyed231572011-07-14 02:18:59 +00004445 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004446 }
4447 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4448 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004449 }
4450 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004451 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004452 if (status == MagickFalse)
4453 {
4454 random_image=DestroyImage(random_image);
4455 return(random_image);
4456 }
cristyaa2c16c2012-03-25 22:21:35 +00004457 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
cristy3ed852e2009-09-05 21:47:34 +00004458 random_image=DestroyImage(random_image);
4459 if (blur_image == (Image *) NULL)
4460 return((Image *) NULL);
cristy6bfd6902011-12-09 01:33:45 +00004461 dodge_image=EdgeImage(blur_image,radius,1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004462 blur_image=DestroyImage(blur_image);
4463 if (dodge_image == (Image *) NULL)
4464 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +00004465 (void) NormalizeImage(dodge_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00004466 (void) NegateImage(dodge_image,MagickFalse,exception);
cristye941a752011-10-15 01:52:48 +00004467 (void) TransformImage(&dodge_image,(char *) NULL,"50%",exception);
cristy3ed852e2009-09-05 21:47:34 +00004468 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4469 if (sketch_image == (Image *) NULL)
4470 {
4471 dodge_image=DestroyImage(dodge_image);
4472 return((Image *) NULL);
4473 }
cristyfeb3e962012-03-29 17:25:55 +00004474 (void) CompositeImage(sketch_image,dodge_image,ColorDodgeCompositeOp,
cristy39172402012-03-30 13:04:39 +00004475 MagickTrue,0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004476 dodge_image=DestroyImage(dodge_image);
4477 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4478 if (blend_image == (Image *) NULL)
4479 {
4480 sketch_image=DestroyImage(sketch_image);
4481 return((Image *) NULL);
4482 }
4483 (void) SetImageArtifact(blend_image,"compose:args","20x80");
cristy39172402012-03-30 13:04:39 +00004484 (void) CompositeImage(sketch_image,blend_image,BlendCompositeOp,MagickTrue,
cristyfeb3e962012-03-29 17:25:55 +00004485 0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004486 blend_image=DestroyImage(blend_image);
4487 return(sketch_image);
4488}
4489
4490/*
4491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4492% %
4493% %
4494% %
4495% S o l a r i z e I m a g e %
4496% %
4497% %
4498% %
4499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4500%
4501% SolarizeImage() applies a special effect to the image, similar to the effect
4502% achieved in a photo darkroom by selectively exposing areas of photo
4503% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4504% measure of the extent of the solarization.
4505%
4506% The format of the SolarizeImage method is:
4507%
cristy5cbc0162011-08-29 00:36:28 +00004508% MagickBooleanType SolarizeImage(Image *image,const double threshold,
4509% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004510%
4511% A description of each parameter follows:
4512%
4513% o image: the image.
4514%
4515% o threshold: Define the extent of the solarization.
4516%
cristy5cbc0162011-08-29 00:36:28 +00004517% o exception: return any errors or warnings in this structure.
4518%
cristy3ed852e2009-09-05 21:47:34 +00004519*/
4520MagickExport MagickBooleanType SolarizeImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00004521 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004522{
4523#define SolarizeImageTag "Solarize/Image"
4524
cristyc4c8d132010-01-07 01:58:38 +00004525 CacheView
4526 *image_view;
4527
cristy3ed852e2009-09-05 21:47:34 +00004528 MagickBooleanType
4529 status;
4530
cristybb503372010-05-27 20:51:26 +00004531 MagickOffsetType
4532 progress;
4533
4534 ssize_t
4535 y;
4536
cristy3ed852e2009-09-05 21:47:34 +00004537 assert(image != (Image *) NULL);
4538 assert(image->signature == MagickSignature);
4539 if (image->debug != MagickFalse)
4540 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4541 if (image->storage_class == PseudoClass)
4542 {
cristybb503372010-05-27 20:51:26 +00004543 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004544 i;
4545
4546 /*
4547 Solarize colormap.
4548 */
cristybb503372010-05-27 20:51:26 +00004549 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004550 {
4551 if ((MagickRealType) image->colormap[i].red > threshold)
4552 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4553 if ((MagickRealType) image->colormap[i].green > threshold)
4554 image->colormap[i].green=(Quantum) QuantumRange-
4555 image->colormap[i].green;
4556 if ((MagickRealType) image->colormap[i].blue > threshold)
4557 image->colormap[i].blue=(Quantum) QuantumRange-
4558 image->colormap[i].blue;
4559 }
4560 }
4561 /*
4562 Solarize image.
4563 */
4564 status=MagickTrue;
4565 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00004566 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004567#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004568 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004569#endif
cristybb503372010-05-27 20:51:26 +00004570 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004571 {
cristybb503372010-05-27 20:51:26 +00004572 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004573 x;
4574
cristy4c08aed2011-07-01 19:47:50 +00004575 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004576 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004577
4578 if (status == MagickFalse)
4579 continue;
cristy5cbc0162011-08-29 00:36:28 +00004580 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00004581 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004582 {
4583 status=MagickFalse;
4584 continue;
4585 }
cristybb503372010-05-27 20:51:26 +00004586 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004587 {
cristy76f512e2011-09-12 01:26:56 +00004588 register ssize_t
4589 i;
4590
cristy10a6c612012-01-29 21:41:05 +00004591 if (GetPixelMask(image,q) != 0)
4592 {
4593 q+=GetPixelChannels(image);
4594 continue;
4595 }
cristy76f512e2011-09-12 01:26:56 +00004596 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4597 {
cristyabace412011-12-11 15:56:53 +00004598 PixelChannel
4599 channel;
4600
cristy76f512e2011-09-12 01:26:56 +00004601 PixelTrait
4602 traits;
4603
cristyabace412011-12-11 15:56:53 +00004604 channel=GetPixelChannelMapChannel(image,i);
4605 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004606 if ((traits == UndefinedPixelTrait) ||
4607 ((traits & CopyPixelTrait) != 0))
4608 continue;
4609 if ((MagickRealType) q[i] > threshold)
4610 q[i]=QuantumRange-q[i];
4611 }
cristyed231572011-07-14 02:18:59 +00004612 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004613 }
4614 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4615 status=MagickFalse;
4616 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4617 {
4618 MagickBooleanType
4619 proceed;
4620
cristyb5d5f722009-11-04 03:03:49 +00004621#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004622 #pragma omp critical (MagickCore_SolarizeImage)
cristy3ed852e2009-09-05 21:47:34 +00004623#endif
4624 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4625 if (proceed == MagickFalse)
4626 status=MagickFalse;
4627 }
4628 }
4629 image_view=DestroyCacheView(image_view);
4630 return(status);
4631}
4632
4633/*
4634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4635% %
4636% %
4637% %
4638% S t e g a n o I m a g e %
4639% %
4640% %
4641% %
4642%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4643%
4644% SteganoImage() hides a digital watermark within the image. Recover
4645% the hidden watermark later to prove that the authenticity of an image.
4646% Offset defines the start position within the image to hide the watermark.
4647%
4648% The format of the SteganoImage method is:
4649%
4650% Image *SteganoImage(const Image *image,Image *watermark,
4651% ExceptionInfo *exception)
4652%
4653% A description of each parameter follows:
4654%
4655% o image: the image.
4656%
4657% o watermark: the watermark image.
4658%
4659% o exception: return any errors or warnings in this structure.
4660%
4661*/
4662MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4663 ExceptionInfo *exception)
4664{
cristye1bf8ad2010-09-19 17:07:03 +00004665#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004666#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004667 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004668#define SteganoImageTag "Stegano/Image"
4669
cristyb0d3bb92010-09-22 14:37:58 +00004670 CacheView
4671 *stegano_view,
4672 *watermark_view;
4673
cristy3ed852e2009-09-05 21:47:34 +00004674 Image
4675 *stegano_image;
4676
4677 int
4678 c;
4679
cristy3ed852e2009-09-05 21:47:34 +00004680 MagickBooleanType
4681 status;
4682
cristy101ab702011-10-13 13:06:32 +00004683 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004684 pixel;
4685
cristy4c08aed2011-07-01 19:47:50 +00004686 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004687 *q;
4688
cristye1bf8ad2010-09-19 17:07:03 +00004689 register ssize_t
4690 x;
4691
cristybb503372010-05-27 20:51:26 +00004692 size_t
cristyeaedf062010-05-29 22:36:02 +00004693 depth,
4694 one;
cristy3ed852e2009-09-05 21:47:34 +00004695
cristye1bf8ad2010-09-19 17:07:03 +00004696 ssize_t
4697 i,
4698 j,
4699 k,
4700 y;
4701
cristy3ed852e2009-09-05 21:47:34 +00004702 /*
4703 Initialize steganographic image attributes.
4704 */
4705 assert(image != (const Image *) NULL);
4706 assert(image->signature == MagickSignature);
4707 if (image->debug != MagickFalse)
4708 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4709 assert(watermark != (const Image *) NULL);
4710 assert(watermark->signature == MagickSignature);
4711 assert(exception != (ExceptionInfo *) NULL);
4712 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004713 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004714 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4715 if (stegano_image == (Image *) NULL)
4716 return((Image *) NULL);
cristyf61b1832012-04-01 01:38:19 +00004717 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
cristy574cc262011-08-05 01:23:58 +00004718 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004719 {
cristy3ed852e2009-09-05 21:47:34 +00004720 stegano_image=DestroyImage(stegano_image);
4721 return((Image *) NULL);
4722 }
cristy3ed852e2009-09-05 21:47:34 +00004723 /*
4724 Hide watermark in low-order bits of image.
4725 */
4726 c=0;
4727 i=0;
4728 j=0;
4729 depth=stegano_image->depth;
cristyf61b1832012-04-01 01:38:19 +00004730 k=stegano_image->offset;
cristyda16f162011-02-19 23:52:17 +00004731 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004732 watermark_view=AcquireCacheView(watermark);
4733 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004734 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004735 {
cristybb503372010-05-27 20:51:26 +00004736 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004737 {
cristybb503372010-05-27 20:51:26 +00004738 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004739 {
cristy1707c6c2012-01-18 23:30:54 +00004740 ssize_t
4741 offset;
4742
cristyf05d4942012-03-17 16:26:09 +00004743 (void) GetOneCacheViewVirtualPixelInfo(watermark_view,x,y,&pixel,
cristyda1f9c12011-10-02 21:39:49 +00004744 exception);
cristy1707c6c2012-01-18 23:30:54 +00004745 offset=k/(ssize_t) stegano_image->columns;
4746 if (offset >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004747 break;
cristyb0d3bb92010-09-22 14:37:58 +00004748 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4749 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4750 exception);
cristyacd2ed22011-08-30 01:44:23 +00004751 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004752 break;
4753 switch (c)
4754 {
4755 case 0:
4756 {
cristyf61b1832012-04-01 01:38:19 +00004757 SetPixelRed(stegano_image,SetBit(GetPixelRed(stegano_image,q),j,
4758 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004759 break;
4760 }
4761 case 1:
4762 {
cristyf61b1832012-04-01 01:38:19 +00004763 SetPixelGreen(stegano_image,SetBit(GetPixelGreen(stegano_image,q),j,
4764 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004765 break;
4766 }
4767 case 2:
4768 {
cristyf61b1832012-04-01 01:38:19 +00004769 SetPixelBlue(stegano_image,SetBit(GetPixelBlue(stegano_image,q),j,
4770 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004771 break;
4772 }
4773 }
cristyb0d3bb92010-09-22 14:37:58 +00004774 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004775 break;
4776 c++;
4777 if (c == 3)
4778 c=0;
4779 k++;
cristybb503372010-05-27 20:51:26 +00004780 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004781 k=0;
cristyf61b1832012-04-01 01:38:19 +00004782 if (k == stegano_image->offset)
cristy3ed852e2009-09-05 21:47:34 +00004783 j++;
4784 }
4785 }
cristy8b27a6d2010-02-14 03:31:15 +00004786 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004787 {
cristy8b27a6d2010-02-14 03:31:15 +00004788 MagickBooleanType
4789 proceed;
4790
4791 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4792 (depth-i),depth);
4793 if (proceed == MagickFalse)
4794 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004795 }
4796 }
cristyb0d3bb92010-09-22 14:37:58 +00004797 stegano_view=DestroyCacheView(stegano_view);
4798 watermark_view=DestroyCacheView(watermark_view);
cristyda16f162011-02-19 23:52:17 +00004799 if (status == MagickFalse)
4800 {
4801 stegano_image=DestroyImage(stegano_image);
4802 return((Image *) NULL);
4803 }
cristy3ed852e2009-09-05 21:47:34 +00004804 return(stegano_image);
4805}
4806
4807/*
4808%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4809% %
4810% %
4811% %
4812% S t e r e o A n a g l y p h I m a g e %
4813% %
4814% %
4815% %
4816%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4817%
4818% StereoAnaglyphImage() combines two images and produces a single image that
4819% is the composite of a left and right image of a stereo pair. Special
4820% red-green stereo glasses are required to view this effect.
4821%
4822% The format of the StereoAnaglyphImage method is:
4823%
4824% Image *StereoImage(const Image *left_image,const Image *right_image,
4825% ExceptionInfo *exception)
4826% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004827% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004828% ExceptionInfo *exception)
4829%
4830% A description of each parameter follows:
4831%
4832% o left_image: the left image.
4833%
4834% o right_image: the right image.
4835%
4836% o exception: return any errors or warnings in this structure.
4837%
4838% o x_offset: amount, in pixels, by which the left image is offset to the
4839% right of the right image.
4840%
4841% o y_offset: amount, in pixels, by which the left image is offset to the
4842% bottom of the right image.
4843%
4844%
4845*/
4846MagickExport Image *StereoImage(const Image *left_image,
4847 const Image *right_image,ExceptionInfo *exception)
4848{
4849 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4850}
4851
4852MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004853 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004854 ExceptionInfo *exception)
4855{
4856#define StereoImageTag "Stereo/Image"
4857
4858 const Image
4859 *image;
4860
4861 Image
4862 *stereo_image;
4863
cristy3ed852e2009-09-05 21:47:34 +00004864 MagickBooleanType
4865 status;
4866
cristy9d314ff2011-03-09 01:30:28 +00004867 ssize_t
4868 y;
4869
cristy3ed852e2009-09-05 21:47:34 +00004870 assert(left_image != (const Image *) NULL);
4871 assert(left_image->signature == MagickSignature);
4872 if (left_image->debug != MagickFalse)
4873 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4874 left_image->filename);
4875 assert(right_image != (const Image *) NULL);
4876 assert(right_image->signature == MagickSignature);
4877 assert(exception != (ExceptionInfo *) NULL);
4878 assert(exception->signature == MagickSignature);
4879 assert(right_image != (const Image *) NULL);
4880 image=left_image;
4881 if ((left_image->columns != right_image->columns) ||
4882 (left_image->rows != right_image->rows))
4883 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4884 /*
4885 Initialize stereo image attributes.
4886 */
4887 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4888 MagickTrue,exception);
4889 if (stereo_image == (Image *) NULL)
4890 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004891 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004892 {
cristy3ed852e2009-09-05 21:47:34 +00004893 stereo_image=DestroyImage(stereo_image);
4894 return((Image *) NULL);
4895 }
4896 /*
4897 Copy left image to red channel and right image to blue channel.
4898 */
cristyda16f162011-02-19 23:52:17 +00004899 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004900 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004901 {
cristy4c08aed2011-07-01 19:47:50 +00004902 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004903 *restrict p,
4904 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004905
cristybb503372010-05-27 20:51:26 +00004906 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004907 x;
4908
cristy4c08aed2011-07-01 19:47:50 +00004909 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004910 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004911
4912 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4913 exception);
4914 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4915 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy76f512e2011-09-12 01:26:56 +00004916 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
4917 (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004918 break;
cristybb503372010-05-27 20:51:26 +00004919 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004920 {
cristy4c08aed2011-07-01 19:47:50 +00004921 SetPixelRed(image,GetPixelRed(left_image,p),r);
cristy76f512e2011-09-12 01:26:56 +00004922 SetPixelGreen(image,GetPixelGreen(right_image,q),r);
4923 SetPixelBlue(image,GetPixelBlue(right_image,q),r);
4924 if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
4925 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4926 GetPixelAlpha(right_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00004927 p+=GetPixelChannels(left_image);
cristy76f512e2011-09-12 01:26:56 +00004928 q+=GetPixelChannels(right_image);
4929 r+=GetPixelChannels(stereo_image);
cristy3ed852e2009-09-05 21:47:34 +00004930 }
4931 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4932 break;
cristy8b27a6d2010-02-14 03:31:15 +00004933 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004934 {
cristy8b27a6d2010-02-14 03:31:15 +00004935 MagickBooleanType
4936 proceed;
4937
cristybb503372010-05-27 20:51:26 +00004938 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4939 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004940 if (proceed == MagickFalse)
4941 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004942 }
4943 }
cristyda16f162011-02-19 23:52:17 +00004944 if (status == MagickFalse)
4945 {
4946 stereo_image=DestroyImage(stereo_image);
4947 return((Image *) NULL);
4948 }
cristy3ed852e2009-09-05 21:47:34 +00004949 return(stereo_image);
4950}
4951
4952/*
4953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4954% %
4955% %
4956% %
4957% S w i r l I m a g e %
4958% %
4959% %
4960% %
4961%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4962%
4963% SwirlImage() swirls the pixels about the center of the image, where
4964% degrees indicates the sweep of the arc through which each pixel is moved.
4965% You get a more dramatic effect as the degrees move from 1 to 360.
4966%
4967% The format of the SwirlImage method is:
4968%
4969% Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00004970% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004971%
4972% A description of each parameter follows:
4973%
4974% o image: the image.
4975%
4976% o degrees: Define the tightness of the swirling effect.
4977%
cristy76f512e2011-09-12 01:26:56 +00004978% o method: the pixel interpolation method.
4979%
cristy3ed852e2009-09-05 21:47:34 +00004980% o exception: return any errors or warnings in this structure.
4981%
4982*/
4983MagickExport Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00004984 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004985{
4986#define SwirlImageTag "Swirl/Image"
4987
cristyfa112112010-01-04 17:48:07 +00004988 CacheView
4989 *image_view,
4990 *swirl_view;
4991
cristy3ed852e2009-09-05 21:47:34 +00004992 Image
4993 *swirl_image;
4994
cristy3ed852e2009-09-05 21:47:34 +00004995 MagickBooleanType
4996 status;
4997
cristybb503372010-05-27 20:51:26 +00004998 MagickOffsetType
4999 progress;
5000
cristy3ed852e2009-09-05 21:47:34 +00005001 MagickRealType
5002 radius;
5003
5004 PointInfo
5005 center,
5006 scale;
5007
cristybb503372010-05-27 20:51:26 +00005008 ssize_t
5009 y;
5010
cristy3ed852e2009-09-05 21:47:34 +00005011 /*
5012 Initialize swirl image attributes.
5013 */
5014 assert(image != (const Image *) NULL);
5015 assert(image->signature == MagickSignature);
5016 if (image->debug != MagickFalse)
5017 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5018 assert(exception != (ExceptionInfo *) NULL);
5019 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00005020 swirl_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005021 if (swirl_image == (Image *) NULL)
5022 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005023 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005024 {
cristy3ed852e2009-09-05 21:47:34 +00005025 swirl_image=DestroyImage(swirl_image);
5026 return((Image *) NULL);
5027 }
cristy4c08aed2011-07-01 19:47:50 +00005028 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005029 swirl_image->matte=MagickTrue;
5030 /*
5031 Compute scaling factor.
5032 */
5033 center.x=(double) image->columns/2.0;
5034 center.y=(double) image->rows/2.0;
5035 radius=MagickMax(center.x,center.y);
5036 scale.x=1.0;
5037 scale.y=1.0;
5038 if (image->columns > image->rows)
5039 scale.y=(double) image->columns/(double) image->rows;
5040 else
5041 if (image->columns < image->rows)
5042 scale.x=(double) image->rows/(double) image->columns;
5043 degrees=(double) DegreesToRadians(degrees);
5044 /*
5045 Swirl image.
5046 */
5047 status=MagickTrue;
5048 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00005049 image_view=AcquireCacheView(image);
5050 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005051#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005052 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005053#endif
cristybb503372010-05-27 20:51:26 +00005054 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005055 {
cristy3ed852e2009-09-05 21:47:34 +00005056 MagickRealType
5057 distance;
5058
5059 PointInfo
5060 delta;
5061
cristy6d188022011-09-12 13:23:33 +00005062 register const Quantum
5063 *restrict p;
5064
cristybb503372010-05-27 20:51:26 +00005065 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005066 x;
5067
cristy4c08aed2011-07-01 19:47:50 +00005068 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005069 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005070
5071 if (status == MagickFalse)
5072 continue;
cristy6d188022011-09-12 13:23:33 +00005073 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00005074 q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00005075 exception);
cristy6d188022011-09-12 13:23:33 +00005076 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005077 {
5078 status=MagickFalse;
5079 continue;
5080 }
cristy3ed852e2009-09-05 21:47:34 +00005081 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00005082 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005083 {
5084 /*
5085 Determine if the pixel is within an ellipse.
5086 */
cristy10a6c612012-01-29 21:41:05 +00005087 if (GetPixelMask(image,p) != 0)
5088 {
5089 p+=GetPixelChannels(image);
5090 q+=GetPixelChannels(swirl_image);
5091 continue;
5092 }
cristy3ed852e2009-09-05 21:47:34 +00005093 delta.x=scale.x*(double) (x-center.x);
5094 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00005095 if (distance >= (radius*radius))
5096 {
cristy1707c6c2012-01-18 23:30:54 +00005097 register ssize_t
5098 i;
5099
cristy6d188022011-09-12 13:23:33 +00005100 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy1707c6c2012-01-18 23:30:54 +00005101 {
5102 PixelChannel
5103 channel;
5104
5105 PixelTrait
5106 swirl_traits,
5107 traits;
5108
5109 channel=GetPixelChannelMapChannel(image,i);
5110 traits=GetPixelChannelMapTraits(image,channel);
5111 swirl_traits=GetPixelChannelMapTraits(swirl_image,channel);
5112 if ((traits == UndefinedPixelTrait) ||
5113 (swirl_traits == UndefinedPixelTrait))
5114 continue;
5115 SetPixelChannel(swirl_image,channel,p[i],q);
5116 }
cristy6d188022011-09-12 13:23:33 +00005117 }
5118 else
cristy3ed852e2009-09-05 21:47:34 +00005119 {
5120 MagickRealType
5121 cosine,
5122 factor,
5123 sine;
5124
5125 /*
5126 Swirl the pixel.
5127 */
5128 factor=1.0-sqrt((double) distance)/radius;
5129 sine=sin((double) (degrees*factor*factor));
5130 cosine=cos((double) (degrees*factor*factor));
cristy76f512e2011-09-12 01:26:56 +00005131 status=InterpolatePixelChannels(image,image_view,swirl_image,method,
5132 ((cosine*delta.x-sine*delta.y)/scale.x+center.x),(double)
5133 ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00005134 }
cristy6d188022011-09-12 13:23:33 +00005135 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00005136 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00005137 }
5138 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5139 status=MagickFalse;
5140 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5141 {
5142 MagickBooleanType
5143 proceed;
5144
cristyb5d5f722009-11-04 03:03:49 +00005145#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005146 #pragma omp critical (MagickCore_SwirlImage)
cristy3ed852e2009-09-05 21:47:34 +00005147#endif
5148 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5149 if (proceed == MagickFalse)
5150 status=MagickFalse;
5151 }
5152 }
5153 swirl_view=DestroyCacheView(swirl_view);
5154 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005155 if (status == MagickFalse)
5156 swirl_image=DestroyImage(swirl_image);
5157 return(swirl_image);
5158}
5159
5160/*
5161%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5162% %
5163% %
5164% %
5165% T i n t I m a g e %
5166% %
5167% %
5168% %
5169%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5170%
5171% TintImage() applies a color vector to each pixel in the image. The length
5172% of the vector is 0 for black and white and at its maximum for the midtones.
5173% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5174%
5175% The format of the TintImage method is:
5176%
cristyb817c3f2011-10-03 14:00:35 +00005177% Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005178% const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005179%
5180% A description of each parameter follows:
5181%
5182% o image: the image.
5183%
cristyb817c3f2011-10-03 14:00:35 +00005184% o blend: A color value used for tinting.
cristy3ed852e2009-09-05 21:47:34 +00005185%
5186% o tint: A color value used for tinting.
5187%
5188% o exception: return any errors or warnings in this structure.
5189%
5190*/
cristyb817c3f2011-10-03 14:00:35 +00005191MagickExport Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005192 const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005193{
5194#define TintImageTag "Tint/Image"
5195
cristyc4c8d132010-01-07 01:58:38 +00005196 CacheView
5197 *image_view,
5198 *tint_view;
5199
cristy3ed852e2009-09-05 21:47:34 +00005200 GeometryInfo
5201 geometry_info;
5202
5203 Image
5204 *tint_image;
5205
cristy3ed852e2009-09-05 21:47:34 +00005206 MagickBooleanType
5207 status;
5208
cristybb503372010-05-27 20:51:26 +00005209 MagickOffsetType
5210 progress;
cristy3ed852e2009-09-05 21:47:34 +00005211
cristy28474bf2011-09-11 23:32:52 +00005212 MagickRealType
5213 intensity;
5214
cristy4c08aed2011-07-01 19:47:50 +00005215 PixelInfo
cristy1707c6c2012-01-18 23:30:54 +00005216 color_vector;
cristy3ed852e2009-09-05 21:47:34 +00005217
cristybb503372010-05-27 20:51:26 +00005218 MagickStatusType
5219 flags;
5220
5221 ssize_t
5222 y;
5223
cristy3ed852e2009-09-05 21:47:34 +00005224 /*
5225 Allocate tint image.
5226 */
5227 assert(image != (const Image *) NULL);
5228 assert(image->signature == MagickSignature);
5229 if (image->debug != MagickFalse)
5230 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5231 assert(exception != (ExceptionInfo *) NULL);
5232 assert(exception->signature == MagickSignature);
5233 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5234 if (tint_image == (Image *) NULL)
5235 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005236 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005237 {
cristy3ed852e2009-09-05 21:47:34 +00005238 tint_image=DestroyImage(tint_image);
5239 return((Image *) NULL);
5240 }
cristyaed9c382011-10-03 17:54:21 +00005241 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005242 return(tint_image);
5243 /*
5244 Determine RGB values of the color.
5245 */
cristy1707c6c2012-01-18 23:30:54 +00005246 GetPixelInfo(image,&color_vector);
cristyb817c3f2011-10-03 14:00:35 +00005247 flags=ParseGeometry(blend,&geometry_info);
cristy1707c6c2012-01-18 23:30:54 +00005248 color_vector.red=geometry_info.rho;
5249 color_vector.green=geometry_info.rho;
5250 color_vector.blue=geometry_info.rho;
5251 color_vector.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005252 if ((flags & SigmaValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005253 color_vector.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00005254 if ((flags & XiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005255 color_vector.blue=geometry_info.xi;
cristyb817c3f2011-10-03 14:00:35 +00005256 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005257 color_vector.alpha=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005258 if (image->colorspace == CMYKColorspace)
5259 {
cristy1707c6c2012-01-18 23:30:54 +00005260 color_vector.black=geometry_info.rho;
cristy76f512e2011-09-12 01:26:56 +00005261 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005262 color_vector.black=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005263 if ((flags & ChiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005264 color_vector.alpha=geometry_info.chi;
cristy76f512e2011-09-12 01:26:56 +00005265 }
cristy28474bf2011-09-11 23:32:52 +00005266 intensity=(MagickRealType) GetPixelInfoIntensity(tint);
cristy1707c6c2012-01-18 23:30:54 +00005267 color_vector.red=(MagickRealType) (color_vector.red*tint->red/100.0-
5268 intensity);
5269 color_vector.green=(MagickRealType) (color_vector.green*tint->green/100.0-
5270 intensity);
5271 color_vector.blue=(MagickRealType) (color_vector.blue*tint->blue/100.0-
5272 intensity);
5273 color_vector.black=(MagickRealType) (color_vector.black*tint->black/100.0-
5274 intensity);
5275 color_vector.alpha=(MagickRealType) (color_vector.alpha*tint->alpha/100.0-
5276 intensity);
cristy3ed852e2009-09-05 21:47:34 +00005277 /*
5278 Tint image.
5279 */
5280 status=MagickTrue;
5281 progress=0;
5282 image_view=AcquireCacheView(image);
5283 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005284#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005285 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005286#endif
cristybb503372010-05-27 20:51:26 +00005287 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005288 {
cristy4c08aed2011-07-01 19:47:50 +00005289 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005290 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005291
cristy4c08aed2011-07-01 19:47:50 +00005292 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005293 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005294
cristy6b91acb2011-04-19 12:23:54 +00005295 register ssize_t
5296 x;
5297
cristy3ed852e2009-09-05 21:47:34 +00005298 if (status == MagickFalse)
5299 continue;
5300 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5301 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5302 exception);
cristy4c08aed2011-07-01 19:47:50 +00005303 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005304 {
5305 status=MagickFalse;
5306 continue;
5307 }
cristybb503372010-05-27 20:51:26 +00005308 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005309 {
cristy4c08aed2011-07-01 19:47:50 +00005310 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005311 pixel;
5312
5313 MagickRealType
5314 weight;
5315
cristy1707c6c2012-01-18 23:30:54 +00005316 register ssize_t
5317 i;
5318
cristy10a6c612012-01-29 21:41:05 +00005319 if (GetPixelMask(image,p) != 0)
5320 {
5321 p+=GetPixelChannels(image);
5322 q+=GetPixelChannels(tint_image);
5323 continue;
5324 }
cristy1707c6c2012-01-18 23:30:54 +00005325 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
5326 {
5327 PixelChannel
5328 channel;
5329
5330 PixelTrait
5331 tint_traits,
5332 traits;
5333
5334 channel=GetPixelChannelMapChannel(image,i);
5335 traits=GetPixelChannelMapTraits(image,channel);
5336 tint_traits=GetPixelChannelMapTraits(tint_image,channel);
5337 if ((traits == UndefinedPixelTrait) ||
5338 (tint_traits == UndefinedPixelTrait))
5339 continue;
5340 if ((tint_traits & CopyPixelTrait) != 0)
5341 {
5342 SetPixelChannel(tint_image,channel,p[i],q);
5343 continue;
5344 }
5345 }
5346 GetPixelInfo(image,&pixel);
5347 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5348 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5349 (1.0-(4.0*(weight*weight)));
5350 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5351 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5352 (1.0-(4.0*(weight*weight)));
5353 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5354 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5355 (1.0-(4.0*(weight*weight)));
5356 weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5357 pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5358 (1.0-(4.0*(weight*weight)));
5359 SetPixelInfoPixel(tint_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005360 p+=GetPixelChannels(image);
5361 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005362 }
5363 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5364 status=MagickFalse;
5365 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5366 {
5367 MagickBooleanType
5368 proceed;
5369
cristyb5d5f722009-11-04 03:03:49 +00005370#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005371 #pragma omp critical (MagickCore_TintImage)
cristy3ed852e2009-09-05 21:47:34 +00005372#endif
5373 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5374 if (proceed == MagickFalse)
5375 status=MagickFalse;
5376 }
5377 }
5378 tint_view=DestroyCacheView(tint_view);
5379 image_view=DestroyCacheView(image_view);
5380 if (status == MagickFalse)
5381 tint_image=DestroyImage(tint_image);
5382 return(tint_image);
5383}
5384
5385/*
5386%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5387% %
5388% %
5389% %
5390% V i g n e t t e I m a g e %
5391% %
5392% %
5393% %
5394%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5395%
5396% VignetteImage() softens the edges of the image in vignette style.
5397%
5398% The format of the VignetteImage method is:
5399%
5400% Image *VignetteImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00005401% const double sigma,const ssize_t x,const ssize_t y,
cristy05c0c9a2011-09-05 23:16:13 +00005402% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005403%
5404% A description of each parameter follows:
5405%
5406% o image: the image.
5407%
5408% o radius: the radius of the pixel neighborhood.
5409%
5410% o sigma: the standard deviation of the Gaussian, in pixels.
5411%
5412% o x, y: Define the x and y ellipse offset.
5413%
5414% o exception: return any errors or warnings in this structure.
5415%
5416*/
5417MagickExport Image *VignetteImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00005418 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005419{
5420 char
5421 ellipse[MaxTextExtent];
5422
5423 DrawInfo
5424 *draw_info;
5425
5426 Image
5427 *canvas_image,
5428 *blur_image,
5429 *oval_image,
5430 *vignette_image;
5431
5432 assert(image != (Image *) NULL);
5433 assert(image->signature == MagickSignature);
5434 if (image->debug != MagickFalse)
5435 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5436 assert(exception != (ExceptionInfo *) NULL);
5437 assert(exception->signature == MagickSignature);
5438 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5439 if (canvas_image == (Image *) NULL)
5440 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005441 if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005442 {
cristy3ed852e2009-09-05 21:47:34 +00005443 canvas_image=DestroyImage(canvas_image);
5444 return((Image *) NULL);
5445 }
5446 canvas_image->matte=MagickTrue;
cristy98621462011-12-31 22:31:11 +00005447 oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
5448 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005449 if (oval_image == (Image *) NULL)
5450 {
5451 canvas_image=DestroyImage(canvas_image);
5452 return((Image *) NULL);
5453 }
cristy9950d572011-10-01 18:22:35 +00005454 (void) QueryColorCompliance("#000000",AllCompliance,
5455 &oval_image->background_color,exception);
cristyea1a8aa2011-10-20 13:24:06 +00005456 (void) SetImageBackgroundColor(oval_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005457 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00005458 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5459 exception);
5460 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5461 exception);
cristy1707c6c2012-01-18 23:30:54 +00005462 (void) FormatLocaleString(ellipse,MaxTextExtent,"ellipse %g,%g,%g,%g,"
5463 "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5464 image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005465 draw_info->primitive=AcquireString(ellipse);
cristy018f07f2011-09-04 21:15:19 +00005466 (void) DrawImage(oval_image,draw_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005467 draw_info=DestroyDrawInfo(draw_info);
cristyaa2c16c2012-03-25 22:21:35 +00005468 blur_image=BlurImage(oval_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00005469 oval_image=DestroyImage(oval_image);
5470 if (blur_image == (Image *) NULL)
5471 {
5472 canvas_image=DestroyImage(canvas_image);
5473 return((Image *) NULL);
5474 }
5475 blur_image->matte=MagickFalse;
cristy39172402012-03-30 13:04:39 +00005476 (void) CompositeImage(canvas_image,blur_image,IntensityCompositeOp,MagickTrue,
5477 0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00005478 blur_image=DestroyImage(blur_image);
5479 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5480 canvas_image=DestroyImage(canvas_image);
5481 return(vignette_image);
5482}
5483
5484/*
5485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5486% %
5487% %
5488% %
5489% W a v e I m a g e %
5490% %
5491% %
5492% %
5493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5494%
5495% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005496% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005497% by the given parameters.
5498%
5499% The format of the WaveImage method is:
5500%
5501% Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005502% const double wave_length,const PixelInterpolateMethod method,
5503% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005504%
5505% A description of each parameter follows:
5506%
5507% o image: the image.
5508%
5509% o amplitude, wave_length: Define the amplitude and wave length of the
5510% sine wave.
5511%
cristy5c4e2582011-09-11 19:21:03 +00005512% o interpolate: the pixel interpolation method.
5513%
cristy3ed852e2009-09-05 21:47:34 +00005514% o exception: return any errors or warnings in this structure.
5515%
5516*/
5517MagickExport Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005518 const double wave_length,const PixelInterpolateMethod method,
5519 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005520{
5521#define WaveImageTag "Wave/Image"
5522
cristyfa112112010-01-04 17:48:07 +00005523 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005524 *image_view,
cristyfa112112010-01-04 17:48:07 +00005525 *wave_view;
5526
cristy3ed852e2009-09-05 21:47:34 +00005527 Image
5528 *wave_image;
5529
cristy3ed852e2009-09-05 21:47:34 +00005530 MagickBooleanType
5531 status;
5532
cristybb503372010-05-27 20:51:26 +00005533 MagickOffsetType
5534 progress;
5535
cristy3ed852e2009-09-05 21:47:34 +00005536 MagickRealType
5537 *sine_map;
5538
cristybb503372010-05-27 20:51:26 +00005539 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005540 i;
5541
cristybb503372010-05-27 20:51:26 +00005542 ssize_t
5543 y;
5544
cristy3ed852e2009-09-05 21:47:34 +00005545 /*
5546 Initialize wave image attributes.
5547 */
5548 assert(image != (Image *) NULL);
5549 assert(image->signature == MagickSignature);
5550 if (image->debug != MagickFalse)
5551 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5552 assert(exception != (ExceptionInfo *) NULL);
5553 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005554 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005555 fabs(amplitude)),MagickTrue,exception);
5556 if (wave_image == (Image *) NULL)
5557 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005558 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005559 {
cristy3ed852e2009-09-05 21:47:34 +00005560 wave_image=DestroyImage(wave_image);
5561 return((Image *) NULL);
5562 }
cristy4c08aed2011-07-01 19:47:50 +00005563 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005564 wave_image->matte=MagickTrue;
5565 /*
5566 Allocate sine map.
5567 */
5568 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5569 sizeof(*sine_map));
5570 if (sine_map == (MagickRealType *) NULL)
5571 {
5572 wave_image=DestroyImage(wave_image);
5573 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5574 }
cristybb503372010-05-27 20:51:26 +00005575 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005576 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5577 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005578 /*
5579 Wave image.
5580 */
5581 status=MagickTrue;
5582 progress=0;
cristyd76c51e2011-03-26 00:21:26 +00005583 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005584 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005585 (void) SetCacheViewVirtualPixelMethod(image_view,
5586 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005587#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005588 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005589#endif
cristybb503372010-05-27 20:51:26 +00005590 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005591 {
cristy4c08aed2011-07-01 19:47:50 +00005592 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005593 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005594
cristye97bb922011-04-03 01:36:52 +00005595 register ssize_t
5596 x;
5597
cristy3ed852e2009-09-05 21:47:34 +00005598 if (status == MagickFalse)
5599 continue;
5600 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5601 exception);
cristyacd2ed22011-08-30 01:44:23 +00005602 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005603 {
5604 status=MagickFalse;
5605 continue;
5606 }
cristybb503372010-05-27 20:51:26 +00005607 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005608 {
cristy5c4e2582011-09-11 19:21:03 +00005609 status=InterpolatePixelChannels(image,image_view,wave_image,method,
5610 (double) x,(double) (y-sine_map[x]),q,exception);
cristyed231572011-07-14 02:18:59 +00005611 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005612 }
5613 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5614 status=MagickFalse;
5615 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5616 {
5617 MagickBooleanType
5618 proceed;
5619
cristyb5d5f722009-11-04 03:03:49 +00005620#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005621 #pragma omp critical (MagickCore_WaveImage)
cristy3ed852e2009-09-05 21:47:34 +00005622#endif
5623 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5624 if (proceed == MagickFalse)
5625 status=MagickFalse;
5626 }
5627 }
5628 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005629 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005630 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5631 if (status == MagickFalse)
5632 wave_image=DestroyImage(wave_image);
5633 return(wave_image);
5634}