blob: f58c26582ceb2f931b127c5a29e7dd56bd5d4fe6 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% FFFFF X X %
7% F X X %
8% FFF X %
9% F X X %
10% F X X %
11% %
12% %
13% MagickCore Image Special Effects Methods %
14% %
15% Software Design %
16% John Cristy %
17% October 1996 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/annotate.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/cache.h"
48#include "MagickCore/cache-view.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/composite.h"
52#include "MagickCore/decorate.h"
cristyc53413d2011-11-17 13:04:26 +000053#include "MagickCore/distort.h"
cristy4c08aed2011-07-01 19:47:50 +000054#include "MagickCore/draw.h"
55#include "MagickCore/effect.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/fx.h"
60#include "MagickCore/fx-private.h"
61#include "MagickCore/gem.h"
cristy8ea81222011-09-04 10:33:32 +000062#include "MagickCore/gem-private.h"
cristy4c08aed2011-07-01 19:47:50 +000063#include "MagickCore/geometry.h"
64#include "MagickCore/layer.h"
65#include "MagickCore/list.h"
66#include "MagickCore/log.h"
67#include "MagickCore/image.h"
68#include "MagickCore/image-private.h"
69#include "MagickCore/magick.h"
70#include "MagickCore/memory_.h"
71#include "MagickCore/monitor.h"
72#include "MagickCore/monitor-private.h"
73#include "MagickCore/option.h"
74#include "MagickCore/pixel.h"
75#include "MagickCore/pixel-accessor.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum.h"
78#include "MagickCore/quantum-private.h"
79#include "MagickCore/random_.h"
80#include "MagickCore/random-private.h"
81#include "MagickCore/resample.h"
82#include "MagickCore/resample-private.h"
83#include "MagickCore/resize.h"
cristy4c08aed2011-07-01 19:47:50 +000084#include "MagickCore/splay-tree.h"
85#include "MagickCore/statistic.h"
86#include "MagickCore/string_.h"
87#include "MagickCore/string-private.h"
88#include "MagickCore/thread-private.h"
89#include "MagickCore/transform.h"
90#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000091
92/*
93 Define declarations.
94*/
95#define LeftShiftOperator 0xf5
96#define RightShiftOperator 0xf6
97#define LessThanEqualOperator 0xf7
98#define GreaterThanEqualOperator 0xf8
99#define EqualOperator 0xf9
100#define NotEqualOperator 0xfa
101#define LogicalAndOperator 0xfb
102#define LogicalOrOperator 0xfc
cristy116af162010-08-13 01:25:47 +0000103#define ExponentialNotation 0xfd
cristy3ed852e2009-09-05 21:47:34 +0000104
105struct _FxInfo
106{
107 const Image
108 *images;
109
cristy3ed852e2009-09-05 21:47:34 +0000110 char
111 *expression;
112
113 FILE
114 *file;
115
116 SplayTreeInfo
117 *colors,
118 *symbols;
119
cristyd76c51e2011-03-26 00:21:26 +0000120 CacheView
121 **view;
cristy3ed852e2009-09-05 21:47:34 +0000122
123 RandomInfo
124 *random_info;
125
126 ExceptionInfo
127 *exception;
128};
129
130/*
131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
132% %
133% %
134% %
135+ A c q u i r e F x I n f o %
136% %
137% %
138% %
139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140%
141% AcquireFxInfo() allocates the FxInfo structure.
142%
143% The format of the AcquireFxInfo method is:
144%
145% FxInfo *AcquireFxInfo(Image *image,const char *expression)
cristy0a9b3722010-10-23 18:45:49 +0000146%
cristy3ed852e2009-09-05 21:47:34 +0000147% A description of each parameter follows:
148%
149% o image: the image.
150%
151% o expression: the expression.
152%
153*/
cristy7832dc22011-09-05 01:21:53 +0000154MagickPrivate FxInfo *AcquireFxInfo(const Image *image,const char *expression)
cristy3ed852e2009-09-05 21:47:34 +0000155{
156 char
157 fx_op[2];
158
cristycb180922011-03-11 14:41:24 +0000159 const Image
160 *next;
161
cristy3ed852e2009-09-05 21:47:34 +0000162 FxInfo
163 *fx_info;
164
cristybb503372010-05-27 20:51:26 +0000165 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000166 i;
167
cristy73bd4a52010-10-05 11:24:23 +0000168 fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +0000169 if (fx_info == (FxInfo *) NULL)
170 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
171 (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
172 fx_info->exception=AcquireExceptionInfo();
anthony7d86e172011-03-23 12:37:06 +0000173 fx_info->images=image;
cristy3ed852e2009-09-05 21:47:34 +0000174 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
175 RelinquishMagickMemory);
176 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
177 RelinquishMagickMemory);
cristyd76c51e2011-03-26 00:21:26 +0000178 fx_info->view=(CacheView **) AcquireQuantumMemory(GetImageListLength(
179 fx_info->images),sizeof(*fx_info->view));
180 if (fx_info->view == (CacheView **) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000181 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristya2262262011-03-11 02:50:37 +0000182 i=0;
cristy0ea377f2011-03-24 00:54:19 +0000183 next=GetFirstImageInList(fx_info->images);
184 for ( ; next != (Image *) NULL; next=next->next)
cristy3ed852e2009-09-05 21:47:34 +0000185 {
cristyd76c51e2011-03-26 00:21:26 +0000186 fx_info->view[i]=AcquireCacheView(next);
cristya2262262011-03-11 02:50:37 +0000187 i++;
cristy3ed852e2009-09-05 21:47:34 +0000188 }
189 fx_info->random_info=AcquireRandomInfo();
190 fx_info->expression=ConstantString(expression);
191 fx_info->file=stderr;
192 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
cristy37af0912011-05-23 16:09:42 +0000193 /*
194 Force right-to-left associativity for unary negation.
195 */
196 (void) SubstituteString(&fx_info->expression,"-","-1.0*");
cristy8b8a3ae2010-10-23 18:49:46 +0000197 /*
cristy3ed852e2009-09-05 21:47:34 +0000198 Convert complex to simple operators.
199 */
200 fx_op[1]='\0';
201 *fx_op=(char) LeftShiftOperator;
202 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
203 *fx_op=(char) RightShiftOperator;
204 (void) SubstituteString(&fx_info->expression,">>",fx_op);
205 *fx_op=(char) LessThanEqualOperator;
206 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
207 *fx_op=(char) GreaterThanEqualOperator;
208 (void) SubstituteString(&fx_info->expression,">=",fx_op);
209 *fx_op=(char) EqualOperator;
210 (void) SubstituteString(&fx_info->expression,"==",fx_op);
211 *fx_op=(char) NotEqualOperator;
212 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
213 *fx_op=(char) LogicalAndOperator;
214 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
215 *fx_op=(char) LogicalOrOperator;
216 (void) SubstituteString(&fx_info->expression,"||",fx_op);
cristy116af162010-08-13 01:25:47 +0000217 *fx_op=(char) ExponentialNotation;
218 (void) SubstituteString(&fx_info->expression,"**",fx_op);
cristy3ed852e2009-09-05 21:47:34 +0000219 return(fx_info);
220}
221
222/*
223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
224% %
225% %
226% %
227% A d d N o i s e I m a g e %
228% %
229% %
230% %
231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
232%
233% AddNoiseImage() adds random noise to the image.
234%
235% The format of the AddNoiseImage method is:
236%
237% Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
cristy9ed1f812011-10-08 02:00:08 +0000238% const double attenuate,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000239%
240% A description of each parameter follows:
241%
242% o image: the image.
243%
244% o channel: the channel type.
245%
246% o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
247% Impulse, Laplacian, or Poisson.
248%
cristy9ed1f812011-10-08 02:00:08 +0000249% o attenuate: attenuate the random distribution.
250%
cristy3ed852e2009-09-05 21:47:34 +0000251% o exception: return any errors or warnings in this structure.
252%
253*/
cristy9ed1f812011-10-08 02:00:08 +0000254MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
255 const double attenuate,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000256{
257#define AddNoiseImageTag "AddNoise/Image"
258
cristyfa112112010-01-04 17:48:07 +0000259 CacheView
260 *image_view,
261 *noise_view;
262
cristy3ed852e2009-09-05 21:47:34 +0000263 Image
264 *noise_image;
265
cristy3ed852e2009-09-05 21:47:34 +0000266 MagickBooleanType
267 status;
268
cristybb503372010-05-27 20:51:26 +0000269 MagickOffsetType
270 progress;
271
cristy3ed852e2009-09-05 21:47:34 +0000272 RandomInfo
cristyfa112112010-01-04 17:48:07 +0000273 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +0000274
cristybb503372010-05-27 20:51:26 +0000275 ssize_t
276 y;
277
cristy3ed852e2009-09-05 21:47:34 +0000278 /*
279 Initialize noise image attributes.
280 */
281 assert(image != (const Image *) NULL);
282 assert(image->signature == MagickSignature);
283 if (image->debug != MagickFalse)
284 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
285 assert(exception != (ExceptionInfo *) NULL);
286 assert(exception->signature == MagickSignature);
cristyb2145892011-10-10 00:55:32 +0000287 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000288 if (noise_image == (Image *) NULL)
289 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000290 if (SetImageStorageClass(noise_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000291 {
cristy3ed852e2009-09-05 21:47:34 +0000292 noise_image=DestroyImage(noise_image);
293 return((Image *) NULL);
294 }
295 /*
296 Add noise in each row.
297 */
cristy3ed852e2009-09-05 21:47:34 +0000298 status=MagickTrue;
299 progress=0;
300 random_info=AcquireRandomInfoThreadSet();
301 image_view=AcquireCacheView(image);
302 noise_view=AcquireCacheView(noise_image);
cristy319a1e72010-02-21 15:13:11 +0000303#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000304 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000305#endif
cristybb503372010-05-27 20:51:26 +0000306 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000307 {
cristy5c9e6f22010-09-17 17:31:01 +0000308 const int
309 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000310
cristy3ed852e2009-09-05 21:47:34 +0000311 MagickBooleanType
312 sync;
313
cristy4c08aed2011-07-01 19:47:50 +0000314 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000315 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000316
cristybb503372010-05-27 20:51:26 +0000317 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000318 x;
319
cristy4c08aed2011-07-01 19:47:50 +0000320 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000321 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000322
323 if (status == MagickFalse)
324 continue;
325 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +0000326 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +0000327 exception);
cristy4c08aed2011-07-01 19:47:50 +0000328 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000329 {
330 status=MagickFalse;
331 continue;
332 }
cristybb503372010-05-27 20:51:26 +0000333 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000334 {
cristy850b3072011-10-08 01:38:05 +0000335 register ssize_t
336 i;
337
338 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
339 {
340 PixelChannel
341 channel;
342
343 PixelTrait
344 noise_traits,
345 traits;
346
cristye2a912b2011-12-05 20:02:07 +0000347 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000348 traits=GetPixelChannelMapTraits(image,channel);
cristy850b3072011-10-08 01:38:05 +0000349 noise_traits=GetPixelChannelMapTraits(noise_image,channel);
350 if ((traits == UndefinedPixelTrait) ||
351 (noise_traits == UndefinedPixelTrait))
352 continue;
cristyec9e3a62012-02-01 02:09:32 +0000353 if (((noise_traits & CopyPixelTrait) != 0) ||
354 (GetPixelMask(image,p) != 0))
cristyb2145892011-10-10 00:55:32 +0000355 {
356 SetPixelChannel(noise_image,channel,p[i],q);
357 continue;
358 }
cristy850b3072011-10-08 01:38:05 +0000359 SetPixelChannel(noise_image,channel,ClampToQuantum(
360 GenerateDifferentialNoise(random_info[id],p[i],noise_type,attenuate)),
361 q);
362 }
cristyed231572011-07-14 02:18:59 +0000363 p+=GetPixelChannels(image);
364 q+=GetPixelChannels(noise_image);
cristy3ed852e2009-09-05 21:47:34 +0000365 }
366 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
367 if (sync == MagickFalse)
368 status=MagickFalse;
369 if (image->progress_monitor != (MagickProgressMonitor) NULL)
370 {
371 MagickBooleanType
372 proceed;
373
cristyb5d5f722009-11-04 03:03:49 +0000374#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy319a1e72010-02-21 15:13:11 +0000375 #pragma omp critical (MagickCore_AddNoiseImage)
cristy3ed852e2009-09-05 21:47:34 +0000376#endif
377 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
378 image->rows);
379 if (proceed == MagickFalse)
380 status=MagickFalse;
381 }
382 }
383 noise_view=DestroyCacheView(noise_view);
384 image_view=DestroyCacheView(image_view);
385 random_info=DestroyRandomInfoThreadSet(random_info);
386 if (status == MagickFalse)
387 noise_image=DestroyImage(noise_image);
388 return(noise_image);
389}
390
391/*
392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
393% %
394% %
395% %
396% B l u e S h i f t I m a g e %
397% %
398% %
399% %
400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
401%
402% BlueShiftImage() mutes the colors of the image to simulate a scene at
403% nighttime in the moonlight.
404%
405% The format of the BlueShiftImage method is:
406%
407% Image *BlueShiftImage(const Image *image,const double factor,
408% ExceptionInfo *exception)
409%
410% A description of each parameter follows:
411%
412% o image: the image.
413%
414% o factor: the shift factor.
415%
416% o exception: return any errors or warnings in this structure.
417%
418*/
419MagickExport Image *BlueShiftImage(const Image *image,const double factor,
420 ExceptionInfo *exception)
421{
422#define BlueShiftImageTag "BlueShift/Image"
423
cristyc4c8d132010-01-07 01:58:38 +0000424 CacheView
425 *image_view,
426 *shift_view;
427
cristy3ed852e2009-09-05 21:47:34 +0000428 Image
429 *shift_image;
430
cristy3ed852e2009-09-05 21:47:34 +0000431 MagickBooleanType
432 status;
433
cristybb503372010-05-27 20:51:26 +0000434 MagickOffsetType
435 progress;
436
437 ssize_t
438 y;
439
cristy3ed852e2009-09-05 21:47:34 +0000440 /*
441 Allocate blue shift image.
442 */
443 assert(image != (const Image *) NULL);
444 assert(image->signature == MagickSignature);
445 if (image->debug != MagickFalse)
446 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
447 assert(exception != (ExceptionInfo *) NULL);
448 assert(exception->signature == MagickSignature);
cristya6d7a9b2012-01-18 20:04:48 +0000449 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000450 if (shift_image == (Image *) NULL)
451 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000452 if (SetImageStorageClass(shift_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000453 {
cristy3ed852e2009-09-05 21:47:34 +0000454 shift_image=DestroyImage(shift_image);
455 return((Image *) NULL);
456 }
457 /*
458 Blue-shift DirectClass image.
459 */
460 status=MagickTrue;
461 progress=0;
462 image_view=AcquireCacheView(image);
463 shift_view=AcquireCacheView(shift_image);
cristy319a1e72010-02-21 15:13:11 +0000464#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000465 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000466#endif
cristybb503372010-05-27 20:51:26 +0000467 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000468 {
469 MagickBooleanType
470 sync;
471
cristy4c08aed2011-07-01 19:47:50 +0000472 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000473 pixel;
474
475 Quantum
476 quantum;
477
cristy4c08aed2011-07-01 19:47:50 +0000478 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000479 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000480
cristybb503372010-05-27 20:51:26 +0000481 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000482 x;
483
cristy4c08aed2011-07-01 19:47:50 +0000484 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000485 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000486
487 if (status == MagickFalse)
488 continue;
489 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
490 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
491 exception);
cristy4c08aed2011-07-01 19:47:50 +0000492 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000493 {
494 status=MagickFalse;
495 continue;
496 }
cristybb503372010-05-27 20:51:26 +0000497 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000498 {
cristy4c08aed2011-07-01 19:47:50 +0000499 quantum=GetPixelRed(image,p);
500 if (GetPixelGreen(image,p) < quantum)
501 quantum=GetPixelGreen(image,p);
502 if (GetPixelBlue(image,p) < quantum)
503 quantum=GetPixelBlue(image,p);
504 pixel.red=0.5*(GetPixelRed(image,p)+factor*quantum);
505 pixel.green=0.5*(GetPixelGreen(image,p)+factor*quantum);
506 pixel.blue=0.5*(GetPixelBlue(image,p)+factor*quantum);
507 quantum=GetPixelRed(image,p);
508 if (GetPixelGreen(image,p) > quantum)
509 quantum=GetPixelGreen(image,p);
510 if (GetPixelBlue(image,p) > quantum)
511 quantum=GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000512 pixel.red=0.5*(pixel.red+factor*quantum);
513 pixel.green=0.5*(pixel.green+factor*quantum);
514 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristy4c08aed2011-07-01 19:47:50 +0000515 SetPixelRed(shift_image,ClampToQuantum(pixel.red),q);
516 SetPixelGreen(shift_image,ClampToQuantum(pixel.green),q);
517 SetPixelBlue(shift_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000518 p+=GetPixelChannels(image);
519 q+=GetPixelChannels(shift_image);
cristy3ed852e2009-09-05 21:47:34 +0000520 }
521 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
522 if (sync == MagickFalse)
523 status=MagickFalse;
524 if (image->progress_monitor != (MagickProgressMonitor) NULL)
525 {
526 MagickBooleanType
527 proceed;
528
cristy319a1e72010-02-21 15:13:11 +0000529#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000530 #pragma omp critical (MagickCore_BlueShiftImage)
cristy3ed852e2009-09-05 21:47:34 +0000531#endif
532 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
533 image->rows);
534 if (proceed == MagickFalse)
535 status=MagickFalse;
536 }
537 }
538 image_view=DestroyCacheView(image_view);
539 shift_view=DestroyCacheView(shift_view);
540 if (status == MagickFalse)
541 shift_image=DestroyImage(shift_image);
542 return(shift_image);
543}
544
545/*
546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547% %
548% %
549% %
550% C h a r c o a l I m a g e %
551% %
552% %
553% %
554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555%
556% CharcoalImage() creates a new image that is a copy of an existing one with
557% the edge highlighted. It allocates the memory necessary for the new Image
558% structure and returns a pointer to the new image.
559%
560% The format of the CharcoalImage method is:
561%
562% Image *CharcoalImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +0000563% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000564%
565% A description of each parameter follows:
566%
567% o image: the image.
568%
569% o radius: the radius of the pixel neighborhood.
570%
571% o sigma: the standard deviation of the Gaussian, in pixels.
572%
573% o exception: return any errors or warnings in this structure.
574%
575*/
576MagickExport Image *CharcoalImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +0000577 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000578{
579 Image
580 *charcoal_image,
581 *clone_image,
582 *edge_image;
583
584 assert(image != (Image *) NULL);
585 assert(image->signature == MagickSignature);
586 if (image->debug != MagickFalse)
587 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
588 assert(exception != (ExceptionInfo *) NULL);
589 assert(exception->signature == MagickSignature);
590 clone_image=CloneImage(image,0,0,MagickTrue,exception);
591 if (clone_image == (Image *) NULL)
592 return((Image *) NULL);
cristy018f07f2011-09-04 21:15:19 +0000593 (void) SetImageType(clone_image,GrayscaleType,exception);
cristy8ae632d2011-09-05 17:29:53 +0000594 edge_image=EdgeImage(clone_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000595 clone_image=DestroyImage(clone_image);
596 if (edge_image == (Image *) NULL)
597 return((Image *) NULL);
cristyaa2c16c2012-03-25 22:21:35 +0000598 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000599 edge_image=DestroyImage(edge_image);
600 if (charcoal_image == (Image *) NULL)
601 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +0000602 (void) NormalizeImage(charcoal_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +0000603 (void) NegateImage(charcoal_image,MagickFalse,exception);
cristy018f07f2011-09-04 21:15:19 +0000604 (void) SetImageType(charcoal_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000605 return(charcoal_image);
606}
607
608/*
609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
610% %
611% %
612% %
613% C o l o r i z e I m a g e %
614% %
615% %
616% %
617%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
618%
619% ColorizeImage() blends the fill color with each pixel in the image.
620% A percentage blend is specified with opacity. Control the application
621% of different color components by specifying a different percentage for
622% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
623%
624% The format of the ColorizeImage method is:
625%
cristyc7e6ff62011-10-03 13:46:11 +0000626% Image *ColorizeImage(const Image *image,const char *blend,
627% const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000628%
629% A description of each parameter follows:
630%
631% o image: the image.
632%
cristyc7e6ff62011-10-03 13:46:11 +0000633% o blend: A character string indicating the level of blending as a
cristy3ed852e2009-09-05 21:47:34 +0000634% percentage.
635%
636% o colorize: A color value.
637%
638% o exception: return any errors or warnings in this structure.
639%
640*/
cristyc7e6ff62011-10-03 13:46:11 +0000641MagickExport Image *ColorizeImage(const Image *image,const char *blend,
642 const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000643{
644#define ColorizeImageTag "Colorize/Image"
645
cristyc4c8d132010-01-07 01:58:38 +0000646 CacheView
647 *colorize_view,
648 *image_view;
649
cristy3ed852e2009-09-05 21:47:34 +0000650 GeometryInfo
651 geometry_info;
652
653 Image
654 *colorize_image;
655
cristy3ed852e2009-09-05 21:47:34 +0000656 MagickBooleanType
657 status;
658
cristybb503372010-05-27 20:51:26 +0000659 MagickOffsetType
660 progress;
661
cristy3ed852e2009-09-05 21:47:34 +0000662 MagickStatusType
663 flags;
664
cristyc7e6ff62011-10-03 13:46:11 +0000665 PixelInfo
cristyf9bf43e2012-04-07 18:13:07 +0000666 fill_color;
cristyc7e6ff62011-10-03 13:46:11 +0000667
cristybb503372010-05-27 20:51:26 +0000668 ssize_t
669 y;
670
cristy3ed852e2009-09-05 21:47:34 +0000671 /*
672 Allocate colorized image.
673 */
674 assert(image != (const Image *) NULL);
675 assert(image->signature == MagickSignature);
676 if (image->debug != MagickFalse)
677 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
678 assert(exception != (ExceptionInfo *) NULL);
679 assert(exception->signature == MagickSignature);
680 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
681 exception);
682 if (colorize_image == (Image *) NULL)
683 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000684 if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000685 {
cristy3ed852e2009-09-05 21:47:34 +0000686 colorize_image=DestroyImage(colorize_image);
687 return((Image *) NULL);
688 }
cristy5b67d4e2012-02-07 19:43:53 +0000689 if ((colorize->matte != MagickFalse) &&
690 (colorize_image->matte == MagickFalse))
691 (void) SetImageAlpha(colorize_image,OpaqueAlpha,exception);
cristyc7e6ff62011-10-03 13:46:11 +0000692 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000693 return(colorize_image);
694 /*
anthonyfd706f92012-01-19 04:22:02 +0000695 Determine RGB values of the fill color for pixel
cristy3ed852e2009-09-05 21:47:34 +0000696 */
cristyf9bf43e2012-04-07 18:13:07 +0000697 GetPixelInfo(image,&fill_color);
cristyb817c3f2011-10-03 14:00:35 +0000698 flags=ParseGeometry(blend,&geometry_info);
cristyf9bf43e2012-04-07 18:13:07 +0000699 fill_color.red=geometry_info.rho;
700 fill_color.green=geometry_info.rho;
701 fill_color.blue=geometry_info.rho;
702 fill_color.black=geometry_info.rho;
703 fill_color.alpha=100.0;
cristy3ed852e2009-09-05 21:47:34 +0000704 if ((flags & SigmaValue) != 0)
cristyf9bf43e2012-04-07 18:13:07 +0000705 fill_color.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +0000706 if ((flags & XiValue) != 0)
cristyf9bf43e2012-04-07 18:13:07 +0000707 fill_color.blue=geometry_info.xi;
cristy3ed852e2009-09-05 21:47:34 +0000708 if ((flags & PsiValue) != 0)
cristyf9bf43e2012-04-07 18:13:07 +0000709 fill_color.alpha=geometry_info.psi;
710 if (fill_color.colorspace == CMYKColorspace)
cristyc7e6ff62011-10-03 13:46:11 +0000711 {
cristyc7e6ff62011-10-03 13:46:11 +0000712 if ((flags & PsiValue) != 0)
cristyf9bf43e2012-04-07 18:13:07 +0000713 fill_color.black=geometry_info.psi;
cristyc7e6ff62011-10-03 13:46:11 +0000714 if ((flags & ChiValue) != 0)
cristyf9bf43e2012-04-07 18:13:07 +0000715 fill_color.alpha=geometry_info.chi;
cristyc7e6ff62011-10-03 13:46:11 +0000716 }
cristyf9bf43e2012-04-07 18:13:07 +0000717 if ((image->colorspace == GRAYColorspace) &&
718 (IsPixelInfoGray(&fill_color) != MagickFalse))
719 colorize_image->colorspace=sRGBColorspace;
cristy3ed852e2009-09-05 21:47:34 +0000720 /*
721 Colorize DirectClass image.
722 */
723 status=MagickTrue;
724 progress=0;
725 image_view=AcquireCacheView(image);
726 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000727#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000728 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000729#endif
cristybb503372010-05-27 20:51:26 +0000730 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000731 {
732 MagickBooleanType
733 sync;
734
cristyf9bf43e2012-04-07 18:13:07 +0000735 PixelInfo
736 pixel;
737
cristy4c08aed2011-07-01 19:47:50 +0000738 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000739 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000740
cristybb503372010-05-27 20:51:26 +0000741 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000742 x;
743
cristy4c08aed2011-07-01 19:47:50 +0000744 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000745 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000746
747 if (status == MagickFalse)
748 continue;
749 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
750 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
751 exception);
cristy4c08aed2011-07-01 19:47:50 +0000752 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000753 {
754 status=MagickFalse;
755 continue;
756 }
cristyf9bf43e2012-04-07 18:13:07 +0000757 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000758 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000759 {
cristyf9bf43e2012-04-07 18:13:07 +0000760 GetPixelInfoPixel(image,p,&pixel);
761 pixel.red=(pixel.red*(100.0-fill_color.red)+colorize->red*
762 fill_color.red)/100.0;
763 pixel.green=(pixel.green*(100.0-fill_color.green)+colorize->green*
764 fill_color.green)/100.0;
765 pixel.blue=(pixel.blue*(100.0-fill_color.blue)+colorize->blue*
766 fill_color.blue)/100.0;
767 pixel.black=(pixel.black*(100.0-fill_color.black)+colorize->black*
768 fill_color.black)/100.0;
769 pixel.alpha=(pixel.alpha*(100.0-fill_color.alpha)+colorize->alpha*
770 fill_color.alpha)/100.0;
771 SetPixelInfoPixel(colorize_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000772 p+=GetPixelChannels(image);
773 q+=GetPixelChannels(colorize_image);
cristy3ed852e2009-09-05 21:47:34 +0000774 }
775 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
776 if (sync == MagickFalse)
777 status=MagickFalse;
778 if (image->progress_monitor != (MagickProgressMonitor) NULL)
779 {
780 MagickBooleanType
781 proceed;
782
cristy319a1e72010-02-21 15:13:11 +0000783#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000784 #pragma omp critical (MagickCore_ColorizeImage)
cristy3ed852e2009-09-05 21:47:34 +0000785#endif
786 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
787 if (proceed == MagickFalse)
788 status=MagickFalse;
789 }
790 }
791 image_view=DestroyCacheView(image_view);
792 colorize_view=DestroyCacheView(colorize_view);
793 if (status == MagickFalse)
794 colorize_image=DestroyImage(colorize_image);
795 return(colorize_image);
796}
797
798/*
799%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
800% %
801% %
802% %
cristye6365592010-04-02 17:31:23 +0000803% C o l o r M a t r i x I m a g e %
804% %
805% %
806% %
807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808%
809% ColorMatrixImage() applies color transformation to an image. This method
810% permits saturation changes, hue rotation, luminance to alpha, and various
811% other effects. Although variable-sized transformation matrices can be used,
812% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
813% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
814% except offsets are in column 6 rather than 5 (in support of CMYKA images)
815% and offsets are normalized (divide Flash offset by 255).
816%
817% The format of the ColorMatrixImage method is:
818%
819% Image *ColorMatrixImage(const Image *image,
820% const KernelInfo *color_matrix,ExceptionInfo *exception)
821%
822% A description of each parameter follows:
823%
824% o image: the image.
825%
826% o color_matrix: the color matrix.
827%
828% o exception: return any errors or warnings in this structure.
829%
830*/
anthonyfd706f92012-01-19 04:22:02 +0000831/* FUTURE: modify to make use of a MagickMatrix Mutliply function
832 That should be provided in "matrix.c"
833 (ASIDE: actually distorts should do this too but currently doesn't)
834*/
835
cristye6365592010-04-02 17:31:23 +0000836MagickExport Image *ColorMatrixImage(const Image *image,
837 const KernelInfo *color_matrix,ExceptionInfo *exception)
838{
839#define ColorMatrixImageTag "ColorMatrix/Image"
840
841 CacheView
842 *color_view,
843 *image_view;
844
845 double
846 ColorMatrix[6][6] =
847 {
848 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
849 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
850 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
851 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
852 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
853 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
854 };
855
856 Image
857 *color_image;
858
cristye6365592010-04-02 17:31:23 +0000859 MagickBooleanType
860 status;
861
cristybb503372010-05-27 20:51:26 +0000862 MagickOffsetType
863 progress;
864
865 register ssize_t
cristye6365592010-04-02 17:31:23 +0000866 i;
867
cristybb503372010-05-27 20:51:26 +0000868 ssize_t
869 u,
870 v,
871 y;
872
cristye6365592010-04-02 17:31:23 +0000873 /*
anthonyfd706f92012-01-19 04:22:02 +0000874 Map given color_matrix, into a 6x6 matrix RGBKA and a constant
cristye6365592010-04-02 17:31:23 +0000875 */
876 assert(image != (Image *) NULL);
877 assert(image->signature == MagickSignature);
878 if (image->debug != MagickFalse)
879 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
880 assert(exception != (ExceptionInfo *) NULL);
881 assert(exception->signature == MagickSignature);
882 i=0;
cristybb503372010-05-27 20:51:26 +0000883 for (v=0; v < (ssize_t) color_matrix->height; v++)
884 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000885 {
886 if ((v < 6) && (u < 6))
887 ColorMatrix[v][u]=color_matrix->values[i];
888 i++;
889 }
890 /*
891 Initialize color image.
892 */
cristy12550e62010-06-07 12:46:40 +0000893 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000894 if (color_image == (Image *) NULL)
895 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000896 if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
cristye6365592010-04-02 17:31:23 +0000897 {
cristye6365592010-04-02 17:31:23 +0000898 color_image=DestroyImage(color_image);
899 return((Image *) NULL);
900 }
901 if (image->debug != MagickFalse)
902 {
903 char
904 format[MaxTextExtent],
905 *message;
906
907 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
908 " ColorMatrix image with color matrix:");
909 message=AcquireString("");
910 for (v=0; v < 6; v++)
911 {
912 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000913 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000914 (void) ConcatenateString(&message,format);
915 for (u=0; u < 6; u++)
916 {
cristyb51dff52011-05-19 16:55:47 +0000917 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
cristye6365592010-04-02 17:31:23 +0000918 ColorMatrix[v][u]);
919 (void) ConcatenateString(&message,format);
920 }
921 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
922 }
923 message=DestroyString(message);
924 }
925 /*
anthonyfd706f92012-01-19 04:22:02 +0000926 Apply the ColorMatrix to image.
cristye6365592010-04-02 17:31:23 +0000927 */
928 status=MagickTrue;
929 progress=0;
930 image_view=AcquireCacheView(image);
931 color_view=AcquireCacheView(color_image);
932#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000933 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye6365592010-04-02 17:31:23 +0000934#endif
cristybb503372010-05-27 20:51:26 +0000935 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000936 {
cristyfcc25d92012-02-19 23:06:48 +0000937 PixelInfo
cristye6365592010-04-02 17:31:23 +0000938 pixel;
939
cristy4c08aed2011-07-01 19:47:50 +0000940 register const Quantum
cristye6365592010-04-02 17:31:23 +0000941 *restrict p;
942
cristy4c08aed2011-07-01 19:47:50 +0000943 register Quantum
944 *restrict q;
945
cristybb503372010-05-27 20:51:26 +0000946 register ssize_t
cristye6365592010-04-02 17:31:23 +0000947 x;
948
cristye6365592010-04-02 17:31:23 +0000949 if (status == MagickFalse)
950 continue;
951 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
952 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
953 exception);
cristy4c08aed2011-07-01 19:47:50 +0000954 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristye6365592010-04-02 17:31:23 +0000955 {
956 status=MagickFalse;
957 continue;
958 }
cristyfcc25d92012-02-19 23:06:48 +0000959 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000960 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +0000961 {
cristybb503372010-05-27 20:51:26 +0000962 register ssize_t
cristye6365592010-04-02 17:31:23 +0000963 v;
964
cristybb503372010-05-27 20:51:26 +0000965 size_t
cristye6365592010-04-02 17:31:23 +0000966 height;
967
cristyfcc25d92012-02-19 23:06:48 +0000968 GetPixelInfoPixel(image,p,&pixel);
cristye6365592010-04-02 17:31:23 +0000969 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +0000970 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +0000971 {
cristyfcc25d92012-02-19 23:06:48 +0000972 MagickRealType
973 sum;
974
975 sum=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
cristy4c08aed2011-07-01 19:47:50 +0000976 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +0000977 if (image->colorspace == CMYKColorspace)
cristyfcc25d92012-02-19 23:06:48 +0000978 sum+=ColorMatrix[v][3]*GetPixelBlack(image,p);
cristy4c08aed2011-07-01 19:47:50 +0000979 if (image->matte != MagickFalse)
cristyfcc25d92012-02-19 23:06:48 +0000980 sum+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
981 sum+=QuantumRange*ColorMatrix[v][5];
cristye6365592010-04-02 17:31:23 +0000982 switch (v)
983 {
cristyfcc25d92012-02-19 23:06:48 +0000984 case 0: pixel.red=sum; break;
985 case 1: pixel.green=sum; break;
986 case 2: pixel.blue=sum; break;
987 case 3: pixel.black=sum; break;
988 case 4: pixel.alpha=sum; break;
989 default: break;
cristye6365592010-04-02 17:31:23 +0000990 }
991 }
cristyfcc25d92012-02-19 23:06:48 +0000992 SetPixelInfoPixel(color_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000993 p+=GetPixelChannels(image);
994 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +0000995 }
996 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
997 status=MagickFalse;
998 if (image->progress_monitor != (MagickProgressMonitor) NULL)
999 {
1000 MagickBooleanType
1001 proceed;
1002
1003#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00001004 #pragma omp critical (MagickCore_ColorMatrixImage)
cristye6365592010-04-02 17:31:23 +00001005#endif
1006 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1007 image->rows);
1008 if (proceed == MagickFalse)
1009 status=MagickFalse;
1010 }
1011 }
1012 color_view=DestroyCacheView(color_view);
1013 image_view=DestroyCacheView(image_view);
1014 if (status == MagickFalse)
1015 color_image=DestroyImage(color_image);
1016 return(color_image);
1017}
1018
1019/*
1020%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1021% %
1022% %
1023% %
cristy3ed852e2009-09-05 21:47:34 +00001024+ D e s t r o y F x I n f o %
1025% %
1026% %
1027% %
1028%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1029%
1030% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1031%
1032% The format of the DestroyFxInfo method is:
1033%
1034% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1035%
1036% A description of each parameter follows:
1037%
1038% o fx_info: the fx info.
1039%
1040*/
cristy7832dc22011-09-05 01:21:53 +00001041MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
cristy3ed852e2009-09-05 21:47:34 +00001042{
cristybb503372010-05-27 20:51:26 +00001043 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001044 i;
1045
1046 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1047 fx_info->expression=DestroyString(fx_info->expression);
1048 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1049 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001050 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001051 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1052 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001053 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1054 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1055 return(fx_info);
1056}
1057
1058/*
1059%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1060% %
1061% %
1062% %
cristy3ed852e2009-09-05 21:47:34 +00001063+ 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 %
1064% %
1065% %
1066% %
1067%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1068%
1069% FxEvaluateChannelExpression() evaluates an expression and returns the
1070% results.
1071%
1072% The format of the FxEvaluateExpression method is:
1073%
1074% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001075% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001076% MagickRealType *alpha,Exceptioninfo *exception)
1077% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1078% MagickRealType *alpha,Exceptioninfo *exception)
1079%
1080% A description of each parameter follows:
1081%
1082% o fx_info: the fx info.
1083%
1084% o channel: the channel.
1085%
1086% o x,y: the pixel position.
1087%
1088% o alpha: the result.
1089%
1090% o exception: return any errors or warnings in this structure.
1091%
1092*/
1093
cristy351842f2010-03-07 15:27:38 +00001094static inline double MagickMax(const double x,const double y)
1095{
1096 if (x > y)
1097 return(x);
1098 return(y);
1099}
1100
1101static inline double MagickMin(const double x,const double y)
1102{
1103 if (x < y)
1104 return(x);
1105 return(y);
1106}
1107
cristy3ed852e2009-09-05 21:47:34 +00001108static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001109 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001110{
1111 char
1112 key[MaxTextExtent],
1113 statistic[MaxTextExtent];
1114
1115 const char
1116 *value;
1117
1118 register const char
1119 *p;
1120
1121 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1122 if (*p == '.')
1123 switch (*++p) /* e.g. depth.r */
1124 {
cristy541ae572011-08-05 19:08:59 +00001125 case 'r': channel=RedPixelChannel; break;
1126 case 'g': channel=GreenPixelChannel; break;
1127 case 'b': channel=BluePixelChannel; break;
1128 case 'c': channel=CyanPixelChannel; break;
1129 case 'm': channel=MagentaPixelChannel; break;
1130 case 'y': channel=YellowPixelChannel; break;
1131 case 'k': channel=BlackPixelChannel; break;
cristy3ed852e2009-09-05 21:47:34 +00001132 default: break;
1133 }
cristyb51dff52011-05-19 16:55:47 +00001134 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001135 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001136 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1137 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001138 return(QuantumScale*StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001139 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1140 if (LocaleNCompare(symbol,"depth",5) == 0)
1141 {
cristybb503372010-05-27 20:51:26 +00001142 size_t
cristy3ed852e2009-09-05 21:47:34 +00001143 depth;
1144
cristyfefab1b2011-07-05 00:33:22 +00001145 depth=GetImageDepth(image,exception);
1146 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001147 }
1148 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1149 {
1150 double
1151 kurtosis,
1152 skewness;
1153
cristyd42d9952011-07-08 14:21:50 +00001154 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001155 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001156 }
1157 if (LocaleNCompare(symbol,"maxima",6) == 0)
1158 {
1159 double
1160 maxima,
1161 minima;
1162
cristyd42d9952011-07-08 14:21:50 +00001163 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001164 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001165 }
1166 if (LocaleNCompare(symbol,"mean",4) == 0)
1167 {
1168 double
1169 mean,
1170 standard_deviation;
1171
cristyd42d9952011-07-08 14:21:50 +00001172 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001173 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001174 }
1175 if (LocaleNCompare(symbol,"minima",6) == 0)
1176 {
1177 double
1178 maxima,
1179 minima;
1180
cristyd42d9952011-07-08 14:21:50 +00001181 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001182 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001183 }
1184 if (LocaleNCompare(symbol,"skewness",8) == 0)
1185 {
1186 double
1187 kurtosis,
1188 skewness;
1189
cristyd42d9952011-07-08 14:21:50 +00001190 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001191 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001192 }
1193 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1194 {
1195 double
1196 mean,
1197 standard_deviation;
1198
cristyd42d9952011-07-08 14:21:50 +00001199 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001200 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001201 standard_deviation);
1202 }
1203 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1204 ConstantString(statistic));
cristydbdd0e32011-11-04 23:29:40 +00001205 return(QuantumScale*StringToDouble(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001206}
1207
1208static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001209 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001210 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001211
cristyb0aad4c2011-11-02 19:30:35 +00001212static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
1213{
1214 if (beta != 0)
1215 return(FxGCD(beta,alpha % beta));
1216 return(alpha);
1217}
1218
cristy3ed852e2009-09-05 21:47:34 +00001219static inline const char *FxSubexpression(const char *expression,
1220 ExceptionInfo *exception)
1221{
1222 const char
1223 *subexpression;
1224
cristybb503372010-05-27 20:51:26 +00001225 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001226 level;
1227
1228 level=0;
1229 subexpression=expression;
1230 while ((*subexpression != '\0') &&
1231 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1232 {
1233 if (strchr("(",(int) *subexpression) != (char *) NULL)
1234 level++;
1235 else
1236 if (strchr(")",(int) *subexpression) != (char *) NULL)
1237 level--;
1238 subexpression++;
1239 }
1240 if (*subexpression == '\0')
1241 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1242 "UnbalancedParenthesis","`%s'",expression);
1243 return(subexpression);
1244}
1245
cristy0568ffc2011-07-25 16:54:14 +00001246static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001247 const ssize_t x,const ssize_t y,const char *expression,
1248 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001249{
1250 char
1251 *q,
1252 subexpression[MaxTextExtent],
1253 symbol[MaxTextExtent];
1254
1255 const char
1256 *p,
1257 *value;
1258
1259 Image
1260 *image;
1261
cristy4c08aed2011-07-01 19:47:50 +00001262 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001263 pixel;
1264
1265 MagickRealType
1266 alpha,
1267 beta;
1268
1269 PointInfo
1270 point;
1271
cristybb503372010-05-27 20:51:26 +00001272 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001273 i;
1274
1275 size_t
cristy1707c6c2012-01-18 23:30:54 +00001276 length,
cristy3ed852e2009-09-05 21:47:34 +00001277 level;
1278
1279 p=expression;
1280 i=GetImageIndexInList(fx_info->images);
1281 level=0;
1282 point.x=(double) x;
1283 point.y=(double) y;
1284 if (isalpha((int) *(p+1)) == 0)
1285 {
1286 if (strchr("suv",(int) *p) != (char *) NULL)
1287 {
1288 switch (*p)
1289 {
1290 case 's':
1291 default:
1292 {
1293 i=GetImageIndexInList(fx_info->images);
1294 break;
1295 }
1296 case 'u': i=0; break;
1297 case 'v': i=1; break;
1298 }
1299 p++;
1300 if (*p == '[')
1301 {
1302 level++;
1303 q=subexpression;
1304 for (p++; *p != '\0'; )
1305 {
1306 if (*p == '[')
1307 level++;
1308 else
1309 if (*p == ']')
1310 {
1311 level--;
1312 if (level == 0)
1313 break;
1314 }
1315 *q++=(*p++);
1316 }
1317 *q='\0';
1318 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1319 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001320 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001321 p++;
1322 }
1323 if (*p == '.')
1324 p++;
1325 }
1326 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1327 {
1328 p++;
1329 if (*p == '{')
1330 {
1331 level++;
1332 q=subexpression;
1333 for (p++; *p != '\0'; )
1334 {
1335 if (*p == '{')
1336 level++;
1337 else
1338 if (*p == '}')
1339 {
1340 level--;
1341 if (level == 0)
1342 break;
1343 }
1344 *q++=(*p++);
1345 }
1346 *q='\0';
1347 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1348 &beta,exception);
1349 point.x=alpha;
1350 point.y=beta;
1351 p++;
1352 }
1353 else
1354 if (*p == '[')
1355 {
1356 level++;
1357 q=subexpression;
1358 for (p++; *p != '\0'; )
1359 {
1360 if (*p == '[')
1361 level++;
1362 else
1363 if (*p == ']')
1364 {
1365 level--;
1366 if (level == 0)
1367 break;
1368 }
1369 *q++=(*p++);
1370 }
1371 *q='\0';
1372 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1373 &beta,exception);
1374 point.x+=alpha;
1375 point.y+=beta;
1376 p++;
1377 }
1378 if (*p == '.')
1379 p++;
1380 }
1381 }
1382 length=GetImageListLength(fx_info->images);
1383 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001384 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001385 i%=length;
1386 image=GetImageFromList(fx_info->images,i);
1387 if (image == (Image *) NULL)
1388 {
1389 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1390 "NoSuchImage","`%s'",expression);
1391 return(0.0);
1392 }
cristy4c08aed2011-07-01 19:47:50 +00001393 GetPixelInfo(image,&pixel);
1394 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001395 point.x,point.y,&pixel,exception);
cristy1707c6c2012-01-18 23:30:54 +00001396 if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1397 (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001398 (LocaleCompare(p,"saturation") != 0) &&
1399 (LocaleCompare(p,"lightness") != 0))
1400 {
1401 char
1402 name[MaxTextExtent];
1403
1404 (void) CopyMagickString(name,p,MaxTextExtent);
1405 for (q=name+(strlen(name)-1); q > name; q--)
1406 {
1407 if (*q == ')')
1408 break;
1409 if (*q == '.')
1410 {
1411 *q='\0';
1412 break;
1413 }
1414 }
1415 if ((strlen(name) > 2) &&
1416 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1417 {
cristy4c08aed2011-07-01 19:47:50 +00001418 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001419 *color;
1420
cristy4c08aed2011-07-01 19:47:50 +00001421 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1422 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001423 {
1424 pixel=(*color);
1425 p+=strlen(name);
1426 }
1427 else
cristy1707c6c2012-01-18 23:30:54 +00001428 {
1429 MagickBooleanType
1430 status;
1431
1432 status=QueryColorCompliance(name,AllCompliance,&pixel,
1433 fx_info->exception);
1434 if (status != MagickFalse)
1435 {
1436 (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1437 name),ClonePixelInfo(&pixel));
1438 p+=strlen(name);
1439 }
1440 }
cristy3ed852e2009-09-05 21:47:34 +00001441 }
1442 }
1443 (void) CopyMagickString(symbol,p,MaxTextExtent);
1444 StripString(symbol);
1445 if (*symbol == '\0')
1446 {
1447 switch (channel)
1448 {
cristy0568ffc2011-07-25 16:54:14 +00001449 case RedPixelChannel: return(QuantumScale*pixel.red);
1450 case GreenPixelChannel: return(QuantumScale*pixel.green);
1451 case BluePixelChannel: return(QuantumScale*pixel.blue);
1452 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001453 {
1454 if (image->colorspace != CMYKColorspace)
1455 {
1456 (void) ThrowMagickException(exception,GetMagickModule(),
cristy1a020e42011-12-06 18:13:23 +00001457 ImageError,"ColorSeparatedImageRequired","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00001458 image->filename);
1459 return(0.0);
1460 }
cristy4c08aed2011-07-01 19:47:50 +00001461 return(QuantumScale*pixel.black);
1462 }
cristy0568ffc2011-07-25 16:54:14 +00001463 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001464 {
1465 MagickRealType
1466 alpha;
1467
1468 if (pixel.matte == MagickFalse)
1469 return(1.0);
1470 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1471 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001472 }
cristya382aca2011-12-06 18:22:48 +00001473 case IndexPixelChannel:
1474 return(0.0);
cristyb3a73b52011-07-26 01:34:43 +00001475 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001476 {
cristy4c08aed2011-07-01 19:47:50 +00001477 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001478 }
cristy3ed852e2009-09-05 21:47:34 +00001479 default:
1480 break;
1481 }
1482 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1483 "UnableToParseExpression","`%s'",p);
1484 return(0.0);
1485 }
1486 switch (*symbol)
1487 {
1488 case 'A':
1489 case 'a':
1490 {
1491 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001492 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001493 break;
1494 }
1495 case 'B':
1496 case 'b':
1497 {
1498 if (LocaleCompare(symbol,"b") == 0)
1499 return(QuantumScale*pixel.blue);
1500 break;
1501 }
1502 case 'C':
1503 case 'c':
1504 {
1505 if (LocaleNCompare(symbol,"channel",7) == 0)
1506 {
1507 GeometryInfo
1508 channel_info;
1509
1510 MagickStatusType
1511 flags;
1512
1513 flags=ParseGeometry(symbol+7,&channel_info);
1514 if (image->colorspace == CMYKColorspace)
1515 switch (channel)
1516 {
cristy0568ffc2011-07-25 16:54:14 +00001517 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001518 {
1519 if ((flags & RhoValue) == 0)
1520 return(0.0);
1521 return(channel_info.rho);
1522 }
cristy0568ffc2011-07-25 16:54:14 +00001523 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001524 {
1525 if ((flags & SigmaValue) == 0)
1526 return(0.0);
1527 return(channel_info.sigma);
1528 }
cristy0568ffc2011-07-25 16:54:14 +00001529 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001530 {
1531 if ((flags & XiValue) == 0)
1532 return(0.0);
1533 return(channel_info.xi);
1534 }
cristy0568ffc2011-07-25 16:54:14 +00001535 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001536 {
1537 if ((flags & PsiValue) == 0)
1538 return(0.0);
1539 return(channel_info.psi);
1540 }
cristy0568ffc2011-07-25 16:54:14 +00001541 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001542 {
1543 if ((flags & ChiValue) == 0)
1544 return(0.0);
1545 return(channel_info.chi);
1546 }
1547 default:
1548 return(0.0);
1549 }
1550 switch (channel)
1551 {
cristy0568ffc2011-07-25 16:54:14 +00001552 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001553 {
1554 if ((flags & RhoValue) == 0)
1555 return(0.0);
1556 return(channel_info.rho);
1557 }
cristy0568ffc2011-07-25 16:54:14 +00001558 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001559 {
1560 if ((flags & SigmaValue) == 0)
1561 return(0.0);
1562 return(channel_info.sigma);
1563 }
cristy0568ffc2011-07-25 16:54:14 +00001564 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001565 {
1566 if ((flags & XiValue) == 0)
1567 return(0.0);
1568 return(channel_info.xi);
1569 }
cristy0568ffc2011-07-25 16:54:14 +00001570 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001571 {
1572 if ((flags & ChiValue) == 0)
1573 return(0.0);
1574 return(channel_info.chi);
1575 }
cristy0568ffc2011-07-25 16:54:14 +00001576 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001577 {
1578 if ((flags & PsiValue) == 0)
1579 return(0.0);
1580 return(channel_info.psi);
1581 }
cristy3ed852e2009-09-05 21:47:34 +00001582 default:
1583 return(0.0);
1584 }
1585 return(0.0);
1586 }
1587 if (LocaleCompare(symbol,"c") == 0)
1588 return(QuantumScale*pixel.red);
1589 break;
1590 }
1591 case 'D':
1592 case 'd':
1593 {
1594 if (LocaleNCompare(symbol,"depth",5) == 0)
1595 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1596 break;
1597 }
1598 case 'G':
1599 case 'g':
1600 {
1601 if (LocaleCompare(symbol,"g") == 0)
1602 return(QuantumScale*pixel.green);
1603 break;
1604 }
1605 case 'K':
1606 case 'k':
1607 {
1608 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1609 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1610 if (LocaleCompare(symbol,"k") == 0)
1611 {
1612 if (image->colorspace != CMYKColorspace)
1613 {
1614 (void) ThrowMagickException(exception,GetMagickModule(),
1615 OptionError,"ColorSeparatedImageRequired","`%s'",
1616 image->filename);
1617 return(0.0);
1618 }
cristy4c08aed2011-07-01 19:47:50 +00001619 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001620 }
1621 break;
1622 }
1623 case 'H':
1624 case 'h':
1625 {
1626 if (LocaleCompare(symbol,"h") == 0)
1627 return((MagickRealType) image->rows);
1628 if (LocaleCompare(symbol,"hue") == 0)
1629 {
1630 double
1631 hue,
1632 lightness,
1633 saturation;
1634
cristyda1f9c12011-10-02 21:39:49 +00001635 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1636 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001637 return(hue);
1638 }
1639 break;
1640 }
1641 case 'I':
1642 case 'i':
1643 {
1644 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1645 (LocaleCompare(symbol,"image.minima") == 0) ||
1646 (LocaleCompare(symbol,"image.maxima") == 0) ||
1647 (LocaleCompare(symbol,"image.mean") == 0) ||
1648 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1649 (LocaleCompare(symbol,"image.skewness") == 0) ||
1650 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1651 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1652 if (LocaleCompare(symbol,"image.resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001653 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001654 if (LocaleCompare(symbol,"image.resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001655 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001656 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001657 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001658 if (LocaleCompare(symbol,"i") == 0)
1659 return((MagickRealType) x);
1660 break;
1661 }
1662 case 'J':
1663 case 'j':
1664 {
1665 if (LocaleCompare(symbol,"j") == 0)
1666 return((MagickRealType) y);
1667 break;
1668 }
1669 case 'L':
1670 case 'l':
1671 {
1672 if (LocaleCompare(symbol,"lightness") == 0)
1673 {
1674 double
1675 hue,
1676 lightness,
1677 saturation;
1678
cristyda1f9c12011-10-02 21:39:49 +00001679 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1680 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001681 return(lightness);
1682 }
1683 if (LocaleCompare(symbol,"luminance") == 0)
1684 {
1685 double
1686 luminence;
1687
1688 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1689 return(QuantumScale*luminence);
1690 }
1691 break;
1692 }
1693 case 'M':
1694 case 'm':
1695 {
1696 if (LocaleNCompare(symbol,"maxima",6) == 0)
1697 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1698 if (LocaleNCompare(symbol,"mean",4) == 0)
1699 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1700 if (LocaleNCompare(symbol,"minima",6) == 0)
1701 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1702 if (LocaleCompare(symbol,"m") == 0)
1703 return(QuantumScale*pixel.blue);
1704 break;
1705 }
1706 case 'N':
1707 case 'n':
1708 {
1709 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001710 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001711 break;
1712 }
1713 case 'O':
1714 case 'o':
1715 {
1716 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001717 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001718 break;
1719 }
1720 case 'P':
1721 case 'p':
1722 {
1723 if (LocaleCompare(symbol,"page.height") == 0)
1724 return((MagickRealType) image->page.height);
1725 if (LocaleCompare(symbol,"page.width") == 0)
1726 return((MagickRealType) image->page.width);
1727 if (LocaleCompare(symbol,"page.x") == 0)
1728 return((MagickRealType) image->page.x);
1729 if (LocaleCompare(symbol,"page.y") == 0)
1730 return((MagickRealType) image->page.y);
1731 break;
1732 }
1733 case 'R':
1734 case 'r':
1735 {
1736 if (LocaleCompare(symbol,"resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001737 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001738 if (LocaleCompare(symbol,"resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001739 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001740 if (LocaleCompare(symbol,"r") == 0)
1741 return(QuantumScale*pixel.red);
1742 break;
1743 }
1744 case 'S':
1745 case 's':
1746 {
1747 if (LocaleCompare(symbol,"saturation") == 0)
1748 {
1749 double
1750 hue,
1751 lightness,
1752 saturation;
1753
cristyda1f9c12011-10-02 21:39:49 +00001754 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1755 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001756 return(saturation);
1757 }
1758 if (LocaleNCompare(symbol,"skewness",8) == 0)
1759 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1760 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1761 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1762 break;
1763 }
1764 case 'T':
1765 case 't':
1766 {
1767 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001768 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001769 break;
1770 }
1771 case 'W':
1772 case 'w':
1773 {
1774 if (LocaleCompare(symbol,"w") == 0)
1775 return((MagickRealType) image->columns);
1776 break;
1777 }
1778 case 'Y':
1779 case 'y':
1780 {
1781 if (LocaleCompare(symbol,"y") == 0)
1782 return(QuantumScale*pixel.green);
1783 break;
1784 }
1785 case 'Z':
1786 case 'z':
1787 {
1788 if (LocaleCompare(symbol,"z") == 0)
1789 {
1790 MagickRealType
1791 depth;
1792
cristyfefab1b2011-07-05 00:33:22 +00001793 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001794 return(depth);
1795 }
1796 break;
1797 }
1798 default:
1799 break;
1800 }
1801 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1802 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001803 return((MagickRealType) StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001804 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1805 "UnableToParseExpression","`%s'",symbol);
1806 return(0.0);
1807}
1808
1809static const char *FxOperatorPrecedence(const char *expression,
1810 ExceptionInfo *exception)
1811{
1812 typedef enum
1813 {
1814 UndefinedPrecedence,
1815 NullPrecedence,
1816 BitwiseComplementPrecedence,
1817 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001818 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001819 MultiplyPrecedence,
1820 AdditionPrecedence,
1821 ShiftPrecedence,
1822 RelationalPrecedence,
1823 EquivalencyPrecedence,
1824 BitwiseAndPrecedence,
1825 BitwiseOrPrecedence,
1826 LogicalAndPrecedence,
1827 LogicalOrPrecedence,
1828 TernaryPrecedence,
1829 AssignmentPrecedence,
1830 CommaPrecedence,
1831 SeparatorPrecedence
1832 } FxPrecedence;
1833
1834 FxPrecedence
1835 precedence,
1836 target;
1837
1838 register const char
1839 *subexpression;
1840
1841 register int
1842 c;
1843
cristybb503372010-05-27 20:51:26 +00001844 size_t
cristy3ed852e2009-09-05 21:47:34 +00001845 level;
1846
1847 c=0;
1848 level=0;
1849 subexpression=(const char *) NULL;
1850 target=NullPrecedence;
1851 while (*expression != '\0')
1852 {
1853 precedence=UndefinedPrecedence;
1854 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1855 {
1856 expression++;
1857 continue;
1858 }
cristy488fa882010-03-01 22:34:24 +00001859 switch (*expression)
1860 {
1861 case 'A':
1862 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001863 {
cristyb33454f2011-08-03 02:10:45 +00001864#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001865 if (LocaleNCompare(expression,"acosh",5) == 0)
1866 {
1867 expression+=5;
1868 break;
1869 }
cristyb33454f2011-08-03 02:10:45 +00001870#endif
1871#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001872 if (LocaleNCompare(expression,"asinh",5) == 0)
1873 {
1874 expression+=5;
1875 break;
1876 }
cristyb33454f2011-08-03 02:10:45 +00001877#endif
1878#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001879 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001880 {
1881 expression+=5;
1882 break;
1883 }
cristyb33454f2011-08-03 02:10:45 +00001884#endif
cristy488fa882010-03-01 22:34:24 +00001885 break;
cristy3ed852e2009-09-05 21:47:34 +00001886 }
cristy62d455f2011-11-03 11:42:28 +00001887 case 'E':
1888 case 'e':
1889 {
1890 if ((LocaleNCompare(expression,"E+",2) == 0) ||
1891 (LocaleNCompare(expression,"E-",2) == 0))
1892 {
1893 expression+=2; /* scientific notation */
1894 break;
1895 }
1896 }
cristy488fa882010-03-01 22:34:24 +00001897 case 'J':
1898 case 'j':
1899 {
1900 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1901 (LocaleNCompare(expression,"j1",2) == 0))
1902 {
1903 expression+=2;
1904 break;
1905 }
1906 break;
1907 }
cristy2def9322010-06-18 23:59:37 +00001908 case '#':
1909 {
1910 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1911 expression++;
1912 break;
1913 }
cristy488fa882010-03-01 22:34:24 +00001914 default:
1915 break;
1916 }
cristy3ed852e2009-09-05 21:47:34 +00001917 if ((c == (int) '{') || (c == (int) '['))
1918 level++;
1919 else
1920 if ((c == (int) '}') || (c == (int) ']'))
1921 level--;
1922 if (level == 0)
1923 switch ((unsigned char) *expression)
1924 {
1925 case '~':
1926 case '!':
1927 {
1928 precedence=BitwiseComplementPrecedence;
1929 break;
1930 }
1931 case '^':
cristy6621e252010-08-13 00:42:57 +00001932 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001933 {
1934 precedence=ExponentPrecedence;
1935 break;
1936 }
1937 default:
1938 {
1939 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1940 (strchr(")",c) != (char *) NULL))) &&
1941 (((islower((int) ((char) *expression)) != 0) ||
1942 (strchr("(",(int) *expression) != (char *) NULL)) ||
1943 ((isdigit((int) ((char) c)) == 0) &&
1944 (isdigit((int) ((char) *expression)) != 0))) &&
1945 (strchr("xy",(int) *expression) == (char *) NULL))
1946 precedence=MultiplyPrecedence;
1947 break;
1948 }
1949 case '*':
1950 case '/':
1951 case '%':
1952 {
1953 precedence=MultiplyPrecedence;
1954 break;
1955 }
1956 case '+':
1957 case '-':
1958 {
1959 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1960 (isalpha(c) != 0))
1961 precedence=AdditionPrecedence;
1962 break;
1963 }
1964 case LeftShiftOperator:
1965 case RightShiftOperator:
1966 {
1967 precedence=ShiftPrecedence;
1968 break;
1969 }
1970 case '<':
1971 case LessThanEqualOperator:
1972 case GreaterThanEqualOperator:
1973 case '>':
1974 {
1975 precedence=RelationalPrecedence;
1976 break;
1977 }
1978 case EqualOperator:
1979 case NotEqualOperator:
1980 {
1981 precedence=EquivalencyPrecedence;
1982 break;
1983 }
1984 case '&':
1985 {
1986 precedence=BitwiseAndPrecedence;
1987 break;
1988 }
1989 case '|':
1990 {
1991 precedence=BitwiseOrPrecedence;
1992 break;
1993 }
1994 case LogicalAndOperator:
1995 {
1996 precedence=LogicalAndPrecedence;
1997 break;
1998 }
1999 case LogicalOrOperator:
2000 {
2001 precedence=LogicalOrPrecedence;
2002 break;
2003 }
cristy116af162010-08-13 01:25:47 +00002004 case ExponentialNotation:
2005 {
2006 precedence=ExponentialNotationPrecedence;
2007 break;
2008 }
cristy3ed852e2009-09-05 21:47:34 +00002009 case ':':
2010 case '?':
2011 {
2012 precedence=TernaryPrecedence;
2013 break;
2014 }
2015 case '=':
2016 {
2017 precedence=AssignmentPrecedence;
2018 break;
2019 }
2020 case ',':
2021 {
2022 precedence=CommaPrecedence;
2023 break;
2024 }
2025 case ';':
2026 {
2027 precedence=SeparatorPrecedence;
2028 break;
2029 }
2030 }
2031 if ((precedence == BitwiseComplementPrecedence) ||
2032 (precedence == TernaryPrecedence) ||
2033 (precedence == AssignmentPrecedence))
2034 {
2035 if (precedence > target)
2036 {
2037 /*
2038 Right-to-left associativity.
2039 */
2040 target=precedence;
2041 subexpression=expression;
2042 }
2043 }
2044 else
2045 if (precedence >= target)
2046 {
2047 /*
2048 Left-to-right associativity.
2049 */
2050 target=precedence;
2051 subexpression=expression;
2052 }
2053 if (strchr("(",(int) *expression) != (char *) NULL)
2054 expression=FxSubexpression(expression,exception);
2055 c=(int) (*expression++);
2056 }
2057 return(subexpression);
2058}
2059
2060static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002061 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002062 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002063{
2064 char
2065 *q,
2066 subexpression[MaxTextExtent];
2067
2068 MagickRealType
2069 alpha,
2070 gamma;
2071
2072 register const char
2073 *p;
2074
2075 *beta=0.0;
2076 if (exception->severity != UndefinedException)
2077 return(0.0);
2078 while (isspace((int) *expression) != 0)
2079 expression++;
2080 if (*expression == '\0')
2081 {
2082 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2083 "MissingExpression","`%s'",expression);
2084 return(0.0);
2085 }
cristy66322f02010-05-17 11:40:48 +00002086 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002087 p=FxOperatorPrecedence(expression,exception);
2088 if (p != (const char *) NULL)
2089 {
2090 (void) CopyMagickString(subexpression,expression,(size_t)
2091 (p-expression+1));
2092 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2093 exception);
2094 switch ((unsigned char) *p)
2095 {
2096 case '~':
2097 {
2098 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002099 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002100 return(*beta);
2101 }
2102 case '!':
2103 {
2104 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2105 return(*beta == 0.0 ? 1.0 : 0.0);
2106 }
2107 case '^':
2108 {
2109 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2110 channel,x,y,++p,beta,exception));
2111 return(*beta);
2112 }
2113 case '*':
cristy116af162010-08-13 01:25:47 +00002114 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002115 {
2116 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2117 return(alpha*(*beta));
2118 }
2119 case '/':
2120 {
2121 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2122 if (*beta == 0.0)
2123 {
2124 if (exception->severity == UndefinedException)
2125 (void) ThrowMagickException(exception,GetMagickModule(),
2126 OptionError,"DivideByZero","`%s'",expression);
2127 return(0.0);
2128 }
2129 return(alpha/(*beta));
2130 }
2131 case '%':
2132 {
2133 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2134 *beta=fabs(floor(((double) *beta)+0.5));
2135 if (*beta == 0.0)
2136 {
2137 (void) ThrowMagickException(exception,GetMagickModule(),
2138 OptionError,"DivideByZero","`%s'",expression);
2139 return(0.0);
2140 }
2141 return(fmod((double) alpha,(double) *beta));
2142 }
2143 case '+':
2144 {
2145 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2146 return(alpha+(*beta));
2147 }
2148 case '-':
2149 {
2150 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2151 return(alpha-(*beta));
2152 }
2153 case LeftShiftOperator:
2154 {
2155 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002156 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002157 return(*beta);
2158 }
2159 case RightShiftOperator:
2160 {
2161 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002162 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002163 return(*beta);
2164 }
2165 case '<':
2166 {
2167 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2168 return(alpha < *beta ? 1.0 : 0.0);
2169 }
2170 case LessThanEqualOperator:
2171 {
2172 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2173 return(alpha <= *beta ? 1.0 : 0.0);
2174 }
2175 case '>':
2176 {
2177 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2178 return(alpha > *beta ? 1.0 : 0.0);
2179 }
2180 case GreaterThanEqualOperator:
2181 {
2182 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2183 return(alpha >= *beta ? 1.0 : 0.0);
2184 }
2185 case EqualOperator:
2186 {
2187 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2188 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2189 }
2190 case NotEqualOperator:
2191 {
2192 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2193 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2194 }
2195 case '&':
2196 {
2197 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002198 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002199 return(*beta);
2200 }
2201 case '|':
2202 {
2203 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002204 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002205 return(*beta);
2206 }
2207 case LogicalAndOperator:
2208 {
2209 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2210 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2211 return(*beta);
2212 }
2213 case LogicalOrOperator:
2214 {
2215 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2216 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2217 return(*beta);
2218 }
2219 case '?':
2220 {
2221 MagickRealType
2222 gamma;
2223
2224 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2225 q=subexpression;
2226 p=StringToken(":",&q);
2227 if (q == (char *) NULL)
2228 {
2229 (void) ThrowMagickException(exception,GetMagickModule(),
2230 OptionError,"UnableToParseExpression","`%s'",subexpression);
2231 return(0.0);
2232 }
2233 if (fabs((double) alpha) > MagickEpsilon)
2234 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2235 else
2236 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2237 return(gamma);
2238 }
2239 case '=':
2240 {
2241 char
2242 numeric[MaxTextExtent];
2243
2244 q=subexpression;
2245 while (isalpha((int) ((unsigned char) *q)) != 0)
2246 q++;
2247 if (*q != '\0')
2248 {
2249 (void) ThrowMagickException(exception,GetMagickModule(),
2250 OptionError,"UnableToParseExpression","`%s'",subexpression);
2251 return(0.0);
2252 }
2253 ClearMagickException(exception);
2254 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002255 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002256 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002257 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2258 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2259 subexpression),ConstantString(numeric));
2260 return(*beta);
2261 }
2262 case ',':
2263 {
2264 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2265 return(alpha);
2266 }
2267 case ';':
2268 {
2269 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2270 return(*beta);
2271 }
2272 default:
2273 {
2274 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2275 exception);
2276 return(gamma);
2277 }
2278 }
2279 }
2280 if (strchr("(",(int) *expression) != (char *) NULL)
2281 {
2282 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2283 subexpression[strlen(subexpression)-1]='\0';
2284 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2285 exception);
2286 return(gamma);
2287 }
cristy8b8a3ae2010-10-23 18:49:46 +00002288 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002289 {
2290 case '+':
2291 {
2292 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2293 exception);
2294 return(1.0*gamma);
2295 }
2296 case '-':
2297 {
2298 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2299 exception);
2300 return(-1.0*gamma);
2301 }
2302 case '~':
2303 {
2304 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2305 exception);
cristybb503372010-05-27 20:51:26 +00002306 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002307 }
2308 case 'A':
2309 case 'a':
2310 {
2311 if (LocaleNCompare(expression,"abs",3) == 0)
2312 {
2313 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2314 exception);
2315 return((MagickRealType) fabs((double) alpha));
2316 }
cristyb33454f2011-08-03 02:10:45 +00002317#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002318 if (LocaleNCompare(expression,"acosh",5) == 0)
2319 {
2320 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2321 exception);
2322 return((MagickRealType) acosh((double) alpha));
2323 }
cristyb33454f2011-08-03 02:10:45 +00002324#endif
cristy3ed852e2009-09-05 21:47:34 +00002325 if (LocaleNCompare(expression,"acos",4) == 0)
2326 {
2327 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2328 exception);
2329 return((MagickRealType) acos((double) alpha));
2330 }
cristy43c22f42010-03-30 12:34:07 +00002331#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002332 if (LocaleNCompare(expression,"airy",4) == 0)
2333 {
2334 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2335 exception);
2336 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002337 return(1.0);
2338 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002339 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002340 }
cristy43c22f42010-03-30 12:34:07 +00002341#endif
cristyb33454f2011-08-03 02:10:45 +00002342#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002343 if (LocaleNCompare(expression,"asinh",5) == 0)
2344 {
2345 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2346 exception);
2347 return((MagickRealType) asinh((double) alpha));
2348 }
cristyb33454f2011-08-03 02:10:45 +00002349#endif
cristy3ed852e2009-09-05 21:47:34 +00002350 if (LocaleNCompare(expression,"asin",4) == 0)
2351 {
2352 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2353 exception);
2354 return((MagickRealType) asin((double) alpha));
2355 }
2356 if (LocaleNCompare(expression,"alt",3) == 0)
2357 {
2358 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2359 exception);
cristybb503372010-05-27 20:51:26 +00002360 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002361 }
2362 if (LocaleNCompare(expression,"atan2",5) == 0)
2363 {
2364 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2365 exception);
2366 return((MagickRealType) atan2((double) alpha,(double) *beta));
2367 }
cristyb33454f2011-08-03 02:10:45 +00002368#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002369 if (LocaleNCompare(expression,"atanh",5) == 0)
2370 {
2371 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2372 exception);
2373 return((MagickRealType) atanh((double) alpha));
2374 }
cristyb33454f2011-08-03 02:10:45 +00002375#endif
cristy3ed852e2009-09-05 21:47:34 +00002376 if (LocaleNCompare(expression,"atan",4) == 0)
2377 {
2378 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2379 exception);
2380 return((MagickRealType) atan((double) alpha));
2381 }
2382 if (LocaleCompare(expression,"a") == 0)
2383 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2384 break;
2385 }
2386 case 'B':
2387 case 'b':
2388 {
2389 if (LocaleCompare(expression,"b") == 0)
2390 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2391 break;
2392 }
2393 case 'C':
2394 case 'c':
2395 {
2396 if (LocaleNCompare(expression,"ceil",4) == 0)
2397 {
2398 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2399 exception);
2400 return((MagickRealType) ceil((double) alpha));
2401 }
2402 if (LocaleNCompare(expression,"cosh",4) == 0)
2403 {
2404 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2405 exception);
2406 return((MagickRealType) cosh((double) alpha));
2407 }
2408 if (LocaleNCompare(expression,"cos",3) == 0)
2409 {
2410 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2411 exception);
2412 return((MagickRealType) cos((double) alpha));
2413 }
2414 if (LocaleCompare(expression,"c") == 0)
2415 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2416 break;
2417 }
2418 case 'D':
2419 case 'd':
2420 {
2421 if (LocaleNCompare(expression,"debug",5) == 0)
2422 {
2423 const char
2424 *type;
2425
2426 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2427 exception);
2428 if (fx_info->images->colorspace == CMYKColorspace)
2429 switch (channel)
2430 {
cristy0568ffc2011-07-25 16:54:14 +00002431 case CyanPixelChannel: type="cyan"; break;
2432 case MagentaPixelChannel: type="magenta"; break;
2433 case YellowPixelChannel: type="yellow"; break;
2434 case AlphaPixelChannel: type="opacity"; break;
2435 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002436 default: type="unknown"; break;
2437 }
2438 else
2439 switch (channel)
2440 {
cristy0568ffc2011-07-25 16:54:14 +00002441 case RedPixelChannel: type="red"; break;
2442 case GreenPixelChannel: type="green"; break;
2443 case BluePixelChannel: type="blue"; break;
2444 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002445 default: type="unknown"; break;
2446 }
2447 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2448 if (strlen(subexpression) > 1)
2449 subexpression[strlen(subexpression)-1]='\0';
2450 if (fx_info->file != (FILE *) NULL)
cristy1707c6c2012-01-18 23:30:54 +00002451 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2452 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2453 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002454 return(0.0);
2455 }
cristy5597a8d2011-11-04 00:25:32 +00002456 if (LocaleNCompare(expression,"drc",3) == 0)
2457 {
2458 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2459 exception);
2460 return((MagickRealType) (alpha/(*beta*(alpha-1.0)+1.0)));
2461 }
cristy3ed852e2009-09-05 21:47:34 +00002462 break;
2463 }
2464 case 'E':
2465 case 'e':
2466 {
2467 if (LocaleCompare(expression,"epsilon") == 0)
2468 return((MagickRealType) MagickEpsilon);
2469 if (LocaleNCompare(expression,"exp",3) == 0)
2470 {
2471 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2472 exception);
2473 return((MagickRealType) exp((double) alpha));
2474 }
2475 if (LocaleCompare(expression,"e") == 0)
2476 return((MagickRealType) 2.7182818284590452354);
2477 break;
2478 }
2479 case 'F':
2480 case 'f':
2481 {
2482 if (LocaleNCompare(expression,"floor",5) == 0)
2483 {
2484 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2485 exception);
2486 return((MagickRealType) floor((double) alpha));
2487 }
2488 break;
2489 }
2490 case 'G':
2491 case 'g':
2492 {
cristy9eeedea2011-11-02 19:04:05 +00002493 if (LocaleNCompare(expression,"gauss",5) == 0)
2494 {
2495 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2496 exception);
2497 gamma=exp((double) (-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2498 return((MagickRealType) gamma);
2499 }
cristyb0aad4c2011-11-02 19:30:35 +00002500 if (LocaleNCompare(expression,"gcd",3) == 0)
2501 {
2502 MagickOffsetType
2503 gcd;
2504
2505 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2506 exception);
cristy1707c6c2012-01-18 23:30:54 +00002507 gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2508 0.5));
cristyb0aad4c2011-11-02 19:30:35 +00002509 return((MagickRealType) gcd);
2510 }
cristy3ed852e2009-09-05 21:47:34 +00002511 if (LocaleCompare(expression,"g") == 0)
2512 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2513 break;
2514 }
2515 case 'H':
2516 case 'h':
2517 {
2518 if (LocaleCompare(expression,"h") == 0)
2519 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2520 if (LocaleCompare(expression,"hue") == 0)
2521 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2522 if (LocaleNCompare(expression,"hypot",5) == 0)
2523 {
2524 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2525 exception);
2526 return((MagickRealType) hypot((double) alpha,(double) *beta));
2527 }
2528 break;
2529 }
2530 case 'K':
2531 case 'k':
2532 {
2533 if (LocaleCompare(expression,"k") == 0)
2534 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2535 break;
2536 }
2537 case 'I':
2538 case 'i':
2539 {
2540 if (LocaleCompare(expression,"intensity") == 0)
2541 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2542 if (LocaleNCompare(expression,"int",3) == 0)
2543 {
2544 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2545 exception);
cristy16788e42010-08-13 13:44:26 +00002546 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002547 }
cristy82b20722011-11-05 21:52:36 +00002548#if defined(MAGICKCORE_HAVE_ISNAN)
cristy639399c2011-11-02 19:16:15 +00002549 if (LocaleNCompare(expression,"isnan",5) == 0)
2550 {
2551 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2552 exception);
cristy17a10202011-11-02 19:17:04 +00002553 return((MagickRealType) !!isnan((double) alpha));
cristy639399c2011-11-02 19:16:15 +00002554 }
cristy82b20722011-11-05 21:52:36 +00002555#endif
cristy3ed852e2009-09-05 21:47:34 +00002556 if (LocaleCompare(expression,"i") == 0)
2557 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2558 break;
2559 }
2560 case 'J':
2561 case 'j':
2562 {
2563 if (LocaleCompare(expression,"j") == 0)
2564 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002565#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002566 if (LocaleNCompare(expression,"j0",2) == 0)
2567 {
2568 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2569 exception);
2570 return((MagickRealType) j0((double) alpha));
2571 }
cristy161b9262010-03-20 19:34:32 +00002572#endif
2573#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002574 if (LocaleNCompare(expression,"j1",2) == 0)
2575 {
2576 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2577 exception);
2578 return((MagickRealType) j1((double) alpha));
2579 }
cristy161b9262010-03-20 19:34:32 +00002580#endif
cristyaa018fa2010-04-08 23:03:54 +00002581#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002582 if (LocaleNCompare(expression,"jinc",4) == 0)
2583 {
2584 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2585 exception);
cristy0946a822010-03-12 17:14:58 +00002586 if (alpha == 0.0)
2587 return(1.0);
cristy1707c6c2012-01-18 23:30:54 +00002588 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/(MagickPI*
2589 alpha));
cristyfce2f7b2010-03-12 00:29:49 +00002590 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002591 }
cristyaa018fa2010-04-08 23:03:54 +00002592#endif
cristy3ed852e2009-09-05 21:47:34 +00002593 break;
2594 }
2595 case 'L':
2596 case 'l':
2597 {
2598 if (LocaleNCompare(expression,"ln",2) == 0)
2599 {
2600 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2601 exception);
2602 return((MagickRealType) log((double) alpha));
2603 }
cristyc8ed5322010-08-31 12:07:59 +00002604 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002605 {
cristyc8ed5322010-08-31 12:07:59 +00002606 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002607 exception);
2608 return((MagickRealType) log10((double) alpha))/log10(2.0);
2609 }
2610 if (LocaleNCompare(expression,"log",3) == 0)
2611 {
2612 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2613 exception);
2614 return((MagickRealType) log10((double) alpha));
2615 }
2616 if (LocaleCompare(expression,"lightness") == 0)
2617 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2618 break;
2619 }
2620 case 'M':
2621 case 'm':
2622 {
2623 if (LocaleCompare(expression,"MaxRGB") == 0)
2624 return((MagickRealType) QuantumRange);
2625 if (LocaleNCompare(expression,"maxima",6) == 0)
2626 break;
2627 if (LocaleNCompare(expression,"max",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002628 {
2629 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2630 exception);
2631 return(alpha > *beta ? alpha : *beta);
2632 }
cristy3ed852e2009-09-05 21:47:34 +00002633 if (LocaleNCompare(expression,"minima",6) == 0)
2634 break;
2635 if (LocaleNCompare(expression,"min",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002636 {
2637 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2638 exception);
2639 return(alpha < *beta ? alpha : *beta);
2640 }
cristy3ed852e2009-09-05 21:47:34 +00002641 if (LocaleNCompare(expression,"mod",3) == 0)
2642 {
2643 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2644 exception);
cristy984049c2011-11-03 18:34:58 +00002645 gamma=alpha-floor((double) (alpha/(*beta)))*(*beta);
2646 return(gamma);
cristy3ed852e2009-09-05 21:47:34 +00002647 }
2648 if (LocaleCompare(expression,"m") == 0)
2649 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2650 break;
2651 }
2652 case 'N':
2653 case 'n':
2654 {
cristyad3502e2011-11-02 19:10:45 +00002655 if (LocaleNCompare(expression,"not",3) == 0)
2656 {
2657 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2658 exception);
2659 return((MagickRealType) (alpha < MagickEpsilon));
2660 }
cristy3ed852e2009-09-05 21:47:34 +00002661 if (LocaleCompare(expression,"n") == 0)
2662 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2663 break;
2664 }
2665 case 'O':
2666 case 'o':
2667 {
2668 if (LocaleCompare(expression,"Opaque") == 0)
2669 return(1.0);
2670 if (LocaleCompare(expression,"o") == 0)
2671 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2672 break;
2673 }
2674 case 'P':
2675 case 'p':
2676 {
cristy670aa3c2011-11-03 00:54:00 +00002677 if (LocaleCompare(expression,"phi") == 0)
2678 return((MagickRealType) MagickPHI);
cristy3ed852e2009-09-05 21:47:34 +00002679 if (LocaleCompare(expression,"pi") == 0)
2680 return((MagickRealType) MagickPI);
2681 if (LocaleNCompare(expression,"pow",3) == 0)
2682 {
2683 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2684 exception);
2685 return((MagickRealType) pow((double) alpha,(double) *beta));
2686 }
2687 if (LocaleCompare(expression,"p") == 0)
2688 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2689 break;
2690 }
2691 case 'Q':
2692 case 'q':
2693 {
2694 if (LocaleCompare(expression,"QuantumRange") == 0)
2695 return((MagickRealType) QuantumRange);
2696 if (LocaleCompare(expression,"QuantumScale") == 0)
2697 return((MagickRealType) QuantumScale);
2698 break;
2699 }
2700 case 'R':
2701 case 'r':
2702 {
2703 if (LocaleNCompare(expression,"rand",4) == 0)
2704 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2705 if (LocaleNCompare(expression,"round",5) == 0)
2706 {
2707 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2708 exception);
cristy16788e42010-08-13 13:44:26 +00002709 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002710 }
2711 if (LocaleCompare(expression,"r") == 0)
2712 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2713 break;
2714 }
2715 case 'S':
2716 case 's':
2717 {
2718 if (LocaleCompare(expression,"saturation") == 0)
2719 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2720 if (LocaleNCompare(expression,"sign",4) == 0)
2721 {
2722 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2723 exception);
2724 return(alpha < 0.0 ? -1.0 : 1.0);
2725 }
cristya6a09e72010-03-02 14:51:02 +00002726 if (LocaleNCompare(expression,"sinc",4) == 0)
2727 {
2728 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2729 exception);
2730 if (alpha == 0)
2731 return(1.0);
2732 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2733 (MagickPI*alpha));
2734 return(gamma);
2735 }
cristy3ed852e2009-09-05 21:47:34 +00002736 if (LocaleNCompare(expression,"sinh",4) == 0)
2737 {
2738 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2739 exception);
2740 return((MagickRealType) sinh((double) alpha));
2741 }
2742 if (LocaleNCompare(expression,"sin",3) == 0)
2743 {
2744 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2745 exception);
2746 return((MagickRealType) sin((double) alpha));
2747 }
2748 if (LocaleNCompare(expression,"sqrt",4) == 0)
2749 {
2750 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2751 exception);
2752 return((MagickRealType) sqrt((double) alpha));
2753 }
cristy9eeedea2011-11-02 19:04:05 +00002754 if (LocaleNCompare(expression,"squish",6) == 0)
2755 {
2756 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2757 exception);
2758 return((MagickRealType) (1.0/(1.0+exp((double) (4.0*alpha)))));
2759 }
cristy3ed852e2009-09-05 21:47:34 +00002760 if (LocaleCompare(expression,"s") == 0)
2761 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2762 break;
2763 }
2764 case 'T':
2765 case 't':
2766 {
2767 if (LocaleNCompare(expression,"tanh",4) == 0)
2768 {
2769 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2770 exception);
2771 return((MagickRealType) tanh((double) alpha));
2772 }
2773 if (LocaleNCompare(expression,"tan",3) == 0)
2774 {
2775 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2776 exception);
2777 return((MagickRealType) tan((double) alpha));
2778 }
2779 if (LocaleCompare(expression,"Transparent") == 0)
2780 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002781 if (LocaleNCompare(expression,"trunc",5) == 0)
2782 {
2783 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2784 exception);
2785 if (alpha >= 0.0)
2786 return((MagickRealType) floor((double) alpha));
2787 return((MagickRealType) ceil((double) alpha));
2788 }
cristy3ed852e2009-09-05 21:47:34 +00002789 if (LocaleCompare(expression,"t") == 0)
2790 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2791 break;
2792 }
2793 case 'U':
2794 case 'u':
2795 {
2796 if (LocaleCompare(expression,"u") == 0)
2797 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2798 break;
2799 }
2800 case 'V':
2801 case 'v':
2802 {
2803 if (LocaleCompare(expression,"v") == 0)
2804 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2805 break;
2806 }
2807 case 'W':
2808 case 'w':
2809 {
cristy9eeedea2011-11-02 19:04:05 +00002810 if (LocaleNCompare(expression,"while",5) == 0)
2811 {
2812 do
2813 {
2814 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2815 exception);
2816 } while (fabs((double) alpha) >= MagickEpsilon);
2817 return((MagickRealType) *beta);
2818 }
cristy3ed852e2009-09-05 21:47:34 +00002819 if (LocaleCompare(expression,"w") == 0)
2820 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2821 break;
2822 }
2823 case 'Y':
2824 case 'y':
2825 {
2826 if (LocaleCompare(expression,"y") == 0)
2827 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2828 break;
2829 }
2830 case 'Z':
2831 case 'z':
2832 {
2833 if (LocaleCompare(expression,"z") == 0)
2834 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2835 break;
2836 }
2837 default:
2838 break;
2839 }
2840 q=(char *) expression;
cristydbdd0e32011-11-04 23:29:40 +00002841 alpha=InterpretSiPrefixValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002842 if (q == expression)
2843 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2844 return(alpha);
2845}
2846
cristy7832dc22011-09-05 01:21:53 +00002847MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
cristy3ed852e2009-09-05 21:47:34 +00002848 MagickRealType *alpha,ExceptionInfo *exception)
2849{
2850 MagickBooleanType
2851 status;
2852
cristy541ae572011-08-05 19:08:59 +00002853 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2854 exception);
cristy3ed852e2009-09-05 21:47:34 +00002855 return(status);
2856}
2857
2858MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2859 MagickRealType *alpha,ExceptionInfo *exception)
2860{
2861 FILE
2862 *file;
2863
2864 MagickBooleanType
2865 status;
2866
2867 file=fx_info->file;
2868 fx_info->file=(FILE *) NULL;
cristy541ae572011-08-05 19:08:59 +00002869 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2870 exception);
cristy3ed852e2009-09-05 21:47:34 +00002871 fx_info->file=file;
2872 return(status);
2873}
2874
cristy7832dc22011-09-05 01:21:53 +00002875MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002876 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002877 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002878{
2879 MagickRealType
2880 beta;
2881
2882 beta=0.0;
2883 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2884 exception);
2885 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2886}
2887
2888/*
2889%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2890% %
2891% %
2892% %
2893% F x I m a g e %
2894% %
2895% %
2896% %
2897%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2898%
2899% FxImage() applies a mathematical expression to the specified image.
2900%
2901% The format of the FxImage method is:
2902%
2903% Image *FxImage(const Image *image,const char *expression,
2904% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002905%
2906% A description of each parameter follows:
2907%
2908% o image: the image.
2909%
cristy3ed852e2009-09-05 21:47:34 +00002910% o expression: A mathematical expression.
2911%
2912% o exception: return any errors or warnings in this structure.
2913%
2914*/
2915
2916static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2917{
cristybb503372010-05-27 20:51:26 +00002918 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002919 i;
2920
2921 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002922 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002923 if (fx_info[i] != (FxInfo *) NULL)
2924 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002925 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002926 return(fx_info);
2927}
2928
2929static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2930 ExceptionInfo *exception)
2931{
2932 char
2933 *fx_expression;
2934
2935 FxInfo
2936 **fx_info;
2937
2938 MagickRealType
2939 alpha;
2940
cristybb503372010-05-27 20:51:26 +00002941 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002942 i;
2943
cristybb503372010-05-27 20:51:26 +00002944 size_t
cristy3ed852e2009-09-05 21:47:34 +00002945 number_threads;
2946
2947 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002948 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002949 if (fx_info == (FxInfo **) NULL)
2950 return((FxInfo **) NULL);
2951 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2952 if (*expression != '@')
2953 fx_expression=ConstantString(expression);
2954 else
2955 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00002956 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002957 {
2958 fx_info[i]=AcquireFxInfo(image,fx_expression);
2959 if (fx_info[i] == (FxInfo *) NULL)
2960 return(DestroyFxThreadSet(fx_info));
2961 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2962 }
2963 fx_expression=DestroyString(fx_expression);
2964 return(fx_info);
2965}
2966
2967MagickExport Image *FxImage(const Image *image,const char *expression,
2968 ExceptionInfo *exception)
2969{
cristy3ed852e2009-09-05 21:47:34 +00002970#define FxImageTag "Fx/Image"
2971
cristyfa112112010-01-04 17:48:07 +00002972 CacheView
cristy79cedc72011-07-25 00:41:15 +00002973 *fx_view,
2974 *image_view;
cristyfa112112010-01-04 17:48:07 +00002975
cristy3ed852e2009-09-05 21:47:34 +00002976 FxInfo
cristyfa112112010-01-04 17:48:07 +00002977 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00002978
2979 Image
2980 *fx_image;
2981
cristy3ed852e2009-09-05 21:47:34 +00002982 MagickBooleanType
2983 status;
2984
cristybb503372010-05-27 20:51:26 +00002985 MagickOffsetType
2986 progress;
2987
cristy3ed852e2009-09-05 21:47:34 +00002988 MagickRealType
2989 alpha;
2990
cristybb503372010-05-27 20:51:26 +00002991 ssize_t
2992 y;
2993
cristy3ed852e2009-09-05 21:47:34 +00002994 assert(image != (Image *) NULL);
2995 assert(image->signature == MagickSignature);
2996 if (image->debug != MagickFalse)
2997 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00002998 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00002999 if (fx_image == (Image *) NULL)
3000 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003001 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003002 {
cristy3ed852e2009-09-05 21:47:34 +00003003 fx_image=DestroyImage(fx_image);
3004 return((Image *) NULL);
3005 }
3006 fx_info=AcquireFxThreadSet(image,expression,exception);
3007 if (fx_info == (FxInfo **) NULL)
3008 {
3009 fx_image=DestroyImage(fx_image);
3010 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3011 }
3012 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3013 if (status == MagickFalse)
3014 {
3015 fx_image=DestroyImage(fx_image);
3016 fx_info=DestroyFxThreadSet(fx_info);
3017 return((Image *) NULL);
3018 }
3019 /*
3020 Fx image.
3021 */
3022 status=MagickTrue;
3023 progress=0;
cristy79cedc72011-07-25 00:41:15 +00003024 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003025 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003026#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003027 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003028#endif
cristybb503372010-05-27 20:51:26 +00003029 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003030 {
cristy5c9e6f22010-09-17 17:31:01 +00003031 const int
3032 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003033
cristy79cedc72011-07-25 00:41:15 +00003034 register const Quantum
3035 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003036
cristy4c08aed2011-07-01 19:47:50 +00003037 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003038 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003039
cristy79cedc72011-07-25 00:41:15 +00003040 register ssize_t
3041 x;
3042
cristy3ed852e2009-09-05 21:47:34 +00003043 if (status == MagickFalse)
3044 continue;
cristy79cedc72011-07-25 00:41:15 +00003045 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003046 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003047 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003048 {
3049 status=MagickFalse;
3050 continue;
3051 }
cristybb503372010-05-27 20:51:26 +00003052 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003053 {
cristy79cedc72011-07-25 00:41:15 +00003054 register ssize_t
3055 i;
3056
3057 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3058 {
3059 MagickRealType
3060 alpha;
3061
3062 PixelChannel
3063 channel;
3064
3065 PixelTrait
3066 fx_traits,
3067 traits;
3068
cristye2a912b2011-12-05 20:02:07 +00003069 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003070 traits=GetPixelChannelMapTraits(image,channel);
cristy79cedc72011-07-25 00:41:15 +00003071 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
cristy010d7d12011-08-31 01:02:48 +00003072 if ((traits == UndefinedPixelTrait) ||
3073 (fx_traits == UndefinedPixelTrait))
cristy79cedc72011-07-25 00:41:15 +00003074 continue;
cristyd09f8802012-02-04 16:44:10 +00003075 if (((fx_traits & CopyPixelTrait) != 0) ||
3076 (GetPixelMask(image,p) != 0))
cristy79cedc72011-07-25 00:41:15 +00003077 {
cristy0beccfa2011-09-25 20:47:53 +00003078 SetPixelChannel(fx_image,channel,p[i],q);
cristy79cedc72011-07-25 00:41:15 +00003079 continue;
3080 }
3081 alpha=0.0;
cristya382aca2011-12-06 18:22:48 +00003082 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3083 exception);
cristyb3a73b52011-07-26 01:34:43 +00003084 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003085 }
3086 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003087 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003088 }
3089 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3090 status=MagickFalse;
3091 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3092 {
3093 MagickBooleanType
3094 proceed;
3095
cristyb5d5f722009-11-04 03:03:49 +00003096#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003097 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003098#endif
3099 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3100 if (proceed == MagickFalse)
3101 status=MagickFalse;
3102 }
3103 }
cristy3ed852e2009-09-05 21:47:34 +00003104 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003105 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003106 fx_info=DestroyFxThreadSet(fx_info);
3107 if (status == MagickFalse)
3108 fx_image=DestroyImage(fx_image);
3109 return(fx_image);
3110}
3111
3112/*
3113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3114% %
3115% %
3116% %
3117% I m p l o d e I m a g e %
3118% %
3119% %
3120% %
3121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3122%
3123% ImplodeImage() creates a new image that is a copy of an existing
3124% one with the image pixels "implode" by the specified percentage. It
3125% allocates the memory necessary for the new Image structure and returns a
3126% pointer to the new image.
3127%
3128% The format of the ImplodeImage method is:
3129%
3130% Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003131% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003132%
3133% A description of each parameter follows:
3134%
3135% o implode_image: Method ImplodeImage returns a pointer to the image
3136% after it is implode. A null image is returned if there is a memory
3137% shortage.
3138%
3139% o image: the image.
3140%
3141% o amount: Define the extent of the implosion.
3142%
cristy76f512e2011-09-12 01:26:56 +00003143% o method: the pixel interpolation method.
3144%
cristy3ed852e2009-09-05 21:47:34 +00003145% o exception: return any errors or warnings in this structure.
3146%
3147*/
3148MagickExport Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003149 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003150{
3151#define ImplodeImageTag "Implode/Image"
3152
cristyfa112112010-01-04 17:48:07 +00003153 CacheView
3154 *image_view,
3155 *implode_view;
3156
cristy3ed852e2009-09-05 21:47:34 +00003157 Image
3158 *implode_image;
3159
cristy3ed852e2009-09-05 21:47:34 +00003160 MagickBooleanType
3161 status;
3162
cristybb503372010-05-27 20:51:26 +00003163 MagickOffsetType
3164 progress;
3165
cristy3ed852e2009-09-05 21:47:34 +00003166 MagickRealType
3167 radius;
3168
3169 PointInfo
3170 center,
3171 scale;
3172
cristybb503372010-05-27 20:51:26 +00003173 ssize_t
3174 y;
3175
cristy3ed852e2009-09-05 21:47:34 +00003176 /*
3177 Initialize implode image attributes.
3178 */
3179 assert(image != (Image *) NULL);
3180 assert(image->signature == MagickSignature);
3181 if (image->debug != MagickFalse)
3182 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3183 assert(exception != (ExceptionInfo *) NULL);
3184 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00003185 implode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3186 exception);
cristy3ed852e2009-09-05 21:47:34 +00003187 if (implode_image == (Image *) NULL)
3188 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003189 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003190 {
cristy3ed852e2009-09-05 21:47:34 +00003191 implode_image=DestroyImage(implode_image);
3192 return((Image *) NULL);
3193 }
cristy4c08aed2011-07-01 19:47:50 +00003194 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003195 implode_image->matte=MagickTrue;
3196 /*
3197 Compute scaling factor.
3198 */
3199 scale.x=1.0;
3200 scale.y=1.0;
3201 center.x=0.5*image->columns;
3202 center.y=0.5*image->rows;
3203 radius=center.x;
3204 if (image->columns > image->rows)
3205 scale.y=(double) image->columns/(double) image->rows;
3206 else
3207 if (image->columns < image->rows)
3208 {
3209 scale.x=(double) image->rows/(double) image->columns;
3210 radius=center.y;
3211 }
3212 /*
3213 Implode image.
3214 */
3215 status=MagickTrue;
3216 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003217 image_view=AcquireCacheView(image);
3218 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003219#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003220 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003221#endif
cristybb503372010-05-27 20:51:26 +00003222 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003223 {
cristy3ed852e2009-09-05 21:47:34 +00003224 MagickRealType
3225 distance;
3226
3227 PointInfo
3228 delta;
3229
cristy6d188022011-09-12 13:23:33 +00003230 register const Quantum
3231 *restrict p;
3232
cristybb503372010-05-27 20:51:26 +00003233 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003234 x;
3235
cristy4c08aed2011-07-01 19:47:50 +00003236 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003237 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003238
3239 if (status == MagickFalse)
3240 continue;
cristy6d188022011-09-12 13:23:33 +00003241 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003242 q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003243 exception);
cristy6d188022011-09-12 13:23:33 +00003244 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003245 {
3246 status=MagickFalse;
3247 continue;
3248 }
cristy3ed852e2009-09-05 21:47:34 +00003249 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00003250 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003251 {
cristy6d188022011-09-12 13:23:33 +00003252 register ssize_t
3253 i;
3254
cristy3ed852e2009-09-05 21:47:34 +00003255 /*
3256 Determine if the pixel is within an ellipse.
3257 */
cristy10a6c612012-01-29 21:41:05 +00003258 if (GetPixelMask(image,p) != 0)
3259 {
3260 p+=GetPixelChannels(image);
3261 q+=GetPixelChannels(implode_image);
3262 continue;
3263 }
cristy3ed852e2009-09-05 21:47:34 +00003264 delta.x=scale.x*(double) (x-center.x);
3265 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00003266 if (distance >= (radius*radius))
cristya6d7a9b2012-01-18 20:04:48 +00003267 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy6d188022011-09-12 13:23:33 +00003268 {
cristya6d7a9b2012-01-18 20:04:48 +00003269 PixelChannel
3270 channel;
3271
cristy1707c6c2012-01-18 23:30:54 +00003272 PixelTrait
3273 implode_traits,
3274 traits;
3275
cristya6d7a9b2012-01-18 20:04:48 +00003276 channel=GetPixelChannelMapChannel(image,i);
cristy1707c6c2012-01-18 23:30:54 +00003277 traits=GetPixelChannelMapTraits(image,channel);
3278 implode_traits=GetPixelChannelMapTraits(implode_image,channel);
3279 if ((traits == UndefinedPixelTrait) ||
3280 (implode_traits == UndefinedPixelTrait))
3281 continue;
cristya6d7a9b2012-01-18 20:04:48 +00003282 SetPixelChannel(implode_image,channel,p[i],q);
cristy6d188022011-09-12 13:23:33 +00003283 }
3284 else
cristy3ed852e2009-09-05 21:47:34 +00003285 {
3286 double
3287 factor;
3288
3289 /*
3290 Implode the pixel.
3291 */
3292 factor=1.0;
3293 if (distance > 0.0)
cristy1707c6c2012-01-18 23:30:54 +00003294 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/radius/
3295 2)),-amount);
cristy76f512e2011-09-12 01:26:56 +00003296 status=InterpolatePixelChannels(image,image_view,implode_image,method,
3297 (double) (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3298 scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00003299 }
cristy6d188022011-09-12 13:23:33 +00003300 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003301 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003302 }
3303 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3304 status=MagickFalse;
3305 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3306 {
3307 MagickBooleanType
3308 proceed;
3309
cristyb5d5f722009-11-04 03:03:49 +00003310#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003311 #pragma omp critical (MagickCore_ImplodeImage)
cristy3ed852e2009-09-05 21:47:34 +00003312#endif
3313 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3314 if (proceed == MagickFalse)
3315 status=MagickFalse;
3316 }
3317 }
3318 implode_view=DestroyCacheView(implode_view);
3319 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003320 if (status == MagickFalse)
3321 implode_image=DestroyImage(implode_image);
3322 return(implode_image);
3323}
3324
3325/*
3326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3327% %
3328% %
3329% %
3330% M o r p h I m a g e s %
3331% %
3332% %
3333% %
3334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3335%
3336% The MorphImages() method requires a minimum of two images. The first
3337% image is transformed into the second by a number of intervening images
3338% as specified by frames.
3339%
3340% The format of the MorphImage method is:
3341%
cristybb503372010-05-27 20:51:26 +00003342% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003343% ExceptionInfo *exception)
3344%
3345% A description of each parameter follows:
3346%
3347% o image: the image.
3348%
3349% o number_frames: Define the number of in-between image to generate.
3350% The more in-between frames, the smoother the morph.
3351%
3352% o exception: return any errors or warnings in this structure.
3353%
3354*/
3355MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003356 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003357{
3358#define MorphImageTag "Morph/Image"
3359
3360 Image
3361 *morph_image,
3362 *morph_images;
3363
cristy9d314ff2011-03-09 01:30:28 +00003364 MagickBooleanType
3365 status;
cristy3ed852e2009-09-05 21:47:34 +00003366
3367 MagickOffsetType
3368 scene;
3369
3370 MagickRealType
3371 alpha,
3372 beta;
3373
3374 register const Image
3375 *next;
3376
cristybb503372010-05-27 20:51:26 +00003377 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003378 i;
3379
cristy9d314ff2011-03-09 01:30:28 +00003380 ssize_t
3381 y;
cristy3ed852e2009-09-05 21:47:34 +00003382
3383 /*
3384 Clone first frame in sequence.
3385 */
3386 assert(image != (Image *) NULL);
3387 assert(image->signature == MagickSignature);
3388 if (image->debug != MagickFalse)
3389 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3390 assert(exception != (ExceptionInfo *) NULL);
3391 assert(exception->signature == MagickSignature);
3392 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3393 if (morph_images == (Image *) NULL)
3394 return((Image *) NULL);
3395 if (GetNextImageInList(image) == (Image *) NULL)
3396 {
3397 /*
3398 Morph single image.
3399 */
cristybb503372010-05-27 20:51:26 +00003400 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003401 {
3402 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3403 if (morph_image == (Image *) NULL)
3404 {
3405 morph_images=DestroyImageList(morph_images);
3406 return((Image *) NULL);
3407 }
3408 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003409 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003410 {
cristy8b27a6d2010-02-14 03:31:15 +00003411 MagickBooleanType
3412 proceed;
3413
cristybb503372010-05-27 20:51:26 +00003414 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3415 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003416 if (proceed == MagickFalse)
3417 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003418 }
3419 }
3420 return(GetFirstImageInList(morph_images));
3421 }
3422 /*
3423 Morph image sequence.
3424 */
3425 status=MagickTrue;
3426 scene=0;
3427 next=image;
3428 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3429 {
cristybb503372010-05-27 20:51:26 +00003430 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003431 {
3432 CacheView
3433 *image_view,
3434 *morph_view;
3435
3436 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3437 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003438 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristyaa2c16c2012-03-25 22:21:35 +00003439 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*next->rows+beta*
3440 GetNextImageInList(next)->rows+0.5),next->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00003441 if (morph_image == (Image *) NULL)
3442 {
3443 morph_images=DestroyImageList(morph_images);
3444 return((Image *) NULL);
3445 }
cristy1707c6c2012-01-18 23:30:54 +00003446 status=SetImageStorageClass(morph_image,DirectClass,exception);
3447 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003448 {
cristy3ed852e2009-09-05 21:47:34 +00003449 morph_image=DestroyImage(morph_image);
3450 return((Image *) NULL);
3451 }
3452 AppendImageToList(&morph_images,morph_image);
3453 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003454 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
cristyaa2c16c2012-03-25 22:21:35 +00003455 morph_images->rows,GetNextImageInList(next)->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00003456 if (morph_image == (Image *) NULL)
3457 {
3458 morph_images=DestroyImageList(morph_images);
3459 return((Image *) NULL);
3460 }
3461 image_view=AcquireCacheView(morph_image);
3462 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003463#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003464 #pragma omp parallel for schedule(static,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003465#endif
cristybb503372010-05-27 20:51:26 +00003466 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003467 {
3468 MagickBooleanType
3469 sync;
3470
cristy4c08aed2011-07-01 19:47:50 +00003471 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003472 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003473
cristybb503372010-05-27 20:51:26 +00003474 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003475 x;
3476
cristy4c08aed2011-07-01 19:47:50 +00003477 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003478 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003479
3480 if (status == MagickFalse)
3481 continue;
3482 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3483 exception);
3484 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3485 exception);
cristy4c08aed2011-07-01 19:47:50 +00003486 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003487 {
3488 status=MagickFalse;
3489 continue;
3490 }
cristybb503372010-05-27 20:51:26 +00003491 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003492 {
cristy1707c6c2012-01-18 23:30:54 +00003493 register ssize_t
3494 i;
3495
cristy10a6c612012-01-29 21:41:05 +00003496 for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
cristy1707c6c2012-01-18 23:30:54 +00003497 {
3498 PixelChannel
3499 channel;
3500
3501 PixelTrait
3502 morph_traits,
3503 traits;
3504
3505 channel=GetPixelChannelMapChannel(image,i);
3506 traits=GetPixelChannelMapTraits(image,channel);
3507 morph_traits=GetPixelChannelMapTraits(morph_image,channel);
3508 if ((traits == UndefinedPixelTrait) ||
3509 (morph_traits == UndefinedPixelTrait))
3510 continue;
cristyd09f8802012-02-04 16:44:10 +00003511 if (((morph_traits & CopyPixelTrait) != 0) ||
3512 (GetPixelMask(image,p) != 0))
cristy1707c6c2012-01-18 23:30:54 +00003513 {
3514 SetPixelChannel(morph_image,channel,p[i],q);
3515 continue;
3516 }
3517 SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3518 GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3519 }
cristyed231572011-07-14 02:18:59 +00003520 p+=GetPixelChannels(morph_image);
3521 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003522 }
3523 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3524 if (sync == MagickFalse)
3525 status=MagickFalse;
3526 }
3527 morph_view=DestroyCacheView(morph_view);
3528 image_view=DestroyCacheView(image_view);
3529 morph_image=DestroyImage(morph_image);
3530 }
cristybb503372010-05-27 20:51:26 +00003531 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003532 break;
3533 /*
3534 Clone last frame in sequence.
3535 */
3536 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3537 if (morph_image == (Image *) NULL)
3538 {
3539 morph_images=DestroyImageList(morph_images);
3540 return((Image *) NULL);
3541 }
3542 AppendImageToList(&morph_images,morph_image);
3543 morph_images=GetLastImageInList(morph_images);
3544 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3545 {
3546 MagickBooleanType
3547 proceed;
3548
cristyb5d5f722009-11-04 03:03:49 +00003549#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003550 #pragma omp critical (MagickCore_MorphImages)
cristy3ed852e2009-09-05 21:47:34 +00003551#endif
3552 proceed=SetImageProgress(image,MorphImageTag,scene,
3553 GetImageListLength(image));
3554 if (proceed == MagickFalse)
3555 status=MagickFalse;
3556 }
3557 scene++;
3558 }
3559 if (GetNextImageInList(next) != (Image *) NULL)
3560 {
3561 morph_images=DestroyImageList(morph_images);
3562 return((Image *) NULL);
3563 }
3564 return(GetFirstImageInList(morph_images));
3565}
3566
3567/*
3568%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3569% %
3570% %
3571% %
3572% P l a s m a I m a g e %
3573% %
3574% %
3575% %
3576%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3577%
3578% PlasmaImage() initializes an image with plasma fractal values. The image
3579% must be initialized with a base color and the random number generator
3580% seeded before this method is called.
3581%
3582% The format of the PlasmaImage method is:
3583%
3584% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristy5cbc0162011-08-29 00:36:28 +00003585% size_t attenuate,size_t depth,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003586%
3587% A description of each parameter follows:
3588%
3589% o image: the image.
3590%
3591% o segment: Define the region to apply plasma fractals values.
3592%
glennrp7dae1ca2010-09-16 12:17:35 +00003593% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003594%
3595% o depth: Limit the plasma recursion depth.
3596%
cristy5cbc0162011-08-29 00:36:28 +00003597% o exception: return any errors or warnings in this structure.
3598%
cristy3ed852e2009-09-05 21:47:34 +00003599*/
3600
3601static inline Quantum PlasmaPixel(RandomInfo *random_info,
3602 const MagickRealType pixel,const MagickRealType noise)
3603{
3604 Quantum
3605 plasma;
3606
cristyce70c172010-01-07 17:15:30 +00003607 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003608 noise/2.0);
3609 return(plasma);
3610}
3611
cristyda1f9c12011-10-02 21:39:49 +00003612static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
3613 CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3614 const SegmentInfo *segment,size_t attenuate,size_t depth,
3615 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003616{
cristy3ed852e2009-09-05 21:47:34 +00003617 MagickRealType
3618 plasma;
3619
cristyda1f9c12011-10-02 21:39:49 +00003620 PixelChannel
3621 channel;
3622
3623 PixelTrait
3624 traits;
3625
3626 register const Quantum
3627 *restrict u,
3628 *restrict v;
3629
3630 register Quantum
3631 *restrict q;
3632
3633 register ssize_t
3634 i;
cristy3ed852e2009-09-05 21:47:34 +00003635
cristy9d314ff2011-03-09 01:30:28 +00003636 ssize_t
3637 x,
3638 x_mid,
3639 y,
3640 y_mid;
3641
cristy3ed852e2009-09-05 21:47:34 +00003642 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3643 return(MagickTrue);
3644 if (depth != 0)
3645 {
3646 SegmentInfo
3647 local_info;
3648
3649 /*
3650 Divide the area into quadrants and recurse.
3651 */
3652 depth--;
3653 attenuate++;
cristybb503372010-05-27 20:51:26 +00003654 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3655 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003656 local_info=(*segment);
3657 local_info.x2=(double) x_mid;
3658 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003659 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3660 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003661 local_info=(*segment);
3662 local_info.y1=(double) y_mid;
3663 local_info.x2=(double) x_mid;
cristyda1f9c12011-10-02 21:39:49 +00003664 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3665 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003666 local_info=(*segment);
3667 local_info.x1=(double) x_mid;
3668 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003669 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3670 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003671 local_info=(*segment);
3672 local_info.x1=(double) x_mid;
3673 local_info.y1=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003674 return(PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3675 &local_info,attenuate,depth,exception));
cristy3ed852e2009-09-05 21:47:34 +00003676 }
cristybb503372010-05-27 20:51:26 +00003677 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3678 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003679 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3680 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3681 return(MagickFalse);
3682 /*
3683 Average pixels and apply plasma.
3684 */
cristy3ed852e2009-09-05 21:47:34 +00003685 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3686 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3687 {
cristy3ed852e2009-09-05 21:47:34 +00003688 /*
3689 Left pixel.
3690 */
cristybb503372010-05-27 20:51:26 +00003691 x=(ssize_t) ceil(segment->x1-0.5);
cristy1707c6c2012-01-18 23:30:54 +00003692 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3693 exception);
3694 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3695 exception);
cristyc5c6f662010-09-22 14:23:02 +00003696 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003697 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3698 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003699 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003700 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3701 {
cristye2a912b2011-12-05 20:02:07 +00003702 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003703 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003704 if (traits == UndefinedPixelTrait)
3705 continue;
3706 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3707 }
cristyc5c6f662010-09-22 14:23:02 +00003708 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003709 if (segment->x1 != segment->x2)
3710 {
3711 /*
3712 Right pixel.
3713 */
cristybb503372010-05-27 20:51:26 +00003714 x=(ssize_t) ceil(segment->x2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003715 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3716 1,1,exception);
3717 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3718 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003719 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003720 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3721 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003722 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003723 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3724 {
cristye2a912b2011-12-05 20:02:07 +00003725 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003726 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003727 if (traits == UndefinedPixelTrait)
3728 continue;
3729 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3730 }
cristyc5c6f662010-09-22 14:23:02 +00003731 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003732 }
3733 }
3734 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3735 {
3736 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3737 {
cristy3ed852e2009-09-05 21:47:34 +00003738 /*
3739 Bottom pixel.
3740 */
cristybb503372010-05-27 20:51:26 +00003741 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003742 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3743 1,1,exception);
3744 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3745 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003746 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003747 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3748 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003749 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003750 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3751 {
cristye2a912b2011-12-05 20:02:07 +00003752 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003753 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003754 if (traits == UndefinedPixelTrait)
3755 continue;
3756 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3757 }
cristyc5c6f662010-09-22 14:23:02 +00003758 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003759 }
3760 if (segment->y1 != segment->y2)
3761 {
cristy3ed852e2009-09-05 21:47:34 +00003762 /*
3763 Top pixel.
3764 */
cristybb503372010-05-27 20:51:26 +00003765 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003766 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3767 1,1,exception);
3768 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3769 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003770 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003771 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3772 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003773 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003774 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3775 {
cristye2a912b2011-12-05 20:02:07 +00003776 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003777 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003778 if (traits == UndefinedPixelTrait)
3779 continue;
3780 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3781 }
cristyc5c6f662010-09-22 14:23:02 +00003782 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003783 }
3784 }
3785 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3786 {
cristy3ed852e2009-09-05 21:47:34 +00003787 /*
3788 Middle pixel.
3789 */
cristybb503372010-05-27 20:51:26 +00003790 x=(ssize_t) ceil(segment->x1-0.5);
3791 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003792 u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
cristybb503372010-05-27 20:51:26 +00003793 x=(ssize_t) ceil(segment->x2-0.5);
3794 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003795 v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003796 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003797 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3798 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003799 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003800 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3801 {
cristye2a912b2011-12-05 20:02:07 +00003802 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003803 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003804 if (traits == UndefinedPixelTrait)
3805 continue;
3806 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3807 }
cristyc5c6f662010-09-22 14:23:02 +00003808 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003809 }
3810 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3811 return(MagickTrue);
3812 return(MagickFalse);
3813}
cristyda1f9c12011-10-02 21:39:49 +00003814
cristy3ed852e2009-09-05 21:47:34 +00003815MagickExport MagickBooleanType PlasmaImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00003816 const SegmentInfo *segment,size_t attenuate,size_t depth,
3817 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003818{
cristyc5c6f662010-09-22 14:23:02 +00003819 CacheView
cristyda1f9c12011-10-02 21:39:49 +00003820 *image_view,
3821 *u_view,
3822 *v_view;
cristyc5c6f662010-09-22 14:23:02 +00003823
cristy3ed852e2009-09-05 21:47:34 +00003824 MagickBooleanType
3825 status;
3826
3827 RandomInfo
3828 *random_info;
3829
3830 if (image->debug != MagickFalse)
3831 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3832 assert(image != (Image *) NULL);
3833 assert(image->signature == MagickSignature);
3834 if (image->debug != MagickFalse)
3835 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy5cbc0162011-08-29 00:36:28 +00003836 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristyc5c6f662010-09-22 14:23:02 +00003837 return(MagickFalse);
3838 image_view=AcquireCacheView(image);
cristyda1f9c12011-10-02 21:39:49 +00003839 u_view=AcquireCacheView(image);
3840 v_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003841 random_info=AcquireRandomInfo();
cristyda1f9c12011-10-02 21:39:49 +00003842 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
3843 attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003844 random_info=DestroyRandomInfo(random_info);
cristyda1f9c12011-10-02 21:39:49 +00003845 v_view=DestroyCacheView(v_view);
3846 u_view=DestroyCacheView(u_view);
cristyc5c6f662010-09-22 14:23:02 +00003847 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003848 return(status);
3849}
3850
3851/*
3852%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3853% %
3854% %
3855% %
3856% P o l a r o i d I m a g e %
3857% %
3858% %
3859% %
3860%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3861%
3862% PolaroidImage() simulates a Polaroid picture.
3863%
3864% The format of the AnnotateImage method is:
3865%
3866% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003867% const char *caption,const double angle,
3868% const PixelInterpolateMethod method,ExceptionInfo exception)
cristy3ed852e2009-09-05 21:47:34 +00003869%
3870% A description of each parameter follows:
3871%
3872% o image: the image.
3873%
3874% o draw_info: the draw info.
3875%
cristye9e3d382011-12-14 01:50:13 +00003876% o caption: the Polaroid caption.
3877%
cristycee97112010-05-28 00:44:52 +00003878% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003879%
cristy5c4e2582011-09-11 19:21:03 +00003880% o method: the pixel interpolation method.
3881%
cristy3ed852e2009-09-05 21:47:34 +00003882% o exception: return any errors or warnings in this structure.
3883%
3884*/
3885MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003886 const char *caption,const double angle,const PixelInterpolateMethod method,
cristy5c4e2582011-09-11 19:21:03 +00003887 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003888{
cristy3ed852e2009-09-05 21:47:34 +00003889 Image
3890 *bend_image,
3891 *caption_image,
3892 *flop_image,
3893 *picture_image,
3894 *polaroid_image,
3895 *rotate_image,
3896 *trim_image;
3897
cristybb503372010-05-27 20:51:26 +00003898 size_t
cristy3ed852e2009-09-05 21:47:34 +00003899 height;
3900
cristy9d314ff2011-03-09 01:30:28 +00003901 ssize_t
3902 quantum;
3903
cristy3ed852e2009-09-05 21:47:34 +00003904 /*
3905 Simulate a Polaroid picture.
3906 */
3907 assert(image != (Image *) NULL);
3908 assert(image->signature == MagickSignature);
3909 if (image->debug != MagickFalse)
3910 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3911 assert(exception != (ExceptionInfo *) NULL);
3912 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003913 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003914 image->rows)/25.0,10.0);
3915 height=image->rows+2*quantum;
3916 caption_image=(Image *) NULL;
cristye9e3d382011-12-14 01:50:13 +00003917 if (caption != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003918 {
3919 char
cristye9e3d382011-12-14 01:50:13 +00003920 geometry[MaxTextExtent],
3921 *text;
cristy3ed852e2009-09-05 21:47:34 +00003922
3923 DrawInfo
3924 *annotate_info;
3925
cristy3ed852e2009-09-05 21:47:34 +00003926 MagickBooleanType
3927 status;
3928
cristy9d314ff2011-03-09 01:30:28 +00003929 ssize_t
3930 count;
3931
cristy3ed852e2009-09-05 21:47:34 +00003932 TypeMetric
3933 metrics;
3934
3935 /*
3936 Generate caption image.
3937 */
3938 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3939 if (caption_image == (Image *) NULL)
3940 return((Image *) NULL);
3941 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
cristye9e3d382011-12-14 01:50:13 +00003942 text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
3943 exception);
3944 (void) CloneString(&annotate_info->text,text);
cristy6b1d05e2010-09-22 19:17:27 +00003945 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
cristye9e3d382011-12-14 01:50:13 +00003946 &text,exception);
3947 status=SetImageExtent(caption_image,image->columns,(size_t) ((count+1)*
3948 (metrics.ascent-metrics.descent)+0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00003949 if (status == MagickFalse)
3950 caption_image=DestroyImage(caption_image);
3951 else
3952 {
3953 caption_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00003954 (void) SetImageBackgroundColor(caption_image,exception);
cristye9e3d382011-12-14 01:50:13 +00003955 (void) CloneString(&annotate_info->text,text);
cristyb51dff52011-05-19 16:55:47 +00003956 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00003957 metrics.ascent);
3958 if (annotate_info->gravity == UndefinedGravity)
3959 (void) CloneString(&annotate_info->geometry,AcquireString(
3960 geometry));
cristy5cbc0162011-08-29 00:36:28 +00003961 (void) AnnotateImage(caption_image,annotate_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003962 height+=caption_image->rows;
3963 }
3964 annotate_info=DestroyDrawInfo(annotate_info);
cristye9e3d382011-12-14 01:50:13 +00003965 text=DestroyString(text);
cristy3ed852e2009-09-05 21:47:34 +00003966 }
3967 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3968 exception);
3969 if (picture_image == (Image *) NULL)
3970 {
3971 if (caption_image != (Image *) NULL)
3972 caption_image=DestroyImage(caption_image);
3973 return((Image *) NULL);
3974 }
3975 picture_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00003976 (void) SetImageBackgroundColor(picture_image,exception);
cristy39172402012-03-30 13:04:39 +00003977 (void) CompositeImage(picture_image,image,OverCompositeOp,MagickTrue,quantum,
cristyfeb3e962012-03-29 17:25:55 +00003978 quantum,exception);
cristy3ed852e2009-09-05 21:47:34 +00003979 if (caption_image != (Image *) NULL)
3980 {
cristyfeb3e962012-03-29 17:25:55 +00003981 (void) CompositeImage(picture_image,caption_image,OverCompositeOp,
cristy39172402012-03-30 13:04:39 +00003982 MagickTrue,quantum,(ssize_t) (image->rows+3*quantum/2),exception);
cristy3ed852e2009-09-05 21:47:34 +00003983 caption_image=DestroyImage(caption_image);
3984 }
cristy9950d572011-10-01 18:22:35 +00003985 (void) QueryColorCompliance("none",AllCompliance,
3986 &picture_image->background_color,exception);
cristy63240882011-08-05 19:05:27 +00003987 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00003988 rotate_image=RotateImage(picture_image,90.0,exception);
3989 picture_image=DestroyImage(picture_image);
3990 if (rotate_image == (Image *) NULL)
3991 return((Image *) NULL);
3992 picture_image=rotate_image;
3993 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
cristy5c4e2582011-09-11 19:21:03 +00003994 picture_image->columns,method,exception);
cristy3ed852e2009-09-05 21:47:34 +00003995 picture_image=DestroyImage(picture_image);
3996 if (bend_image == (Image *) NULL)
3997 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003998 picture_image=bend_image;
3999 rotate_image=RotateImage(picture_image,-90.0,exception);
4000 picture_image=DestroyImage(picture_image);
4001 if (rotate_image == (Image *) NULL)
4002 return((Image *) NULL);
4003 picture_image=rotate_image;
4004 picture_image->background_color=image->background_color;
anthonyf46d4262012-03-26 03:30:34 +00004005 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
cristy3ed852e2009-09-05 21:47:34 +00004006 exception);
4007 if (polaroid_image == (Image *) NULL)
4008 {
4009 picture_image=DestroyImage(picture_image);
4010 return(picture_image);
4011 }
4012 flop_image=FlopImage(polaroid_image,exception);
4013 polaroid_image=DestroyImage(polaroid_image);
4014 if (flop_image == (Image *) NULL)
4015 {
4016 picture_image=DestroyImage(picture_image);
4017 return(picture_image);
4018 }
4019 polaroid_image=flop_image;
cristyfeb3e962012-03-29 17:25:55 +00004020 (void) CompositeImage(polaroid_image,picture_image,OverCompositeOp,
cristy39172402012-03-30 13:04:39 +00004021 MagickTrue,(ssize_t) (-0.01*picture_image->columns/2.0),0L,exception);
cristy3ed852e2009-09-05 21:47:34 +00004022 picture_image=DestroyImage(picture_image);
cristy9950d572011-10-01 18:22:35 +00004023 (void) QueryColorCompliance("none",AllCompliance,
4024 &polaroid_image->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00004025 rotate_image=RotateImage(polaroid_image,angle,exception);
4026 polaroid_image=DestroyImage(polaroid_image);
4027 if (rotate_image == (Image *) NULL)
4028 return((Image *) NULL);
4029 polaroid_image=rotate_image;
4030 trim_image=TrimImage(polaroid_image,exception);
4031 polaroid_image=DestroyImage(polaroid_image);
4032 if (trim_image == (Image *) NULL)
4033 return((Image *) NULL);
4034 polaroid_image=trim_image;
4035 return(polaroid_image);
4036}
4037
4038/*
4039%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4040% %
4041% %
4042% %
cristy3ed852e2009-09-05 21:47:34 +00004043% S e p i a T o n e I m a g e %
4044% %
4045% %
4046% %
4047%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4048%
4049% MagickSepiaToneImage() applies a special effect to the image, similar to the
4050% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4051% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4052% threshold of 80% is a good starting point for a reasonable tone.
4053%
4054% The format of the SepiaToneImage method is:
4055%
4056% Image *SepiaToneImage(const Image *image,const double threshold,
4057% ExceptionInfo *exception)
4058%
4059% A description of each parameter follows:
4060%
4061% o image: the image.
4062%
4063% o threshold: the tone threshold.
4064%
4065% o exception: return any errors or warnings in this structure.
4066%
4067*/
4068MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4069 ExceptionInfo *exception)
4070{
4071#define SepiaToneImageTag "SepiaTone/Image"
4072
cristyc4c8d132010-01-07 01:58:38 +00004073 CacheView
4074 *image_view,
4075 *sepia_view;
4076
cristy3ed852e2009-09-05 21:47:34 +00004077 Image
4078 *sepia_image;
4079
cristy3ed852e2009-09-05 21:47:34 +00004080 MagickBooleanType
4081 status;
4082
cristybb503372010-05-27 20:51:26 +00004083 MagickOffsetType
4084 progress;
4085
4086 ssize_t
4087 y;
4088
cristy3ed852e2009-09-05 21:47:34 +00004089 /*
4090 Initialize sepia-toned image attributes.
4091 */
4092 assert(image != (const Image *) NULL);
4093 assert(image->signature == MagickSignature);
4094 if (image->debug != MagickFalse)
4095 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4096 assert(exception != (ExceptionInfo *) NULL);
4097 assert(exception->signature == MagickSignature);
cristy1707c6c2012-01-18 23:30:54 +00004098 sepia_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004099 if (sepia_image == (Image *) NULL)
4100 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004101 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004102 {
cristy3ed852e2009-09-05 21:47:34 +00004103 sepia_image=DestroyImage(sepia_image);
4104 return((Image *) NULL);
4105 }
4106 /*
4107 Tone each row of the image.
4108 */
4109 status=MagickTrue;
4110 progress=0;
4111 image_view=AcquireCacheView(image);
4112 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004113#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004114 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004115#endif
cristybb503372010-05-27 20:51:26 +00004116 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004117 {
cristy4c08aed2011-07-01 19:47:50 +00004118 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004119 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004120
cristybb503372010-05-27 20:51:26 +00004121 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004122 x;
4123
cristy4c08aed2011-07-01 19:47:50 +00004124 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004125 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004126
4127 if (status == MagickFalse)
4128 continue;
4129 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00004130 q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004131 exception);
cristy4c08aed2011-07-01 19:47:50 +00004132 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004133 {
4134 status=MagickFalse;
4135 continue;
4136 }
cristybb503372010-05-27 20:51:26 +00004137 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004138 {
4139 MagickRealType
4140 intensity,
4141 tone;
4142
cristy4c08aed2011-07-01 19:47:50 +00004143 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004144 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4145 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004146 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004147 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4148 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004149 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004150 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004151 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004152 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004153 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4154 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4155 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4156 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004157 p+=GetPixelChannels(image);
4158 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004159 }
4160 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4161 status=MagickFalse;
4162 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4163 {
4164 MagickBooleanType
4165 proceed;
4166
cristyb5d5f722009-11-04 03:03:49 +00004167#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004168 #pragma omp critical (MagickCore_SepiaToneImage)
cristy3ed852e2009-09-05 21:47:34 +00004169#endif
4170 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4171 image->rows);
4172 if (proceed == MagickFalse)
4173 status=MagickFalse;
4174 }
4175 }
4176 sepia_view=DestroyCacheView(sepia_view);
4177 image_view=DestroyCacheView(image_view);
cristye23ec9d2011-08-16 18:15:40 +00004178 (void) NormalizeImage(sepia_image,exception);
4179 (void) ContrastImage(sepia_image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004180 if (status == MagickFalse)
4181 sepia_image=DestroyImage(sepia_image);
4182 return(sepia_image);
4183}
4184
4185/*
4186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4187% %
4188% %
4189% %
4190% S h a d o w I m a g e %
4191% %
4192% %
4193% %
4194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4195%
4196% ShadowImage() simulates a shadow from the specified image and returns it.
4197%
4198% The format of the ShadowImage method is:
4199%
cristy70cddf72011-12-10 22:42:42 +00004200% Image *ShadowImage(const Image *image,const double alpha,
cristyaa2c16c2012-03-25 22:21:35 +00004201% const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4202% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004203%
4204% A description of each parameter follows:
4205%
4206% o image: the image.
4207%
cristy70cddf72011-12-10 22:42:42 +00004208% o alpha: percentage transparency.
cristy3ed852e2009-09-05 21:47:34 +00004209%
4210% o sigma: the standard deviation of the Gaussian, in pixels.
4211%
4212% o x_offset: the shadow x-offset.
4213%
4214% o y_offset: the shadow y-offset.
4215%
4216% o exception: return any errors or warnings in this structure.
4217%
4218*/
cristy70cddf72011-12-10 22:42:42 +00004219MagickExport Image *ShadowImage(const Image *image,const double alpha,
cristyaa2c16c2012-03-25 22:21:35 +00004220 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4221 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004222{
4223#define ShadowImageTag "Shadow/Image"
4224
cristy70cddf72011-12-10 22:42:42 +00004225 CacheView
4226 *image_view;
4227
cristybd5a96c2011-08-21 00:04:26 +00004228 ChannelType
4229 channel_mask;
4230
cristy3ed852e2009-09-05 21:47:34 +00004231 Image
4232 *border_image,
4233 *clone_image,
4234 *shadow_image;
4235
cristy70cddf72011-12-10 22:42:42 +00004236 MagickBooleanType
4237 status;
4238
cristy3ed852e2009-09-05 21:47:34 +00004239 RectangleInfo
4240 border_info;
4241
cristy70cddf72011-12-10 22:42:42 +00004242 ssize_t
4243 y;
4244
cristy3ed852e2009-09-05 21:47:34 +00004245 assert(image != (Image *) NULL);
4246 assert(image->signature == MagickSignature);
4247 if (image->debug != MagickFalse)
4248 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4249 assert(exception != (ExceptionInfo *) NULL);
4250 assert(exception->signature == MagickSignature);
4251 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4252 if (clone_image == (Image *) NULL)
4253 return((Image *) NULL);
cristy387430f2012-02-07 13:09:46 +00004254 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod,
4255 exception);
cristybb503372010-05-27 20:51:26 +00004256 border_info.width=(size_t) floor(2.0*sigma+0.5);
4257 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004258 border_info.x=0;
4259 border_info.y=0;
cristy9950d572011-10-01 18:22:35 +00004260 (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4261 exception);
cristy70cddf72011-12-10 22:42:42 +00004262 clone_image->matte=MagickTrue;
4263 border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
cristy3ed852e2009-09-05 21:47:34 +00004264 clone_image=DestroyImage(clone_image);
4265 if (border_image == (Image *) NULL)
4266 return((Image *) NULL);
4267 if (border_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004268 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004269 /*
4270 Shadow image.
4271 */
cristy70cddf72011-12-10 22:42:42 +00004272 status=MagickTrue;
4273 image_view=AcquireCacheView(border_image);
4274 for (y=0; y < (ssize_t) border_image->rows; y++)
4275 {
4276 PixelInfo
4277 background_color;
4278
4279 register Quantum
4280 *restrict q;
4281
4282 register ssize_t
4283 x;
4284
4285 if (status == MagickFalse)
4286 continue;
4287 q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4288 exception);
4289 if (q == (Quantum *) NULL)
4290 {
4291 status=MagickFalse;
4292 continue;
4293 }
4294 background_color=border_image->background_color;
4295 background_color.matte=MagickTrue;
4296 for (x=0; x < (ssize_t) border_image->columns; x++)
4297 {
4298 if (border_image->matte != MagickFalse)
4299 background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4300 SetPixelInfoPixel(border_image,&background_color,q);
4301 q+=GetPixelChannels(border_image);
4302 }
4303 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4304 status=MagickFalse;
4305 }
4306 image_view=DestroyCacheView(image_view);
4307 if (status == MagickFalse)
4308 {
4309 border_image=DestroyImage(border_image);
4310 return((Image *) NULL);
4311 }
cristybd5a96c2011-08-21 00:04:26 +00004312 channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
cristyaa2c16c2012-03-25 22:21:35 +00004313 shadow_image=BlurImage(border_image,0.0,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00004314 border_image=DestroyImage(border_image);
4315 if (shadow_image == (Image *) NULL)
4316 return((Image *) NULL);
cristyae1969f2011-12-10 03:07:36 +00004317 (void) SetPixelChannelMapMask(shadow_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004318 if (shadow_image->page.width == 0)
4319 shadow_image->page.width=shadow_image->columns;
4320 if (shadow_image->page.height == 0)
4321 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004322 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4323 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4324 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4325 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004326 return(shadow_image);
4327}
4328
4329/*
4330%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4331% %
4332% %
4333% %
4334% S k e t c h I m a g e %
4335% %
4336% %
4337% %
4338%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4339%
4340% SketchImage() simulates a pencil sketch. We convolve the image with a
4341% Gaussian operator of the given radius and standard deviation (sigma). For
4342% reasonable results, radius should be larger than sigma. Use a radius of 0
4343% and SketchImage() selects a suitable radius for you. Angle gives the angle
4344% of the sketch.
4345%
4346% The format of the SketchImage method is:
4347%
4348% Image *SketchImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00004349% const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004350%
4351% A description of each parameter follows:
4352%
4353% o image: the image.
4354%
cristy574cc262011-08-05 01:23:58 +00004355% o radius: the radius of the Gaussian, in pixels, not counting the
4356% center pixel.
cristy3ed852e2009-09-05 21:47:34 +00004357%
4358% o sigma: the standard deviation of the Gaussian, in pixels.
4359%
cristyf7ef0252011-09-09 14:50:06 +00004360% o angle: apply the effect along this angle.
4361%
cristy3ed852e2009-09-05 21:47:34 +00004362% o exception: return any errors or warnings in this structure.
4363%
4364*/
4365MagickExport Image *SketchImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00004366 const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004367{
cristyfa112112010-01-04 17:48:07 +00004368 CacheView
4369 *random_view;
4370
cristy3ed852e2009-09-05 21:47:34 +00004371 Image
4372 *blend_image,
4373 *blur_image,
4374 *dodge_image,
4375 *random_image,
4376 *sketch_image;
4377
cristy3ed852e2009-09-05 21:47:34 +00004378 MagickBooleanType
4379 status;
4380
cristy3ed852e2009-09-05 21:47:34 +00004381 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004382 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004383
cristy9d314ff2011-03-09 01:30:28 +00004384 ssize_t
4385 y;
4386
cristy3ed852e2009-09-05 21:47:34 +00004387 /*
4388 Sketch image.
4389 */
4390 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4391 MagickTrue,exception);
4392 if (random_image == (Image *) NULL)
4393 return((Image *) NULL);
4394 status=MagickTrue;
cristy1b784432009-12-19 02:20:40 +00004395 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004396 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004397#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004398 #pragma omp parallel for schedule(static,4) shared(status)
cristy1b784432009-12-19 02:20:40 +00004399#endif
cristybb503372010-05-27 20:51:26 +00004400 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004401 {
cristy5c9e6f22010-09-17 17:31:01 +00004402 const int
4403 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004404
cristybb503372010-05-27 20:51:26 +00004405 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004406 x;
4407
cristy4c08aed2011-07-01 19:47:50 +00004408 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004409 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004410
cristy1b784432009-12-19 02:20:40 +00004411 if (status == MagickFalse)
4412 continue;
cristy3ed852e2009-09-05 21:47:34 +00004413 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4414 exception);
cristyacd2ed22011-08-30 01:44:23 +00004415 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004416 {
4417 status=MagickFalse;
4418 continue;
4419 }
cristybb503372010-05-27 20:51:26 +00004420 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004421 {
cristy76f512e2011-09-12 01:26:56 +00004422 MagickRealType
4423 value;
4424
4425 register ssize_t
4426 i;
4427
cristy10a6c612012-01-29 21:41:05 +00004428 if (GetPixelMask(random_image,q) != 0)
4429 {
4430 q+=GetPixelChannels(random_image);
4431 continue;
4432 }
cristy76f512e2011-09-12 01:26:56 +00004433 value=GetPseudoRandomValue(random_info[id]);
4434 for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4435 {
cristyabace412011-12-11 15:56:53 +00004436 PixelChannel
4437 channel;
4438
cristy76f512e2011-09-12 01:26:56 +00004439 PixelTrait
4440 traits;
4441
cristyabace412011-12-11 15:56:53 +00004442 channel=GetPixelChannelMapChannel(image,i);
4443 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004444 if (traits == UndefinedPixelTrait)
4445 continue;
4446 q[i]=ClampToQuantum(QuantumRange*value);
4447 }
cristyed231572011-07-14 02:18:59 +00004448 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004449 }
4450 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4451 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004452 }
4453 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004454 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004455 if (status == MagickFalse)
4456 {
4457 random_image=DestroyImage(random_image);
4458 return(random_image);
4459 }
cristyaa2c16c2012-03-25 22:21:35 +00004460 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
cristy3ed852e2009-09-05 21:47:34 +00004461 random_image=DestroyImage(random_image);
4462 if (blur_image == (Image *) NULL)
4463 return((Image *) NULL);
cristy6bfd6902011-12-09 01:33:45 +00004464 dodge_image=EdgeImage(blur_image,radius,1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004465 blur_image=DestroyImage(blur_image);
4466 if (dodge_image == (Image *) NULL)
4467 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +00004468 (void) NormalizeImage(dodge_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00004469 (void) NegateImage(dodge_image,MagickFalse,exception);
cristye941a752011-10-15 01:52:48 +00004470 (void) TransformImage(&dodge_image,(char *) NULL,"50%",exception);
cristy3ed852e2009-09-05 21:47:34 +00004471 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4472 if (sketch_image == (Image *) NULL)
4473 {
4474 dodge_image=DestroyImage(dodge_image);
4475 return((Image *) NULL);
4476 }
cristyfeb3e962012-03-29 17:25:55 +00004477 (void) CompositeImage(sketch_image,dodge_image,ColorDodgeCompositeOp,
cristy39172402012-03-30 13:04:39 +00004478 MagickTrue,0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004479 dodge_image=DestroyImage(dodge_image);
4480 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4481 if (blend_image == (Image *) NULL)
4482 {
4483 sketch_image=DestroyImage(sketch_image);
4484 return((Image *) NULL);
4485 }
4486 (void) SetImageArtifact(blend_image,"compose:args","20x80");
cristy39172402012-03-30 13:04:39 +00004487 (void) CompositeImage(sketch_image,blend_image,BlendCompositeOp,MagickTrue,
cristyfeb3e962012-03-29 17:25:55 +00004488 0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004489 blend_image=DestroyImage(blend_image);
4490 return(sketch_image);
4491}
4492
4493/*
4494%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4495% %
4496% %
4497% %
4498% S o l a r i z e I m a g e %
4499% %
4500% %
4501% %
4502%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4503%
4504% SolarizeImage() applies a special effect to the image, similar to the effect
4505% achieved in a photo darkroom by selectively exposing areas of photo
4506% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4507% measure of the extent of the solarization.
4508%
4509% The format of the SolarizeImage method is:
4510%
cristy5cbc0162011-08-29 00:36:28 +00004511% MagickBooleanType SolarizeImage(Image *image,const double threshold,
4512% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004513%
4514% A description of each parameter follows:
4515%
4516% o image: the image.
4517%
4518% o threshold: Define the extent of the solarization.
4519%
cristy5cbc0162011-08-29 00:36:28 +00004520% o exception: return any errors or warnings in this structure.
4521%
cristy3ed852e2009-09-05 21:47:34 +00004522*/
4523MagickExport MagickBooleanType SolarizeImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00004524 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004525{
4526#define SolarizeImageTag "Solarize/Image"
4527
cristyc4c8d132010-01-07 01:58:38 +00004528 CacheView
4529 *image_view;
4530
cristy3ed852e2009-09-05 21:47:34 +00004531 MagickBooleanType
4532 status;
4533
cristybb503372010-05-27 20:51:26 +00004534 MagickOffsetType
4535 progress;
4536
4537 ssize_t
4538 y;
4539
cristy3ed852e2009-09-05 21:47:34 +00004540 assert(image != (Image *) NULL);
4541 assert(image->signature == MagickSignature);
4542 if (image->debug != MagickFalse)
4543 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4544 if (image->storage_class == PseudoClass)
4545 {
cristybb503372010-05-27 20:51:26 +00004546 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004547 i;
4548
4549 /*
4550 Solarize colormap.
4551 */
cristybb503372010-05-27 20:51:26 +00004552 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004553 {
4554 if ((MagickRealType) image->colormap[i].red > threshold)
4555 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4556 if ((MagickRealType) image->colormap[i].green > threshold)
4557 image->colormap[i].green=(Quantum) QuantumRange-
4558 image->colormap[i].green;
4559 if ((MagickRealType) image->colormap[i].blue > threshold)
4560 image->colormap[i].blue=(Quantum) QuantumRange-
4561 image->colormap[i].blue;
4562 }
4563 }
4564 /*
4565 Solarize image.
4566 */
4567 status=MagickTrue;
4568 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00004569 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004570#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004571 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004572#endif
cristybb503372010-05-27 20:51:26 +00004573 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004574 {
cristybb503372010-05-27 20:51:26 +00004575 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004576 x;
4577
cristy4c08aed2011-07-01 19:47:50 +00004578 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004579 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004580
4581 if (status == MagickFalse)
4582 continue;
cristy5cbc0162011-08-29 00:36:28 +00004583 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00004584 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004585 {
4586 status=MagickFalse;
4587 continue;
4588 }
cristybb503372010-05-27 20:51:26 +00004589 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004590 {
cristy76f512e2011-09-12 01:26:56 +00004591 register ssize_t
4592 i;
4593
cristy10a6c612012-01-29 21:41:05 +00004594 if (GetPixelMask(image,q) != 0)
4595 {
4596 q+=GetPixelChannels(image);
4597 continue;
4598 }
cristy76f512e2011-09-12 01:26:56 +00004599 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4600 {
cristyabace412011-12-11 15:56:53 +00004601 PixelChannel
4602 channel;
4603
cristy76f512e2011-09-12 01:26:56 +00004604 PixelTrait
4605 traits;
4606
cristyabace412011-12-11 15:56:53 +00004607 channel=GetPixelChannelMapChannel(image,i);
4608 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004609 if ((traits == UndefinedPixelTrait) ||
4610 ((traits & CopyPixelTrait) != 0))
4611 continue;
4612 if ((MagickRealType) q[i] > threshold)
4613 q[i]=QuantumRange-q[i];
4614 }
cristyed231572011-07-14 02:18:59 +00004615 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004616 }
4617 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4618 status=MagickFalse;
4619 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4620 {
4621 MagickBooleanType
4622 proceed;
4623
cristyb5d5f722009-11-04 03:03:49 +00004624#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004625 #pragma omp critical (MagickCore_SolarizeImage)
cristy3ed852e2009-09-05 21:47:34 +00004626#endif
4627 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4628 if (proceed == MagickFalse)
4629 status=MagickFalse;
4630 }
4631 }
4632 image_view=DestroyCacheView(image_view);
4633 return(status);
4634}
4635
4636/*
4637%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4638% %
4639% %
4640% %
4641% S t e g a n o I m a g e %
4642% %
4643% %
4644% %
4645%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4646%
4647% SteganoImage() hides a digital watermark within the image. Recover
4648% the hidden watermark later to prove that the authenticity of an image.
4649% Offset defines the start position within the image to hide the watermark.
4650%
4651% The format of the SteganoImage method is:
4652%
4653% Image *SteganoImage(const Image *image,Image *watermark,
4654% ExceptionInfo *exception)
4655%
4656% A description of each parameter follows:
4657%
4658% o image: the image.
4659%
4660% o watermark: the watermark image.
4661%
4662% o exception: return any errors or warnings in this structure.
4663%
4664*/
4665MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4666 ExceptionInfo *exception)
4667{
cristye1bf8ad2010-09-19 17:07:03 +00004668#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004669#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004670 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004671#define SteganoImageTag "Stegano/Image"
4672
cristyb0d3bb92010-09-22 14:37:58 +00004673 CacheView
4674 *stegano_view,
4675 *watermark_view;
4676
cristy3ed852e2009-09-05 21:47:34 +00004677 Image
4678 *stegano_image;
4679
4680 int
4681 c;
4682
cristy3ed852e2009-09-05 21:47:34 +00004683 MagickBooleanType
4684 status;
4685
cristy101ab702011-10-13 13:06:32 +00004686 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004687 pixel;
4688
cristy4c08aed2011-07-01 19:47:50 +00004689 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004690 *q;
4691
cristye1bf8ad2010-09-19 17:07:03 +00004692 register ssize_t
4693 x;
4694
cristybb503372010-05-27 20:51:26 +00004695 size_t
cristyeaedf062010-05-29 22:36:02 +00004696 depth,
4697 one;
cristy3ed852e2009-09-05 21:47:34 +00004698
cristye1bf8ad2010-09-19 17:07:03 +00004699 ssize_t
4700 i,
4701 j,
4702 k,
4703 y;
4704
cristy3ed852e2009-09-05 21:47:34 +00004705 /*
4706 Initialize steganographic image attributes.
4707 */
4708 assert(image != (const Image *) NULL);
4709 assert(image->signature == MagickSignature);
4710 if (image->debug != MagickFalse)
4711 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4712 assert(watermark != (const Image *) NULL);
4713 assert(watermark->signature == MagickSignature);
4714 assert(exception != (ExceptionInfo *) NULL);
4715 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004716 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004717 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4718 if (stegano_image == (Image *) NULL)
4719 return((Image *) NULL);
cristyf61b1832012-04-01 01:38:19 +00004720 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
cristy574cc262011-08-05 01:23:58 +00004721 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004722 {
cristy3ed852e2009-09-05 21:47:34 +00004723 stegano_image=DestroyImage(stegano_image);
4724 return((Image *) NULL);
4725 }
cristy3ed852e2009-09-05 21:47:34 +00004726 /*
4727 Hide watermark in low-order bits of image.
4728 */
4729 c=0;
4730 i=0;
4731 j=0;
4732 depth=stegano_image->depth;
cristyf61b1832012-04-01 01:38:19 +00004733 k=stegano_image->offset;
cristyda16f162011-02-19 23:52:17 +00004734 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004735 watermark_view=AcquireCacheView(watermark);
4736 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004737 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004738 {
cristybb503372010-05-27 20:51:26 +00004739 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004740 {
cristybb503372010-05-27 20:51:26 +00004741 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004742 {
cristy1707c6c2012-01-18 23:30:54 +00004743 ssize_t
4744 offset;
4745
cristyf05d4942012-03-17 16:26:09 +00004746 (void) GetOneCacheViewVirtualPixelInfo(watermark_view,x,y,&pixel,
cristyda1f9c12011-10-02 21:39:49 +00004747 exception);
cristy1707c6c2012-01-18 23:30:54 +00004748 offset=k/(ssize_t) stegano_image->columns;
4749 if (offset >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004750 break;
cristyb0d3bb92010-09-22 14:37:58 +00004751 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4752 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4753 exception);
cristyacd2ed22011-08-30 01:44:23 +00004754 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004755 break;
4756 switch (c)
4757 {
4758 case 0:
4759 {
cristyf61b1832012-04-01 01:38:19 +00004760 SetPixelRed(stegano_image,SetBit(GetPixelRed(stegano_image,q),j,
4761 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004762 break;
4763 }
4764 case 1:
4765 {
cristyf61b1832012-04-01 01:38:19 +00004766 SetPixelGreen(stegano_image,SetBit(GetPixelGreen(stegano_image,q),j,
4767 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004768 break;
4769 }
4770 case 2:
4771 {
cristyf61b1832012-04-01 01:38:19 +00004772 SetPixelBlue(stegano_image,SetBit(GetPixelBlue(stegano_image,q),j,
4773 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004774 break;
4775 }
4776 }
cristyb0d3bb92010-09-22 14:37:58 +00004777 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004778 break;
4779 c++;
4780 if (c == 3)
4781 c=0;
4782 k++;
cristybb503372010-05-27 20:51:26 +00004783 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004784 k=0;
cristyf61b1832012-04-01 01:38:19 +00004785 if (k == stegano_image->offset)
cristy3ed852e2009-09-05 21:47:34 +00004786 j++;
4787 }
4788 }
cristy8b27a6d2010-02-14 03:31:15 +00004789 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004790 {
cristy8b27a6d2010-02-14 03:31:15 +00004791 MagickBooleanType
4792 proceed;
4793
4794 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4795 (depth-i),depth);
4796 if (proceed == MagickFalse)
4797 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004798 }
4799 }
cristyb0d3bb92010-09-22 14:37:58 +00004800 stegano_view=DestroyCacheView(stegano_view);
4801 watermark_view=DestroyCacheView(watermark_view);
cristyda16f162011-02-19 23:52:17 +00004802 if (status == MagickFalse)
4803 {
4804 stegano_image=DestroyImage(stegano_image);
4805 return((Image *) NULL);
4806 }
cristy3ed852e2009-09-05 21:47:34 +00004807 return(stegano_image);
4808}
4809
4810/*
4811%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4812% %
4813% %
4814% %
4815% S t e r e o A n a g l y p h I m a g e %
4816% %
4817% %
4818% %
4819%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4820%
4821% StereoAnaglyphImage() combines two images and produces a single image that
4822% is the composite of a left and right image of a stereo pair. Special
4823% red-green stereo glasses are required to view this effect.
4824%
4825% The format of the StereoAnaglyphImage method is:
4826%
4827% Image *StereoImage(const Image *left_image,const Image *right_image,
4828% ExceptionInfo *exception)
4829% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004830% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004831% ExceptionInfo *exception)
4832%
4833% A description of each parameter follows:
4834%
4835% o left_image: the left image.
4836%
4837% o right_image: the right image.
4838%
4839% o exception: return any errors or warnings in this structure.
4840%
4841% o x_offset: amount, in pixels, by which the left image is offset to the
4842% right of the right image.
4843%
4844% o y_offset: amount, in pixels, by which the left image is offset to the
4845% bottom of the right image.
4846%
4847%
4848*/
4849MagickExport Image *StereoImage(const Image *left_image,
4850 const Image *right_image,ExceptionInfo *exception)
4851{
4852 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4853}
4854
4855MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004856 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004857 ExceptionInfo *exception)
4858{
4859#define StereoImageTag "Stereo/Image"
4860
4861 const Image
4862 *image;
4863
4864 Image
4865 *stereo_image;
4866
cristy3ed852e2009-09-05 21:47:34 +00004867 MagickBooleanType
4868 status;
4869
cristy9d314ff2011-03-09 01:30:28 +00004870 ssize_t
4871 y;
4872
cristy3ed852e2009-09-05 21:47:34 +00004873 assert(left_image != (const Image *) NULL);
4874 assert(left_image->signature == MagickSignature);
4875 if (left_image->debug != MagickFalse)
4876 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4877 left_image->filename);
4878 assert(right_image != (const Image *) NULL);
4879 assert(right_image->signature == MagickSignature);
4880 assert(exception != (ExceptionInfo *) NULL);
4881 assert(exception->signature == MagickSignature);
4882 assert(right_image != (const Image *) NULL);
4883 image=left_image;
4884 if ((left_image->columns != right_image->columns) ||
4885 (left_image->rows != right_image->rows))
4886 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4887 /*
4888 Initialize stereo image attributes.
4889 */
4890 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4891 MagickTrue,exception);
4892 if (stereo_image == (Image *) NULL)
4893 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004894 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004895 {
cristy3ed852e2009-09-05 21:47:34 +00004896 stereo_image=DestroyImage(stereo_image);
4897 return((Image *) NULL);
4898 }
4899 /*
4900 Copy left image to red channel and right image to blue channel.
4901 */
cristyda16f162011-02-19 23:52:17 +00004902 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004903 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004904 {
cristy4c08aed2011-07-01 19:47:50 +00004905 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004906 *restrict p,
4907 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004908
cristybb503372010-05-27 20:51:26 +00004909 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004910 x;
4911
cristy4c08aed2011-07-01 19:47:50 +00004912 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004913 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004914
4915 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4916 exception);
4917 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4918 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy76f512e2011-09-12 01:26:56 +00004919 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
4920 (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004921 break;
cristybb503372010-05-27 20:51:26 +00004922 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004923 {
cristy4c08aed2011-07-01 19:47:50 +00004924 SetPixelRed(image,GetPixelRed(left_image,p),r);
cristy76f512e2011-09-12 01:26:56 +00004925 SetPixelGreen(image,GetPixelGreen(right_image,q),r);
4926 SetPixelBlue(image,GetPixelBlue(right_image,q),r);
4927 if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
4928 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4929 GetPixelAlpha(right_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00004930 p+=GetPixelChannels(left_image);
cristy76f512e2011-09-12 01:26:56 +00004931 q+=GetPixelChannels(right_image);
4932 r+=GetPixelChannels(stereo_image);
cristy3ed852e2009-09-05 21:47:34 +00004933 }
4934 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4935 break;
cristy8b27a6d2010-02-14 03:31:15 +00004936 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004937 {
cristy8b27a6d2010-02-14 03:31:15 +00004938 MagickBooleanType
4939 proceed;
4940
cristybb503372010-05-27 20:51:26 +00004941 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4942 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004943 if (proceed == MagickFalse)
4944 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004945 }
4946 }
cristyda16f162011-02-19 23:52:17 +00004947 if (status == MagickFalse)
4948 {
4949 stereo_image=DestroyImage(stereo_image);
4950 return((Image *) NULL);
4951 }
cristy3ed852e2009-09-05 21:47:34 +00004952 return(stereo_image);
4953}
4954
4955/*
4956%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4957% %
4958% %
4959% %
4960% S w i r l I m a g e %
4961% %
4962% %
4963% %
4964%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4965%
4966% SwirlImage() swirls the pixels about the center of the image, where
4967% degrees indicates the sweep of the arc through which each pixel is moved.
4968% You get a more dramatic effect as the degrees move from 1 to 360.
4969%
4970% The format of the SwirlImage method is:
4971%
4972% Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00004973% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004974%
4975% A description of each parameter follows:
4976%
4977% o image: the image.
4978%
4979% o degrees: Define the tightness of the swirling effect.
4980%
cristy76f512e2011-09-12 01:26:56 +00004981% o method: the pixel interpolation method.
4982%
cristy3ed852e2009-09-05 21:47:34 +00004983% o exception: return any errors or warnings in this structure.
4984%
4985*/
4986MagickExport Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00004987 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004988{
4989#define SwirlImageTag "Swirl/Image"
4990
cristyfa112112010-01-04 17:48:07 +00004991 CacheView
4992 *image_view,
4993 *swirl_view;
4994
cristy3ed852e2009-09-05 21:47:34 +00004995 Image
4996 *swirl_image;
4997
cristy3ed852e2009-09-05 21:47:34 +00004998 MagickBooleanType
4999 status;
5000
cristybb503372010-05-27 20:51:26 +00005001 MagickOffsetType
5002 progress;
5003
cristy3ed852e2009-09-05 21:47:34 +00005004 MagickRealType
5005 radius;
5006
5007 PointInfo
5008 center,
5009 scale;
5010
cristybb503372010-05-27 20:51:26 +00005011 ssize_t
5012 y;
5013
cristy3ed852e2009-09-05 21:47:34 +00005014 /*
5015 Initialize swirl image attributes.
5016 */
5017 assert(image != (const Image *) NULL);
5018 assert(image->signature == MagickSignature);
5019 if (image->debug != MagickFalse)
5020 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5021 assert(exception != (ExceptionInfo *) NULL);
5022 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00005023 swirl_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005024 if (swirl_image == (Image *) NULL)
5025 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005026 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005027 {
cristy3ed852e2009-09-05 21:47:34 +00005028 swirl_image=DestroyImage(swirl_image);
5029 return((Image *) NULL);
5030 }
cristy4c08aed2011-07-01 19:47:50 +00005031 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005032 swirl_image->matte=MagickTrue;
5033 /*
5034 Compute scaling factor.
5035 */
5036 center.x=(double) image->columns/2.0;
5037 center.y=(double) image->rows/2.0;
5038 radius=MagickMax(center.x,center.y);
5039 scale.x=1.0;
5040 scale.y=1.0;
5041 if (image->columns > image->rows)
5042 scale.y=(double) image->columns/(double) image->rows;
5043 else
5044 if (image->columns < image->rows)
5045 scale.x=(double) image->rows/(double) image->columns;
5046 degrees=(double) DegreesToRadians(degrees);
5047 /*
5048 Swirl image.
5049 */
5050 status=MagickTrue;
5051 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00005052 image_view=AcquireCacheView(image);
5053 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005054#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005055 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005056#endif
cristybb503372010-05-27 20:51:26 +00005057 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005058 {
cristy3ed852e2009-09-05 21:47:34 +00005059 MagickRealType
5060 distance;
5061
5062 PointInfo
5063 delta;
5064
cristy6d188022011-09-12 13:23:33 +00005065 register const Quantum
5066 *restrict p;
5067
cristybb503372010-05-27 20:51:26 +00005068 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005069 x;
5070
cristy4c08aed2011-07-01 19:47:50 +00005071 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005072 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005073
5074 if (status == MagickFalse)
5075 continue;
cristy6d188022011-09-12 13:23:33 +00005076 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00005077 q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00005078 exception);
cristy6d188022011-09-12 13:23:33 +00005079 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005080 {
5081 status=MagickFalse;
5082 continue;
5083 }
cristy3ed852e2009-09-05 21:47:34 +00005084 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00005085 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005086 {
5087 /*
5088 Determine if the pixel is within an ellipse.
5089 */
cristy10a6c612012-01-29 21:41:05 +00005090 if (GetPixelMask(image,p) != 0)
5091 {
5092 p+=GetPixelChannels(image);
5093 q+=GetPixelChannels(swirl_image);
5094 continue;
5095 }
cristy3ed852e2009-09-05 21:47:34 +00005096 delta.x=scale.x*(double) (x-center.x);
5097 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00005098 if (distance >= (radius*radius))
5099 {
cristy1707c6c2012-01-18 23:30:54 +00005100 register ssize_t
5101 i;
5102
cristy6d188022011-09-12 13:23:33 +00005103 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy1707c6c2012-01-18 23:30:54 +00005104 {
5105 PixelChannel
5106 channel;
5107
5108 PixelTrait
5109 swirl_traits,
5110 traits;
5111
5112 channel=GetPixelChannelMapChannel(image,i);
5113 traits=GetPixelChannelMapTraits(image,channel);
5114 swirl_traits=GetPixelChannelMapTraits(swirl_image,channel);
5115 if ((traits == UndefinedPixelTrait) ||
5116 (swirl_traits == UndefinedPixelTrait))
5117 continue;
5118 SetPixelChannel(swirl_image,channel,p[i],q);
5119 }
cristy6d188022011-09-12 13:23:33 +00005120 }
5121 else
cristy3ed852e2009-09-05 21:47:34 +00005122 {
5123 MagickRealType
5124 cosine,
5125 factor,
5126 sine;
5127
5128 /*
5129 Swirl the pixel.
5130 */
5131 factor=1.0-sqrt((double) distance)/radius;
5132 sine=sin((double) (degrees*factor*factor));
5133 cosine=cos((double) (degrees*factor*factor));
cristy76f512e2011-09-12 01:26:56 +00005134 status=InterpolatePixelChannels(image,image_view,swirl_image,method,
5135 ((cosine*delta.x-sine*delta.y)/scale.x+center.x),(double)
5136 ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00005137 }
cristy6d188022011-09-12 13:23:33 +00005138 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00005139 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00005140 }
5141 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5142 status=MagickFalse;
5143 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5144 {
5145 MagickBooleanType
5146 proceed;
5147
cristyb5d5f722009-11-04 03:03:49 +00005148#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005149 #pragma omp critical (MagickCore_SwirlImage)
cristy3ed852e2009-09-05 21:47:34 +00005150#endif
5151 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5152 if (proceed == MagickFalse)
5153 status=MagickFalse;
5154 }
5155 }
5156 swirl_view=DestroyCacheView(swirl_view);
5157 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005158 if (status == MagickFalse)
5159 swirl_image=DestroyImage(swirl_image);
5160 return(swirl_image);
5161}
5162
5163/*
5164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5165% %
5166% %
5167% %
5168% T i n t I m a g e %
5169% %
5170% %
5171% %
5172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5173%
5174% TintImage() applies a color vector to each pixel in the image. The length
5175% of the vector is 0 for black and white and at its maximum for the midtones.
5176% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5177%
5178% The format of the TintImage method is:
5179%
cristyb817c3f2011-10-03 14:00:35 +00005180% Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005181% const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005182%
5183% A description of each parameter follows:
5184%
5185% o image: the image.
5186%
cristyb817c3f2011-10-03 14:00:35 +00005187% o blend: A color value used for tinting.
cristy3ed852e2009-09-05 21:47:34 +00005188%
5189% o tint: A color value used for tinting.
5190%
5191% o exception: return any errors or warnings in this structure.
5192%
5193*/
cristyb817c3f2011-10-03 14:00:35 +00005194MagickExport Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005195 const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005196{
5197#define TintImageTag "Tint/Image"
5198
cristyc4c8d132010-01-07 01:58:38 +00005199 CacheView
5200 *image_view,
5201 *tint_view;
5202
cristy3ed852e2009-09-05 21:47:34 +00005203 GeometryInfo
5204 geometry_info;
5205
5206 Image
5207 *tint_image;
5208
cristy3ed852e2009-09-05 21:47:34 +00005209 MagickBooleanType
5210 status;
5211
cristybb503372010-05-27 20:51:26 +00005212 MagickOffsetType
5213 progress;
cristy3ed852e2009-09-05 21:47:34 +00005214
cristy28474bf2011-09-11 23:32:52 +00005215 MagickRealType
5216 intensity;
5217
cristy4c08aed2011-07-01 19:47:50 +00005218 PixelInfo
cristy1707c6c2012-01-18 23:30:54 +00005219 color_vector;
cristy3ed852e2009-09-05 21:47:34 +00005220
cristybb503372010-05-27 20:51:26 +00005221 MagickStatusType
5222 flags;
5223
5224 ssize_t
5225 y;
5226
cristy3ed852e2009-09-05 21:47:34 +00005227 /*
5228 Allocate tint image.
5229 */
5230 assert(image != (const Image *) NULL);
5231 assert(image->signature == MagickSignature);
5232 if (image->debug != MagickFalse)
5233 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5234 assert(exception != (ExceptionInfo *) NULL);
5235 assert(exception->signature == MagickSignature);
5236 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5237 if (tint_image == (Image *) NULL)
5238 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005239 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005240 {
cristy3ed852e2009-09-05 21:47:34 +00005241 tint_image=DestroyImage(tint_image);
5242 return((Image *) NULL);
5243 }
cristyaed9c382011-10-03 17:54:21 +00005244 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005245 return(tint_image);
5246 /*
5247 Determine RGB values of the color.
5248 */
cristy1707c6c2012-01-18 23:30:54 +00005249 GetPixelInfo(image,&color_vector);
cristyb817c3f2011-10-03 14:00:35 +00005250 flags=ParseGeometry(blend,&geometry_info);
cristy1707c6c2012-01-18 23:30:54 +00005251 color_vector.red=geometry_info.rho;
5252 color_vector.green=geometry_info.rho;
5253 color_vector.blue=geometry_info.rho;
5254 color_vector.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005255 if ((flags & SigmaValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005256 color_vector.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00005257 if ((flags & XiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005258 color_vector.blue=geometry_info.xi;
cristyb817c3f2011-10-03 14:00:35 +00005259 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005260 color_vector.alpha=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005261 if (image->colorspace == CMYKColorspace)
5262 {
cristy1707c6c2012-01-18 23:30:54 +00005263 color_vector.black=geometry_info.rho;
cristy76f512e2011-09-12 01:26:56 +00005264 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005265 color_vector.black=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005266 if ((flags & ChiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005267 color_vector.alpha=geometry_info.chi;
cristy76f512e2011-09-12 01:26:56 +00005268 }
cristy28474bf2011-09-11 23:32:52 +00005269 intensity=(MagickRealType) GetPixelInfoIntensity(tint);
cristy1707c6c2012-01-18 23:30:54 +00005270 color_vector.red=(MagickRealType) (color_vector.red*tint->red/100.0-
5271 intensity);
5272 color_vector.green=(MagickRealType) (color_vector.green*tint->green/100.0-
5273 intensity);
5274 color_vector.blue=(MagickRealType) (color_vector.blue*tint->blue/100.0-
5275 intensity);
5276 color_vector.black=(MagickRealType) (color_vector.black*tint->black/100.0-
5277 intensity);
5278 color_vector.alpha=(MagickRealType) (color_vector.alpha*tint->alpha/100.0-
5279 intensity);
cristy3ed852e2009-09-05 21:47:34 +00005280 /*
5281 Tint image.
5282 */
5283 status=MagickTrue;
5284 progress=0;
5285 image_view=AcquireCacheView(image);
5286 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005287#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005288 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005289#endif
cristybb503372010-05-27 20:51:26 +00005290 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005291 {
cristy4c08aed2011-07-01 19:47:50 +00005292 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005293 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005294
cristy4c08aed2011-07-01 19:47:50 +00005295 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005296 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005297
cristy6b91acb2011-04-19 12:23:54 +00005298 register ssize_t
5299 x;
5300
cristy3ed852e2009-09-05 21:47:34 +00005301 if (status == MagickFalse)
5302 continue;
5303 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5304 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5305 exception);
cristy4c08aed2011-07-01 19:47:50 +00005306 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005307 {
5308 status=MagickFalse;
5309 continue;
5310 }
cristybb503372010-05-27 20:51:26 +00005311 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005312 {
cristy4c08aed2011-07-01 19:47:50 +00005313 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005314 pixel;
5315
5316 MagickRealType
5317 weight;
5318
cristy1707c6c2012-01-18 23:30:54 +00005319 register ssize_t
5320 i;
5321
cristy10a6c612012-01-29 21:41:05 +00005322 if (GetPixelMask(image,p) != 0)
5323 {
5324 p+=GetPixelChannels(image);
5325 q+=GetPixelChannels(tint_image);
5326 continue;
5327 }
cristy1707c6c2012-01-18 23:30:54 +00005328 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
5329 {
5330 PixelChannel
5331 channel;
5332
5333 PixelTrait
5334 tint_traits,
5335 traits;
5336
5337 channel=GetPixelChannelMapChannel(image,i);
5338 traits=GetPixelChannelMapTraits(image,channel);
5339 tint_traits=GetPixelChannelMapTraits(tint_image,channel);
5340 if ((traits == UndefinedPixelTrait) ||
5341 (tint_traits == UndefinedPixelTrait))
5342 continue;
5343 if ((tint_traits & CopyPixelTrait) != 0)
5344 {
5345 SetPixelChannel(tint_image,channel,p[i],q);
5346 continue;
5347 }
5348 }
5349 GetPixelInfo(image,&pixel);
5350 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5351 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5352 (1.0-(4.0*(weight*weight)));
5353 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5354 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5355 (1.0-(4.0*(weight*weight)));
5356 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5357 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5358 (1.0-(4.0*(weight*weight)));
5359 weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5360 pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5361 (1.0-(4.0*(weight*weight)));
5362 SetPixelInfoPixel(tint_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005363 p+=GetPixelChannels(image);
5364 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005365 }
5366 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5367 status=MagickFalse;
5368 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5369 {
5370 MagickBooleanType
5371 proceed;
5372
cristyb5d5f722009-11-04 03:03:49 +00005373#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005374 #pragma omp critical (MagickCore_TintImage)
cristy3ed852e2009-09-05 21:47:34 +00005375#endif
5376 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5377 if (proceed == MagickFalse)
5378 status=MagickFalse;
5379 }
5380 }
5381 tint_view=DestroyCacheView(tint_view);
5382 image_view=DestroyCacheView(image_view);
5383 if (status == MagickFalse)
5384 tint_image=DestroyImage(tint_image);
5385 return(tint_image);
5386}
5387
5388/*
5389%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5390% %
5391% %
5392% %
5393% V i g n e t t e I m a g e %
5394% %
5395% %
5396% %
5397%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5398%
5399% VignetteImage() softens the edges of the image in vignette style.
5400%
5401% The format of the VignetteImage method is:
5402%
5403% Image *VignetteImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00005404% const double sigma,const ssize_t x,const ssize_t y,
cristy05c0c9a2011-09-05 23:16:13 +00005405% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005406%
5407% A description of each parameter follows:
5408%
5409% o image: the image.
5410%
5411% o radius: the radius of the pixel neighborhood.
5412%
5413% o sigma: the standard deviation of the Gaussian, in pixels.
5414%
5415% o x, y: Define the x and y ellipse offset.
5416%
5417% o exception: return any errors or warnings in this structure.
5418%
5419*/
5420MagickExport Image *VignetteImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00005421 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005422{
5423 char
5424 ellipse[MaxTextExtent];
5425
5426 DrawInfo
5427 *draw_info;
5428
5429 Image
5430 *canvas_image,
5431 *blur_image,
5432 *oval_image,
5433 *vignette_image;
5434
5435 assert(image != (Image *) NULL);
5436 assert(image->signature == MagickSignature);
5437 if (image->debug != MagickFalse)
5438 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5439 assert(exception != (ExceptionInfo *) NULL);
5440 assert(exception->signature == MagickSignature);
5441 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5442 if (canvas_image == (Image *) NULL)
5443 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005444 if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005445 {
cristy3ed852e2009-09-05 21:47:34 +00005446 canvas_image=DestroyImage(canvas_image);
5447 return((Image *) NULL);
5448 }
5449 canvas_image->matte=MagickTrue;
cristy98621462011-12-31 22:31:11 +00005450 oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
5451 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005452 if (oval_image == (Image *) NULL)
5453 {
5454 canvas_image=DestroyImage(canvas_image);
5455 return((Image *) NULL);
5456 }
cristy9950d572011-10-01 18:22:35 +00005457 (void) QueryColorCompliance("#000000",AllCompliance,
5458 &oval_image->background_color,exception);
cristyea1a8aa2011-10-20 13:24:06 +00005459 (void) SetImageBackgroundColor(oval_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005460 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00005461 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5462 exception);
5463 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5464 exception);
cristy1707c6c2012-01-18 23:30:54 +00005465 (void) FormatLocaleString(ellipse,MaxTextExtent,"ellipse %g,%g,%g,%g,"
5466 "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5467 image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005468 draw_info->primitive=AcquireString(ellipse);
cristy018f07f2011-09-04 21:15:19 +00005469 (void) DrawImage(oval_image,draw_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005470 draw_info=DestroyDrawInfo(draw_info);
cristyaa2c16c2012-03-25 22:21:35 +00005471 blur_image=BlurImage(oval_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00005472 oval_image=DestroyImage(oval_image);
5473 if (blur_image == (Image *) NULL)
5474 {
5475 canvas_image=DestroyImage(canvas_image);
5476 return((Image *) NULL);
5477 }
5478 blur_image->matte=MagickFalse;
cristy39172402012-03-30 13:04:39 +00005479 (void) CompositeImage(canvas_image,blur_image,IntensityCompositeOp,MagickTrue,
5480 0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00005481 blur_image=DestroyImage(blur_image);
5482 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5483 canvas_image=DestroyImage(canvas_image);
5484 return(vignette_image);
5485}
5486
5487/*
5488%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5489% %
5490% %
5491% %
5492% W a v e I m a g e %
5493% %
5494% %
5495% %
5496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5497%
5498% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005499% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005500% by the given parameters.
5501%
5502% The format of the WaveImage method is:
5503%
5504% Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005505% const double wave_length,const PixelInterpolateMethod method,
5506% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005507%
5508% A description of each parameter follows:
5509%
5510% o image: the image.
5511%
5512% o amplitude, wave_length: Define the amplitude and wave length of the
5513% sine wave.
5514%
cristy5c4e2582011-09-11 19:21:03 +00005515% o interpolate: the pixel interpolation method.
5516%
cristy3ed852e2009-09-05 21:47:34 +00005517% o exception: return any errors or warnings in this structure.
5518%
5519*/
5520MagickExport Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005521 const double wave_length,const PixelInterpolateMethod method,
5522 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005523{
5524#define WaveImageTag "Wave/Image"
5525
cristyfa112112010-01-04 17:48:07 +00005526 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005527 *image_view,
cristyfa112112010-01-04 17:48:07 +00005528 *wave_view;
5529
cristy3ed852e2009-09-05 21:47:34 +00005530 Image
5531 *wave_image;
5532
cristy3ed852e2009-09-05 21:47:34 +00005533 MagickBooleanType
5534 status;
5535
cristybb503372010-05-27 20:51:26 +00005536 MagickOffsetType
5537 progress;
5538
cristy3ed852e2009-09-05 21:47:34 +00005539 MagickRealType
5540 *sine_map;
5541
cristybb503372010-05-27 20:51:26 +00005542 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005543 i;
5544
cristybb503372010-05-27 20:51:26 +00005545 ssize_t
5546 y;
5547
cristy3ed852e2009-09-05 21:47:34 +00005548 /*
5549 Initialize wave image attributes.
5550 */
5551 assert(image != (Image *) NULL);
5552 assert(image->signature == MagickSignature);
5553 if (image->debug != MagickFalse)
5554 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5555 assert(exception != (ExceptionInfo *) NULL);
5556 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005557 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005558 fabs(amplitude)),MagickTrue,exception);
5559 if (wave_image == (Image *) NULL)
5560 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005561 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005562 {
cristy3ed852e2009-09-05 21:47:34 +00005563 wave_image=DestroyImage(wave_image);
5564 return((Image *) NULL);
5565 }
cristy4c08aed2011-07-01 19:47:50 +00005566 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005567 wave_image->matte=MagickTrue;
5568 /*
5569 Allocate sine map.
5570 */
5571 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5572 sizeof(*sine_map));
5573 if (sine_map == (MagickRealType *) NULL)
5574 {
5575 wave_image=DestroyImage(wave_image);
5576 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5577 }
cristybb503372010-05-27 20:51:26 +00005578 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005579 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5580 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005581 /*
5582 Wave image.
5583 */
5584 status=MagickTrue;
5585 progress=0;
cristyd76c51e2011-03-26 00:21:26 +00005586 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005587 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005588 (void) SetCacheViewVirtualPixelMethod(image_view,
5589 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005590#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005591 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005592#endif
cristybb503372010-05-27 20:51:26 +00005593 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005594 {
cristy4c08aed2011-07-01 19:47:50 +00005595 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005596 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005597
cristye97bb922011-04-03 01:36:52 +00005598 register ssize_t
5599 x;
5600
cristy3ed852e2009-09-05 21:47:34 +00005601 if (status == MagickFalse)
5602 continue;
5603 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5604 exception);
cristyacd2ed22011-08-30 01:44:23 +00005605 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005606 {
5607 status=MagickFalse;
5608 continue;
5609 }
cristybb503372010-05-27 20:51:26 +00005610 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005611 {
cristy5c4e2582011-09-11 19:21:03 +00005612 status=InterpolatePixelChannels(image,image_view,wave_image,method,
5613 (double) x,(double) (y-sine_map[x]),q,exception);
cristyed231572011-07-14 02:18:59 +00005614 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005615 }
5616 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5617 status=MagickFalse;
5618 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5619 {
5620 MagickBooleanType
5621 proceed;
5622
cristyb5d5f722009-11-04 03:03:49 +00005623#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005624 #pragma omp critical (MagickCore_WaveImage)
cristy3ed852e2009-09-05 21:47:34 +00005625#endif
5626 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5627 if (proceed == MagickFalse)
5628 status=MagickFalse;
5629 }
5630 }
5631 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005632 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005633 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5634 if (status == MagickFalse)
5635 wave_image=DestroyImage(wave_image);
5636 return(wave_image);
5637}