blob: d0989a6471b2520a456b5ea79e1ff6ca2a5bd095 [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
cristy10a6c612012-01-29 21:41:05 +0000338 if (GetPixelMask(image,p) != 0)
339 {
340 p+=GetPixelChannels(image);
341 q+=GetPixelChannels(noise_image);
342 continue;
343 }
cristy850b3072011-10-08 01:38:05 +0000344 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
345 {
346 PixelChannel
347 channel;
348
349 PixelTrait
350 noise_traits,
351 traits;
352
cristye2a912b2011-12-05 20:02:07 +0000353 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000354 traits=GetPixelChannelMapTraits(image,channel);
cristy850b3072011-10-08 01:38:05 +0000355 noise_traits=GetPixelChannelMapTraits(noise_image,channel);
356 if ((traits == UndefinedPixelTrait) ||
357 (noise_traits == UndefinedPixelTrait))
358 continue;
cristy9eeedea2011-11-02 19:04:05 +0000359 if ((noise_traits & CopyPixelTrait) != 0)
cristyb2145892011-10-10 00:55:32 +0000360 {
361 SetPixelChannel(noise_image,channel,p[i],q);
362 continue;
363 }
cristy850b3072011-10-08 01:38:05 +0000364 SetPixelChannel(noise_image,channel,ClampToQuantum(
365 GenerateDifferentialNoise(random_info[id],p[i],noise_type,attenuate)),
366 q);
367 }
cristyed231572011-07-14 02:18:59 +0000368 p+=GetPixelChannels(image);
369 q+=GetPixelChannels(noise_image);
cristy3ed852e2009-09-05 21:47:34 +0000370 }
371 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
372 if (sync == MagickFalse)
373 status=MagickFalse;
374 if (image->progress_monitor != (MagickProgressMonitor) NULL)
375 {
376 MagickBooleanType
377 proceed;
378
cristyb5d5f722009-11-04 03:03:49 +0000379#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy319a1e72010-02-21 15:13:11 +0000380 #pragma omp critical (MagickCore_AddNoiseImage)
cristy3ed852e2009-09-05 21:47:34 +0000381#endif
382 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
383 image->rows);
384 if (proceed == MagickFalse)
385 status=MagickFalse;
386 }
387 }
388 noise_view=DestroyCacheView(noise_view);
389 image_view=DestroyCacheView(image_view);
390 random_info=DestroyRandomInfoThreadSet(random_info);
391 if (status == MagickFalse)
392 noise_image=DestroyImage(noise_image);
393 return(noise_image);
394}
395
396/*
397%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398% %
399% %
400% %
401% B l u e S h i f t I m a g e %
402% %
403% %
404% %
405%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406%
407% BlueShiftImage() mutes the colors of the image to simulate a scene at
408% nighttime in the moonlight.
409%
410% The format of the BlueShiftImage method is:
411%
412% Image *BlueShiftImage(const Image *image,const double factor,
413% ExceptionInfo *exception)
414%
415% A description of each parameter follows:
416%
417% o image: the image.
418%
419% o factor: the shift factor.
420%
421% o exception: return any errors or warnings in this structure.
422%
423*/
424MagickExport Image *BlueShiftImage(const Image *image,const double factor,
425 ExceptionInfo *exception)
426{
427#define BlueShiftImageTag "BlueShift/Image"
428
cristyc4c8d132010-01-07 01:58:38 +0000429 CacheView
430 *image_view,
431 *shift_view;
432
cristy3ed852e2009-09-05 21:47:34 +0000433 Image
434 *shift_image;
435
cristy3ed852e2009-09-05 21:47:34 +0000436 MagickBooleanType
437 status;
438
cristybb503372010-05-27 20:51:26 +0000439 MagickOffsetType
440 progress;
441
442 ssize_t
443 y;
444
cristy3ed852e2009-09-05 21:47:34 +0000445 /*
446 Allocate blue shift image.
447 */
448 assert(image != (const Image *) NULL);
449 assert(image->signature == MagickSignature);
450 if (image->debug != MagickFalse)
451 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
452 assert(exception != (ExceptionInfo *) NULL);
453 assert(exception->signature == MagickSignature);
cristya6d7a9b2012-01-18 20:04:48 +0000454 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000455 if (shift_image == (Image *) NULL)
456 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000457 if (SetImageStorageClass(shift_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000458 {
cristy3ed852e2009-09-05 21:47:34 +0000459 shift_image=DestroyImage(shift_image);
460 return((Image *) NULL);
461 }
462 /*
463 Blue-shift DirectClass image.
464 */
465 status=MagickTrue;
466 progress=0;
467 image_view=AcquireCacheView(image);
468 shift_view=AcquireCacheView(shift_image);
cristy319a1e72010-02-21 15:13:11 +0000469#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000470 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000471#endif
cristybb503372010-05-27 20:51:26 +0000472 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000473 {
474 MagickBooleanType
475 sync;
476
cristy4c08aed2011-07-01 19:47:50 +0000477 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000478 pixel;
479
480 Quantum
481 quantum;
482
cristy4c08aed2011-07-01 19:47:50 +0000483 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000484 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000485
cristybb503372010-05-27 20:51:26 +0000486 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000487 x;
488
cristy4c08aed2011-07-01 19:47:50 +0000489 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000490 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000491
492 if (status == MagickFalse)
493 continue;
494 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
495 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
496 exception);
cristy4c08aed2011-07-01 19:47:50 +0000497 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000498 {
499 status=MagickFalse;
500 continue;
501 }
cristybb503372010-05-27 20:51:26 +0000502 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000503 {
cristy4c08aed2011-07-01 19:47:50 +0000504 quantum=GetPixelRed(image,p);
505 if (GetPixelGreen(image,p) < quantum)
506 quantum=GetPixelGreen(image,p);
507 if (GetPixelBlue(image,p) < quantum)
508 quantum=GetPixelBlue(image,p);
509 pixel.red=0.5*(GetPixelRed(image,p)+factor*quantum);
510 pixel.green=0.5*(GetPixelGreen(image,p)+factor*quantum);
511 pixel.blue=0.5*(GetPixelBlue(image,p)+factor*quantum);
512 quantum=GetPixelRed(image,p);
513 if (GetPixelGreen(image,p) > quantum)
514 quantum=GetPixelGreen(image,p);
515 if (GetPixelBlue(image,p) > quantum)
516 quantum=GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000517 pixel.red=0.5*(pixel.red+factor*quantum);
518 pixel.green=0.5*(pixel.green+factor*quantum);
519 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristy4c08aed2011-07-01 19:47:50 +0000520 SetPixelRed(shift_image,ClampToQuantum(pixel.red),q);
521 SetPixelGreen(shift_image,ClampToQuantum(pixel.green),q);
522 SetPixelBlue(shift_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000523 p+=GetPixelChannels(image);
524 q+=GetPixelChannels(shift_image);
cristy3ed852e2009-09-05 21:47:34 +0000525 }
526 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
527 if (sync == MagickFalse)
528 status=MagickFalse;
529 if (image->progress_monitor != (MagickProgressMonitor) NULL)
530 {
531 MagickBooleanType
532 proceed;
533
cristy319a1e72010-02-21 15:13:11 +0000534#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000535 #pragma omp critical (MagickCore_BlueShiftImage)
cristy3ed852e2009-09-05 21:47:34 +0000536#endif
537 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
538 image->rows);
539 if (proceed == MagickFalse)
540 status=MagickFalse;
541 }
542 }
543 image_view=DestroyCacheView(image_view);
544 shift_view=DestroyCacheView(shift_view);
545 if (status == MagickFalse)
546 shift_image=DestroyImage(shift_image);
547 return(shift_image);
548}
549
550/*
551%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
552% %
553% %
554% %
555% C h a r c o a l I m a g e %
556% %
557% %
558% %
559%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
560%
561% CharcoalImage() creates a new image that is a copy of an existing one with
562% the edge highlighted. It allocates the memory necessary for the new Image
563% structure and returns a pointer to the new image.
564%
565% The format of the CharcoalImage method is:
566%
567% Image *CharcoalImage(const Image *image,const double radius,
cristy05c0c9a2011-09-05 23:16:13 +0000568% const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000569%
570% A description of each parameter follows:
571%
572% o image: the image.
573%
574% o radius: the radius of the pixel neighborhood.
575%
576% o sigma: the standard deviation of the Gaussian, in pixels.
577%
cristy05c0c9a2011-09-05 23:16:13 +0000578% o bias: the bias.
579%
cristy3ed852e2009-09-05 21:47:34 +0000580% o exception: return any errors or warnings in this structure.
581%
582*/
583MagickExport Image *CharcoalImage(const Image *image,const double radius,
cristy05c0c9a2011-09-05 23:16:13 +0000584 const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000585{
586 Image
587 *charcoal_image,
588 *clone_image,
589 *edge_image;
590
591 assert(image != (Image *) NULL);
592 assert(image->signature == MagickSignature);
593 if (image->debug != MagickFalse)
594 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
595 assert(exception != (ExceptionInfo *) NULL);
596 assert(exception->signature == MagickSignature);
597 clone_image=CloneImage(image,0,0,MagickTrue,exception);
598 if (clone_image == (Image *) NULL)
599 return((Image *) NULL);
cristy018f07f2011-09-04 21:15:19 +0000600 (void) SetImageType(clone_image,GrayscaleType,exception);
cristy8ae632d2011-09-05 17:29:53 +0000601 edge_image=EdgeImage(clone_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000602 clone_image=DestroyImage(clone_image);
603 if (edge_image == (Image *) NULL)
604 return((Image *) NULL);
cristy05c0c9a2011-09-05 23:16:13 +0000605 charcoal_image=BlurImage(edge_image,radius,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +0000606 edge_image=DestroyImage(edge_image);
607 if (charcoal_image == (Image *) NULL)
608 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +0000609 (void) NormalizeImage(charcoal_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +0000610 (void) NegateImage(charcoal_image,MagickFalse,exception);
cristy018f07f2011-09-04 21:15:19 +0000611 (void) SetImageType(charcoal_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000612 return(charcoal_image);
613}
614
615/*
616%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
617% %
618% %
619% %
620% C o l o r i z e I m a g e %
621% %
622% %
623% %
624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
625%
626% ColorizeImage() blends the fill color with each pixel in the image.
627% A percentage blend is specified with opacity. Control the application
628% of different color components by specifying a different percentage for
629% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
630%
631% The format of the ColorizeImage method is:
632%
cristyc7e6ff62011-10-03 13:46:11 +0000633% Image *ColorizeImage(const Image *image,const char *blend,
634% const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000635%
636% A description of each parameter follows:
637%
638% o image: the image.
639%
cristyc7e6ff62011-10-03 13:46:11 +0000640% o blend: A character string indicating the level of blending as a
cristy3ed852e2009-09-05 21:47:34 +0000641% percentage.
642%
643% o colorize: A color value.
644%
645% o exception: return any errors or warnings in this structure.
646%
647*/
cristyc7e6ff62011-10-03 13:46:11 +0000648MagickExport Image *ColorizeImage(const Image *image,const char *blend,
649 const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000650{
651#define ColorizeImageTag "Colorize/Image"
652
cristyc4c8d132010-01-07 01:58:38 +0000653 CacheView
654 *colorize_view,
655 *image_view;
656
cristy3ed852e2009-09-05 21:47:34 +0000657 GeometryInfo
658 geometry_info;
659
660 Image
661 *colorize_image;
662
cristy3ed852e2009-09-05 21:47:34 +0000663 MagickBooleanType
664 status;
665
cristybb503372010-05-27 20:51:26 +0000666 MagickOffsetType
667 progress;
668
cristy3ed852e2009-09-05 21:47:34 +0000669 MagickStatusType
670 flags;
671
cristyc7e6ff62011-10-03 13:46:11 +0000672 PixelInfo
673 pixel;
674
cristybb503372010-05-27 20:51:26 +0000675 ssize_t
676 y;
677
cristy3ed852e2009-09-05 21:47:34 +0000678 /*
679 Allocate colorized image.
680 */
681 assert(image != (const Image *) NULL);
682 assert(image->signature == MagickSignature);
683 if (image->debug != MagickFalse)
684 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
685 assert(exception != (ExceptionInfo *) NULL);
686 assert(exception->signature == MagickSignature);
687 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
688 exception);
689 if (colorize_image == (Image *) NULL)
690 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000691 if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000692 {
cristy3ed852e2009-09-05 21:47:34 +0000693 colorize_image=DestroyImage(colorize_image);
694 return((Image *) NULL);
695 }
cristyc7e6ff62011-10-03 13:46:11 +0000696 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000697 return(colorize_image);
698 /*
anthonyfd706f92012-01-19 04:22:02 +0000699 Determine RGB values of the fill color for pixel
cristy3ed852e2009-09-05 21:47:34 +0000700 */
cristyc7e6ff62011-10-03 13:46:11 +0000701 GetPixelInfo(image,&pixel);
cristyb817c3f2011-10-03 14:00:35 +0000702 flags=ParseGeometry(blend,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000703 pixel.red=geometry_info.rho;
704 pixel.green=geometry_info.rho;
705 pixel.blue=geometry_info.rho;
cristyc7e6ff62011-10-03 13:46:11 +0000706 pixel.alpha=100.0;
cristy3ed852e2009-09-05 21:47:34 +0000707 if ((flags & SigmaValue) != 0)
708 pixel.green=geometry_info.sigma;
709 if ((flags & XiValue) != 0)
710 pixel.blue=geometry_info.xi;
711 if ((flags & PsiValue) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000712 pixel.alpha=geometry_info.psi;
cristyc7e6ff62011-10-03 13:46:11 +0000713 if (pixel.colorspace == CMYKColorspace)
714 {
715 pixel.black=geometry_info.rho;
716 if ((flags & PsiValue) != 0)
717 pixel.black=geometry_info.psi;
718 if ((flags & ChiValue) != 0)
719 pixel.alpha=geometry_info.chi;
720 }
cristy3ed852e2009-09-05 21:47:34 +0000721 /*
722 Colorize DirectClass image.
723 */
724 status=MagickTrue;
725 progress=0;
726 image_view=AcquireCacheView(image);
727 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000728#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000729 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000730#endif
cristybb503372010-05-27 20:51:26 +0000731 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000732 {
733 MagickBooleanType
734 sync;
735
cristy4c08aed2011-07-01 19:47:50 +0000736 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000737 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000738
cristybb503372010-05-27 20:51:26 +0000739 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000740 x;
741
cristy4c08aed2011-07-01 19:47:50 +0000742 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000743 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000744
745 if (status == MagickFalse)
746 continue;
747 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
748 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
749 exception);
cristy4c08aed2011-07-01 19:47:50 +0000750 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000751 {
752 status=MagickFalse;
753 continue;
754 }
cristybb503372010-05-27 20:51:26 +0000755 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000756 {
cristyc7e6ff62011-10-03 13:46:11 +0000757 register ssize_t
758 i;
759
cristy10a6c612012-01-29 21:41:05 +0000760 if (GetPixelMask(image,p) != 0)
761 {
762 p+=GetPixelChannels(image);
763 q+=GetPixelChannels(colorize_image);
764 continue;
765 }
cristyc7e6ff62011-10-03 13:46:11 +0000766 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
767 {
768 PixelChannel
769 channel;
770
771 PixelTrait
772 colorize_traits,
773 traits;
774
cristye2a912b2011-12-05 20:02:07 +0000775 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000776 traits=GetPixelChannelMapTraits(image,channel);
cristyc7e6ff62011-10-03 13:46:11 +0000777 colorize_traits=GetPixelChannelMapTraits(colorize_image,channel);
778 if ((traits == UndefinedPixelTrait) ||
779 (colorize_traits == UndefinedPixelTrait))
780 continue;
781 if ((colorize_traits & CopyPixelTrait) != 0)
782 {
783 SetPixelChannel(colorize_image,channel,p[i],q);
784 continue;
785 }
786 switch (channel)
787 {
788 case RedPixelChannel:
789 {
790 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
791 (100.0-pixel.red)+colorize->red*pixel.red)/100.0),q);
792 break;
793 }
794 case GreenPixelChannel:
795 {
796 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
797 (100.0-pixel.green)+colorize->green*pixel.green)/100.0),q);
798 break;
799 }
800 case BluePixelChannel:
801 {
802 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
803 (100.0-pixel.blue)+colorize->blue*pixel.blue)/100.0),q);
804 break;
805 }
806 case BlackPixelChannel:
807 {
808 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
809 (100.0-pixel.black)+colorize->black*pixel.black)/100.0),q);
810 break;
811 }
812 case AlphaPixelChannel:
813 {
814 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
815 (100.0-pixel.alpha)+colorize->alpha*pixel.alpha)/100.0),q);
816 break;
817 }
818 default:
819 {
820 SetPixelChannel(colorize_image,channel,p[i],q);
821 break;
822 }
823 }
824 }
cristyed231572011-07-14 02:18:59 +0000825 p+=GetPixelChannels(image);
826 q+=GetPixelChannels(colorize_image);
cristy3ed852e2009-09-05 21:47:34 +0000827 }
828 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
829 if (sync == MagickFalse)
830 status=MagickFalse;
831 if (image->progress_monitor != (MagickProgressMonitor) NULL)
832 {
833 MagickBooleanType
834 proceed;
835
cristy319a1e72010-02-21 15:13:11 +0000836#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000837 #pragma omp critical (MagickCore_ColorizeImage)
cristy3ed852e2009-09-05 21:47:34 +0000838#endif
839 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
840 if (proceed == MagickFalse)
841 status=MagickFalse;
842 }
843 }
844 image_view=DestroyCacheView(image_view);
845 colorize_view=DestroyCacheView(colorize_view);
846 if (status == MagickFalse)
847 colorize_image=DestroyImage(colorize_image);
848 return(colorize_image);
849}
850
851/*
852%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
853% %
854% %
855% %
cristye6365592010-04-02 17:31:23 +0000856% C o l o r M a t r i x I m a g e %
857% %
858% %
859% %
860%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
861%
862% ColorMatrixImage() applies color transformation to an image. This method
863% permits saturation changes, hue rotation, luminance to alpha, and various
864% other effects. Although variable-sized transformation matrices can be used,
865% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
866% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
867% except offsets are in column 6 rather than 5 (in support of CMYKA images)
868% and offsets are normalized (divide Flash offset by 255).
869%
870% The format of the ColorMatrixImage method is:
871%
872% Image *ColorMatrixImage(const Image *image,
873% const KernelInfo *color_matrix,ExceptionInfo *exception)
874%
875% A description of each parameter follows:
876%
877% o image: the image.
878%
879% o color_matrix: the color matrix.
880%
881% o exception: return any errors or warnings in this structure.
882%
883*/
anthonyfd706f92012-01-19 04:22:02 +0000884/* FUTURE: modify to make use of a MagickMatrix Mutliply function
885 That should be provided in "matrix.c"
886 (ASIDE: actually distorts should do this too but currently doesn't)
887*/
888
cristye6365592010-04-02 17:31:23 +0000889MagickExport Image *ColorMatrixImage(const Image *image,
890 const KernelInfo *color_matrix,ExceptionInfo *exception)
891{
892#define ColorMatrixImageTag "ColorMatrix/Image"
893
894 CacheView
895 *color_view,
896 *image_view;
897
898 double
899 ColorMatrix[6][6] =
900 {
901 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
902 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
903 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
904 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
905 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
906 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
907 };
908
909 Image
910 *color_image;
911
cristye6365592010-04-02 17:31:23 +0000912 MagickBooleanType
913 status;
914
cristybb503372010-05-27 20:51:26 +0000915 MagickOffsetType
916 progress;
917
918 register ssize_t
cristye6365592010-04-02 17:31:23 +0000919 i;
920
cristybb503372010-05-27 20:51:26 +0000921 ssize_t
922 u,
923 v,
924 y;
925
cristye6365592010-04-02 17:31:23 +0000926 /*
anthonyfd706f92012-01-19 04:22:02 +0000927 Map given color_matrix, into a 6x6 matrix RGBKA and a constant
cristye6365592010-04-02 17:31:23 +0000928 */
929 assert(image != (Image *) NULL);
930 assert(image->signature == MagickSignature);
931 if (image->debug != MagickFalse)
932 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
933 assert(exception != (ExceptionInfo *) NULL);
934 assert(exception->signature == MagickSignature);
935 i=0;
cristybb503372010-05-27 20:51:26 +0000936 for (v=0; v < (ssize_t) color_matrix->height; v++)
937 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000938 {
939 if ((v < 6) && (u < 6))
940 ColorMatrix[v][u]=color_matrix->values[i];
941 i++;
942 }
943 /*
944 Initialize color image.
945 */
cristy12550e62010-06-07 12:46:40 +0000946 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000947 if (color_image == (Image *) NULL)
948 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000949 if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
cristye6365592010-04-02 17:31:23 +0000950 {
cristye6365592010-04-02 17:31:23 +0000951 color_image=DestroyImage(color_image);
952 return((Image *) NULL);
953 }
954 if (image->debug != MagickFalse)
955 {
956 char
957 format[MaxTextExtent],
958 *message;
959
960 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
961 " ColorMatrix image with color matrix:");
962 message=AcquireString("");
963 for (v=0; v < 6; v++)
964 {
965 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000966 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000967 (void) ConcatenateString(&message,format);
968 for (u=0; u < 6; u++)
969 {
cristyb51dff52011-05-19 16:55:47 +0000970 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
cristye6365592010-04-02 17:31:23 +0000971 ColorMatrix[v][u]);
972 (void) ConcatenateString(&message,format);
973 }
974 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
975 }
976 message=DestroyString(message);
977 }
978 /*
anthonyfd706f92012-01-19 04:22:02 +0000979 Apply the ColorMatrix to image.
cristye6365592010-04-02 17:31:23 +0000980 */
981 status=MagickTrue;
982 progress=0;
983 image_view=AcquireCacheView(image);
984 color_view=AcquireCacheView(color_image);
985#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000986 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye6365592010-04-02 17:31:23 +0000987#endif
cristybb503372010-05-27 20:51:26 +0000988 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000989 {
990 MagickRealType
991 pixel;
992
cristy4c08aed2011-07-01 19:47:50 +0000993 register const Quantum
cristye6365592010-04-02 17:31:23 +0000994 *restrict p;
995
cristy4c08aed2011-07-01 19:47:50 +0000996 register Quantum
997 *restrict q;
998
cristybb503372010-05-27 20:51:26 +0000999 register ssize_t
cristye6365592010-04-02 17:31:23 +00001000 x;
1001
cristye6365592010-04-02 17:31:23 +00001002 if (status == MagickFalse)
1003 continue;
1004 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1005 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
1006 exception);
cristy4c08aed2011-07-01 19:47:50 +00001007 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristye6365592010-04-02 17:31:23 +00001008 {
1009 status=MagickFalse;
1010 continue;
1011 }
cristybb503372010-05-27 20:51:26 +00001012 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +00001013 {
cristybb503372010-05-27 20:51:26 +00001014 register ssize_t
cristye6365592010-04-02 17:31:23 +00001015 v;
1016
cristybb503372010-05-27 20:51:26 +00001017 size_t
cristye6365592010-04-02 17:31:23 +00001018 height;
1019
1020 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +00001021 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +00001022 {
cristy4c08aed2011-07-01 19:47:50 +00001023 pixel=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
1024 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +00001025 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001026 pixel+=ColorMatrix[v][3]*GetPixelBlack(image,p);
1027 if (image->matte != MagickFalse)
1028 pixel+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
cristye6365592010-04-02 17:31:23 +00001029 pixel+=QuantumRange*ColorMatrix[v][5];
1030 switch (v)
1031 {
cristy4c08aed2011-07-01 19:47:50 +00001032 case 0: SetPixelRed(color_image,ClampToQuantum(pixel),q); break;
1033 case 1: SetPixelGreen(color_image,ClampToQuantum(pixel),q); break;
1034 case 2: SetPixelBlue(color_image,ClampToQuantum(pixel),q); break;
cristye6365592010-04-02 17:31:23 +00001035 case 3:
1036 {
cristy4c08aed2011-07-01 19:47:50 +00001037 if (image->colorspace == CMYKColorspace)
1038 SetPixelBlack(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001039 break;
1040 }
1041 case 4:
1042 {
cristy4c08aed2011-07-01 19:47:50 +00001043 if (image->matte != MagickFalse)
1044 SetPixelAlpha(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001045 break;
1046 }
1047 }
1048 }
cristyed231572011-07-14 02:18:59 +00001049 p+=GetPixelChannels(image);
1050 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +00001051 }
1052 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1053 status=MagickFalse;
1054 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1055 {
1056 MagickBooleanType
1057 proceed;
1058
1059#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00001060 #pragma omp critical (MagickCore_ColorMatrixImage)
cristye6365592010-04-02 17:31:23 +00001061#endif
1062 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1063 image->rows);
1064 if (proceed == MagickFalse)
1065 status=MagickFalse;
1066 }
1067 }
1068 color_view=DestroyCacheView(color_view);
1069 image_view=DestroyCacheView(image_view);
1070 if (status == MagickFalse)
1071 color_image=DestroyImage(color_image);
1072 return(color_image);
1073}
1074
1075/*
1076%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1077% %
1078% %
1079% %
cristy3ed852e2009-09-05 21:47:34 +00001080+ D e s t r o y F x I n f o %
1081% %
1082% %
1083% %
1084%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1085%
1086% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1087%
1088% The format of the DestroyFxInfo method is:
1089%
1090% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1091%
1092% A description of each parameter follows:
1093%
1094% o fx_info: the fx info.
1095%
1096*/
cristy7832dc22011-09-05 01:21:53 +00001097MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
cristy3ed852e2009-09-05 21:47:34 +00001098{
cristybb503372010-05-27 20:51:26 +00001099 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001100 i;
1101
1102 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1103 fx_info->expression=DestroyString(fx_info->expression);
1104 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1105 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001106 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001107 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1108 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001109 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1110 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1111 return(fx_info);
1112}
1113
1114/*
1115%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116% %
1117% %
1118% %
cristy3ed852e2009-09-05 21:47:34 +00001119+ 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 %
1120% %
1121% %
1122% %
1123%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1124%
1125% FxEvaluateChannelExpression() evaluates an expression and returns the
1126% results.
1127%
1128% The format of the FxEvaluateExpression method is:
1129%
1130% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001131% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001132% MagickRealType *alpha,Exceptioninfo *exception)
1133% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1134% MagickRealType *alpha,Exceptioninfo *exception)
1135%
1136% A description of each parameter follows:
1137%
1138% o fx_info: the fx info.
1139%
1140% o channel: the channel.
1141%
1142% o x,y: the pixel position.
1143%
1144% o alpha: the result.
1145%
1146% o exception: return any errors or warnings in this structure.
1147%
1148*/
1149
cristy351842f2010-03-07 15:27:38 +00001150static inline double MagickMax(const double x,const double y)
1151{
1152 if (x > y)
1153 return(x);
1154 return(y);
1155}
1156
1157static inline double MagickMin(const double x,const double y)
1158{
1159 if (x < y)
1160 return(x);
1161 return(y);
1162}
1163
cristy3ed852e2009-09-05 21:47:34 +00001164static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001165 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001166{
1167 char
1168 key[MaxTextExtent],
1169 statistic[MaxTextExtent];
1170
1171 const char
1172 *value;
1173
1174 register const char
1175 *p;
1176
1177 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1178 if (*p == '.')
1179 switch (*++p) /* e.g. depth.r */
1180 {
cristy541ae572011-08-05 19:08:59 +00001181 case 'r': channel=RedPixelChannel; break;
1182 case 'g': channel=GreenPixelChannel; break;
1183 case 'b': channel=BluePixelChannel; break;
1184 case 'c': channel=CyanPixelChannel; break;
1185 case 'm': channel=MagentaPixelChannel; break;
1186 case 'y': channel=YellowPixelChannel; break;
1187 case 'k': channel=BlackPixelChannel; break;
cristy3ed852e2009-09-05 21:47:34 +00001188 default: break;
1189 }
cristyb51dff52011-05-19 16:55:47 +00001190 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001191 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001192 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1193 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001194 return(QuantumScale*StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001195 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1196 if (LocaleNCompare(symbol,"depth",5) == 0)
1197 {
cristybb503372010-05-27 20:51:26 +00001198 size_t
cristy3ed852e2009-09-05 21:47:34 +00001199 depth;
1200
cristyfefab1b2011-07-05 00:33:22 +00001201 depth=GetImageDepth(image,exception);
1202 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001203 }
1204 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1205 {
1206 double
1207 kurtosis,
1208 skewness;
1209
cristyd42d9952011-07-08 14:21:50 +00001210 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001211 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001212 }
1213 if (LocaleNCompare(symbol,"maxima",6) == 0)
1214 {
1215 double
1216 maxima,
1217 minima;
1218
cristyd42d9952011-07-08 14:21:50 +00001219 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001220 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001221 }
1222 if (LocaleNCompare(symbol,"mean",4) == 0)
1223 {
1224 double
1225 mean,
1226 standard_deviation;
1227
cristyd42d9952011-07-08 14:21:50 +00001228 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001229 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001230 }
1231 if (LocaleNCompare(symbol,"minima",6) == 0)
1232 {
1233 double
1234 maxima,
1235 minima;
1236
cristyd42d9952011-07-08 14:21:50 +00001237 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001238 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001239 }
1240 if (LocaleNCompare(symbol,"skewness",8) == 0)
1241 {
1242 double
1243 kurtosis,
1244 skewness;
1245
cristyd42d9952011-07-08 14:21:50 +00001246 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001247 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001248 }
1249 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1250 {
1251 double
1252 mean,
1253 standard_deviation;
1254
cristyd42d9952011-07-08 14:21:50 +00001255 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001256 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001257 standard_deviation);
1258 }
1259 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1260 ConstantString(statistic));
cristydbdd0e32011-11-04 23:29:40 +00001261 return(QuantumScale*StringToDouble(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001262}
1263
1264static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001265 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001266 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001267
cristyb0aad4c2011-11-02 19:30:35 +00001268static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
1269{
1270 if (beta != 0)
1271 return(FxGCD(beta,alpha % beta));
1272 return(alpha);
1273}
1274
cristy3ed852e2009-09-05 21:47:34 +00001275static inline const char *FxSubexpression(const char *expression,
1276 ExceptionInfo *exception)
1277{
1278 const char
1279 *subexpression;
1280
cristybb503372010-05-27 20:51:26 +00001281 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001282 level;
1283
1284 level=0;
1285 subexpression=expression;
1286 while ((*subexpression != '\0') &&
1287 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1288 {
1289 if (strchr("(",(int) *subexpression) != (char *) NULL)
1290 level++;
1291 else
1292 if (strchr(")",(int) *subexpression) != (char *) NULL)
1293 level--;
1294 subexpression++;
1295 }
1296 if (*subexpression == '\0')
1297 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1298 "UnbalancedParenthesis","`%s'",expression);
1299 return(subexpression);
1300}
1301
cristy0568ffc2011-07-25 16:54:14 +00001302static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001303 const ssize_t x,const ssize_t y,const char *expression,
1304 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001305{
1306 char
1307 *q,
1308 subexpression[MaxTextExtent],
1309 symbol[MaxTextExtent];
1310
1311 const char
1312 *p,
1313 *value;
1314
1315 Image
1316 *image;
1317
cristy4c08aed2011-07-01 19:47:50 +00001318 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001319 pixel;
1320
1321 MagickRealType
1322 alpha,
1323 beta;
1324
1325 PointInfo
1326 point;
1327
cristybb503372010-05-27 20:51:26 +00001328 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001329 i;
1330
1331 size_t
cristy1707c6c2012-01-18 23:30:54 +00001332 length,
cristy3ed852e2009-09-05 21:47:34 +00001333 level;
1334
1335 p=expression;
1336 i=GetImageIndexInList(fx_info->images);
1337 level=0;
1338 point.x=(double) x;
1339 point.y=(double) y;
1340 if (isalpha((int) *(p+1)) == 0)
1341 {
1342 if (strchr("suv",(int) *p) != (char *) NULL)
1343 {
1344 switch (*p)
1345 {
1346 case 's':
1347 default:
1348 {
1349 i=GetImageIndexInList(fx_info->images);
1350 break;
1351 }
1352 case 'u': i=0; break;
1353 case 'v': i=1; break;
1354 }
1355 p++;
1356 if (*p == '[')
1357 {
1358 level++;
1359 q=subexpression;
1360 for (p++; *p != '\0'; )
1361 {
1362 if (*p == '[')
1363 level++;
1364 else
1365 if (*p == ']')
1366 {
1367 level--;
1368 if (level == 0)
1369 break;
1370 }
1371 *q++=(*p++);
1372 }
1373 *q='\0';
1374 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1375 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001376 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001377 p++;
1378 }
1379 if (*p == '.')
1380 p++;
1381 }
1382 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1383 {
1384 p++;
1385 if (*p == '{')
1386 {
1387 level++;
1388 q=subexpression;
1389 for (p++; *p != '\0'; )
1390 {
1391 if (*p == '{')
1392 level++;
1393 else
1394 if (*p == '}')
1395 {
1396 level--;
1397 if (level == 0)
1398 break;
1399 }
1400 *q++=(*p++);
1401 }
1402 *q='\0';
1403 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1404 &beta,exception);
1405 point.x=alpha;
1406 point.y=beta;
1407 p++;
1408 }
1409 else
1410 if (*p == '[')
1411 {
1412 level++;
1413 q=subexpression;
1414 for (p++; *p != '\0'; )
1415 {
1416 if (*p == '[')
1417 level++;
1418 else
1419 if (*p == ']')
1420 {
1421 level--;
1422 if (level == 0)
1423 break;
1424 }
1425 *q++=(*p++);
1426 }
1427 *q='\0';
1428 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1429 &beta,exception);
1430 point.x+=alpha;
1431 point.y+=beta;
1432 p++;
1433 }
1434 if (*p == '.')
1435 p++;
1436 }
1437 }
1438 length=GetImageListLength(fx_info->images);
1439 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001440 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001441 i%=length;
1442 image=GetImageFromList(fx_info->images,i);
1443 if (image == (Image *) NULL)
1444 {
1445 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1446 "NoSuchImage","`%s'",expression);
1447 return(0.0);
1448 }
cristy4c08aed2011-07-01 19:47:50 +00001449 GetPixelInfo(image,&pixel);
1450 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001451 point.x,point.y,&pixel,exception);
cristy1707c6c2012-01-18 23:30:54 +00001452 if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1453 (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001454 (LocaleCompare(p,"saturation") != 0) &&
1455 (LocaleCompare(p,"lightness") != 0))
1456 {
1457 char
1458 name[MaxTextExtent];
1459
1460 (void) CopyMagickString(name,p,MaxTextExtent);
1461 for (q=name+(strlen(name)-1); q > name; q--)
1462 {
1463 if (*q == ')')
1464 break;
1465 if (*q == '.')
1466 {
1467 *q='\0';
1468 break;
1469 }
1470 }
1471 if ((strlen(name) > 2) &&
1472 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1473 {
cristy4c08aed2011-07-01 19:47:50 +00001474 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001475 *color;
1476
cristy4c08aed2011-07-01 19:47:50 +00001477 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1478 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001479 {
1480 pixel=(*color);
1481 p+=strlen(name);
1482 }
1483 else
cristy1707c6c2012-01-18 23:30:54 +00001484 {
1485 MagickBooleanType
1486 status;
1487
1488 status=QueryColorCompliance(name,AllCompliance,&pixel,
1489 fx_info->exception);
1490 if (status != MagickFalse)
1491 {
1492 (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1493 name),ClonePixelInfo(&pixel));
1494 p+=strlen(name);
1495 }
1496 }
cristy3ed852e2009-09-05 21:47:34 +00001497 }
1498 }
1499 (void) CopyMagickString(symbol,p,MaxTextExtent);
1500 StripString(symbol);
1501 if (*symbol == '\0')
1502 {
1503 switch (channel)
1504 {
cristy0568ffc2011-07-25 16:54:14 +00001505 case RedPixelChannel: return(QuantumScale*pixel.red);
1506 case GreenPixelChannel: return(QuantumScale*pixel.green);
1507 case BluePixelChannel: return(QuantumScale*pixel.blue);
1508 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001509 {
1510 if (image->colorspace != CMYKColorspace)
1511 {
1512 (void) ThrowMagickException(exception,GetMagickModule(),
cristy1a020e42011-12-06 18:13:23 +00001513 ImageError,"ColorSeparatedImageRequired","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00001514 image->filename);
1515 return(0.0);
1516 }
cristy4c08aed2011-07-01 19:47:50 +00001517 return(QuantumScale*pixel.black);
1518 }
cristy0568ffc2011-07-25 16:54:14 +00001519 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001520 {
1521 MagickRealType
1522 alpha;
1523
1524 if (pixel.matte == MagickFalse)
1525 return(1.0);
1526 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1527 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001528 }
cristya382aca2011-12-06 18:22:48 +00001529 case IndexPixelChannel:
1530 return(0.0);
cristyb3a73b52011-07-26 01:34:43 +00001531 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001532 {
cristy4c08aed2011-07-01 19:47:50 +00001533 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001534 }
cristy3ed852e2009-09-05 21:47:34 +00001535 default:
1536 break;
1537 }
1538 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1539 "UnableToParseExpression","`%s'",p);
1540 return(0.0);
1541 }
1542 switch (*symbol)
1543 {
1544 case 'A':
1545 case 'a':
1546 {
1547 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001548 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001549 break;
1550 }
1551 case 'B':
1552 case 'b':
1553 {
1554 if (LocaleCompare(symbol,"b") == 0)
1555 return(QuantumScale*pixel.blue);
1556 break;
1557 }
1558 case 'C':
1559 case 'c':
1560 {
1561 if (LocaleNCompare(symbol,"channel",7) == 0)
1562 {
1563 GeometryInfo
1564 channel_info;
1565
1566 MagickStatusType
1567 flags;
1568
1569 flags=ParseGeometry(symbol+7,&channel_info);
1570 if (image->colorspace == CMYKColorspace)
1571 switch (channel)
1572 {
cristy0568ffc2011-07-25 16:54:14 +00001573 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001574 {
1575 if ((flags & RhoValue) == 0)
1576 return(0.0);
1577 return(channel_info.rho);
1578 }
cristy0568ffc2011-07-25 16:54:14 +00001579 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001580 {
1581 if ((flags & SigmaValue) == 0)
1582 return(0.0);
1583 return(channel_info.sigma);
1584 }
cristy0568ffc2011-07-25 16:54:14 +00001585 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001586 {
1587 if ((flags & XiValue) == 0)
1588 return(0.0);
1589 return(channel_info.xi);
1590 }
cristy0568ffc2011-07-25 16:54:14 +00001591 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001592 {
1593 if ((flags & PsiValue) == 0)
1594 return(0.0);
1595 return(channel_info.psi);
1596 }
cristy0568ffc2011-07-25 16:54:14 +00001597 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001598 {
1599 if ((flags & ChiValue) == 0)
1600 return(0.0);
1601 return(channel_info.chi);
1602 }
1603 default:
1604 return(0.0);
1605 }
1606 switch (channel)
1607 {
cristy0568ffc2011-07-25 16:54:14 +00001608 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001609 {
1610 if ((flags & RhoValue) == 0)
1611 return(0.0);
1612 return(channel_info.rho);
1613 }
cristy0568ffc2011-07-25 16:54:14 +00001614 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001615 {
1616 if ((flags & SigmaValue) == 0)
1617 return(0.0);
1618 return(channel_info.sigma);
1619 }
cristy0568ffc2011-07-25 16:54:14 +00001620 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001621 {
1622 if ((flags & XiValue) == 0)
1623 return(0.0);
1624 return(channel_info.xi);
1625 }
cristy0568ffc2011-07-25 16:54:14 +00001626 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001627 {
1628 if ((flags & ChiValue) == 0)
1629 return(0.0);
1630 return(channel_info.chi);
1631 }
cristy0568ffc2011-07-25 16:54:14 +00001632 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001633 {
1634 if ((flags & PsiValue) == 0)
1635 return(0.0);
1636 return(channel_info.psi);
1637 }
cristy3ed852e2009-09-05 21:47:34 +00001638 default:
1639 return(0.0);
1640 }
1641 return(0.0);
1642 }
1643 if (LocaleCompare(symbol,"c") == 0)
1644 return(QuantumScale*pixel.red);
1645 break;
1646 }
1647 case 'D':
1648 case 'd':
1649 {
1650 if (LocaleNCompare(symbol,"depth",5) == 0)
1651 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1652 break;
1653 }
1654 case 'G':
1655 case 'g':
1656 {
1657 if (LocaleCompare(symbol,"g") == 0)
1658 return(QuantumScale*pixel.green);
1659 break;
1660 }
1661 case 'K':
1662 case 'k':
1663 {
1664 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1665 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1666 if (LocaleCompare(symbol,"k") == 0)
1667 {
1668 if (image->colorspace != CMYKColorspace)
1669 {
1670 (void) ThrowMagickException(exception,GetMagickModule(),
1671 OptionError,"ColorSeparatedImageRequired","`%s'",
1672 image->filename);
1673 return(0.0);
1674 }
cristy4c08aed2011-07-01 19:47:50 +00001675 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001676 }
1677 break;
1678 }
1679 case 'H':
1680 case 'h':
1681 {
1682 if (LocaleCompare(symbol,"h") == 0)
1683 return((MagickRealType) image->rows);
1684 if (LocaleCompare(symbol,"hue") == 0)
1685 {
1686 double
1687 hue,
1688 lightness,
1689 saturation;
1690
cristyda1f9c12011-10-02 21:39:49 +00001691 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1692 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001693 return(hue);
1694 }
1695 break;
1696 }
1697 case 'I':
1698 case 'i':
1699 {
1700 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1701 (LocaleCompare(symbol,"image.minima") == 0) ||
1702 (LocaleCompare(symbol,"image.maxima") == 0) ||
1703 (LocaleCompare(symbol,"image.mean") == 0) ||
1704 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1705 (LocaleCompare(symbol,"image.skewness") == 0) ||
1706 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1707 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1708 if (LocaleCompare(symbol,"image.resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001709 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001710 if (LocaleCompare(symbol,"image.resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001711 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001712 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001713 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001714 if (LocaleCompare(symbol,"i") == 0)
1715 return((MagickRealType) x);
1716 break;
1717 }
1718 case 'J':
1719 case 'j':
1720 {
1721 if (LocaleCompare(symbol,"j") == 0)
1722 return((MagickRealType) y);
1723 break;
1724 }
1725 case 'L':
1726 case 'l':
1727 {
1728 if (LocaleCompare(symbol,"lightness") == 0)
1729 {
1730 double
1731 hue,
1732 lightness,
1733 saturation;
1734
cristyda1f9c12011-10-02 21:39:49 +00001735 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1736 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001737 return(lightness);
1738 }
1739 if (LocaleCompare(symbol,"luminance") == 0)
1740 {
1741 double
1742 luminence;
1743
1744 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1745 return(QuantumScale*luminence);
1746 }
1747 break;
1748 }
1749 case 'M':
1750 case 'm':
1751 {
1752 if (LocaleNCompare(symbol,"maxima",6) == 0)
1753 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1754 if (LocaleNCompare(symbol,"mean",4) == 0)
1755 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1756 if (LocaleNCompare(symbol,"minima",6) == 0)
1757 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1758 if (LocaleCompare(symbol,"m") == 0)
1759 return(QuantumScale*pixel.blue);
1760 break;
1761 }
1762 case 'N':
1763 case 'n':
1764 {
1765 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001766 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001767 break;
1768 }
1769 case 'O':
1770 case 'o':
1771 {
1772 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001773 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001774 break;
1775 }
1776 case 'P':
1777 case 'p':
1778 {
1779 if (LocaleCompare(symbol,"page.height") == 0)
1780 return((MagickRealType) image->page.height);
1781 if (LocaleCompare(symbol,"page.width") == 0)
1782 return((MagickRealType) image->page.width);
1783 if (LocaleCompare(symbol,"page.x") == 0)
1784 return((MagickRealType) image->page.x);
1785 if (LocaleCompare(symbol,"page.y") == 0)
1786 return((MagickRealType) image->page.y);
1787 break;
1788 }
1789 case 'R':
1790 case 'r':
1791 {
1792 if (LocaleCompare(symbol,"resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001793 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001794 if (LocaleCompare(symbol,"resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001795 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001796 if (LocaleCompare(symbol,"r") == 0)
1797 return(QuantumScale*pixel.red);
1798 break;
1799 }
1800 case 'S':
1801 case 's':
1802 {
1803 if (LocaleCompare(symbol,"saturation") == 0)
1804 {
1805 double
1806 hue,
1807 lightness,
1808 saturation;
1809
cristyda1f9c12011-10-02 21:39:49 +00001810 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1811 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001812 return(saturation);
1813 }
1814 if (LocaleNCompare(symbol,"skewness",8) == 0)
1815 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1816 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1817 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1818 break;
1819 }
1820 case 'T':
1821 case 't':
1822 {
1823 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001824 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001825 break;
1826 }
1827 case 'W':
1828 case 'w':
1829 {
1830 if (LocaleCompare(symbol,"w") == 0)
1831 return((MagickRealType) image->columns);
1832 break;
1833 }
1834 case 'Y':
1835 case 'y':
1836 {
1837 if (LocaleCompare(symbol,"y") == 0)
1838 return(QuantumScale*pixel.green);
1839 break;
1840 }
1841 case 'Z':
1842 case 'z':
1843 {
1844 if (LocaleCompare(symbol,"z") == 0)
1845 {
1846 MagickRealType
1847 depth;
1848
cristyfefab1b2011-07-05 00:33:22 +00001849 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001850 return(depth);
1851 }
1852 break;
1853 }
1854 default:
1855 break;
1856 }
1857 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1858 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001859 return((MagickRealType) StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001860 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1861 "UnableToParseExpression","`%s'",symbol);
1862 return(0.0);
1863}
1864
1865static const char *FxOperatorPrecedence(const char *expression,
1866 ExceptionInfo *exception)
1867{
1868 typedef enum
1869 {
1870 UndefinedPrecedence,
1871 NullPrecedence,
1872 BitwiseComplementPrecedence,
1873 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001874 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001875 MultiplyPrecedence,
1876 AdditionPrecedence,
1877 ShiftPrecedence,
1878 RelationalPrecedence,
1879 EquivalencyPrecedence,
1880 BitwiseAndPrecedence,
1881 BitwiseOrPrecedence,
1882 LogicalAndPrecedence,
1883 LogicalOrPrecedence,
1884 TernaryPrecedence,
1885 AssignmentPrecedence,
1886 CommaPrecedence,
1887 SeparatorPrecedence
1888 } FxPrecedence;
1889
1890 FxPrecedence
1891 precedence,
1892 target;
1893
1894 register const char
1895 *subexpression;
1896
1897 register int
1898 c;
1899
cristybb503372010-05-27 20:51:26 +00001900 size_t
cristy3ed852e2009-09-05 21:47:34 +00001901 level;
1902
1903 c=0;
1904 level=0;
1905 subexpression=(const char *) NULL;
1906 target=NullPrecedence;
1907 while (*expression != '\0')
1908 {
1909 precedence=UndefinedPrecedence;
1910 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1911 {
1912 expression++;
1913 continue;
1914 }
cristy488fa882010-03-01 22:34:24 +00001915 switch (*expression)
1916 {
1917 case 'A':
1918 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001919 {
cristyb33454f2011-08-03 02:10:45 +00001920#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001921 if (LocaleNCompare(expression,"acosh",5) == 0)
1922 {
1923 expression+=5;
1924 break;
1925 }
cristyb33454f2011-08-03 02:10:45 +00001926#endif
1927#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001928 if (LocaleNCompare(expression,"asinh",5) == 0)
1929 {
1930 expression+=5;
1931 break;
1932 }
cristyb33454f2011-08-03 02:10:45 +00001933#endif
1934#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001935 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001936 {
1937 expression+=5;
1938 break;
1939 }
cristyb33454f2011-08-03 02:10:45 +00001940#endif
cristy488fa882010-03-01 22:34:24 +00001941 break;
cristy3ed852e2009-09-05 21:47:34 +00001942 }
cristy62d455f2011-11-03 11:42:28 +00001943 case 'E':
1944 case 'e':
1945 {
1946 if ((LocaleNCompare(expression,"E+",2) == 0) ||
1947 (LocaleNCompare(expression,"E-",2) == 0))
1948 {
1949 expression+=2; /* scientific notation */
1950 break;
1951 }
1952 }
cristy488fa882010-03-01 22:34:24 +00001953 case 'J':
1954 case 'j':
1955 {
1956 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1957 (LocaleNCompare(expression,"j1",2) == 0))
1958 {
1959 expression+=2;
1960 break;
1961 }
1962 break;
1963 }
cristy2def9322010-06-18 23:59:37 +00001964 case '#':
1965 {
1966 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1967 expression++;
1968 break;
1969 }
cristy488fa882010-03-01 22:34:24 +00001970 default:
1971 break;
1972 }
cristy3ed852e2009-09-05 21:47:34 +00001973 if ((c == (int) '{') || (c == (int) '['))
1974 level++;
1975 else
1976 if ((c == (int) '}') || (c == (int) ']'))
1977 level--;
1978 if (level == 0)
1979 switch ((unsigned char) *expression)
1980 {
1981 case '~':
1982 case '!':
1983 {
1984 precedence=BitwiseComplementPrecedence;
1985 break;
1986 }
1987 case '^':
cristy6621e252010-08-13 00:42:57 +00001988 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001989 {
1990 precedence=ExponentPrecedence;
1991 break;
1992 }
1993 default:
1994 {
1995 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1996 (strchr(")",c) != (char *) NULL))) &&
1997 (((islower((int) ((char) *expression)) != 0) ||
1998 (strchr("(",(int) *expression) != (char *) NULL)) ||
1999 ((isdigit((int) ((char) c)) == 0) &&
2000 (isdigit((int) ((char) *expression)) != 0))) &&
2001 (strchr("xy",(int) *expression) == (char *) NULL))
2002 precedence=MultiplyPrecedence;
2003 break;
2004 }
2005 case '*':
2006 case '/':
2007 case '%':
2008 {
2009 precedence=MultiplyPrecedence;
2010 break;
2011 }
2012 case '+':
2013 case '-':
2014 {
2015 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
2016 (isalpha(c) != 0))
2017 precedence=AdditionPrecedence;
2018 break;
2019 }
2020 case LeftShiftOperator:
2021 case RightShiftOperator:
2022 {
2023 precedence=ShiftPrecedence;
2024 break;
2025 }
2026 case '<':
2027 case LessThanEqualOperator:
2028 case GreaterThanEqualOperator:
2029 case '>':
2030 {
2031 precedence=RelationalPrecedence;
2032 break;
2033 }
2034 case EqualOperator:
2035 case NotEqualOperator:
2036 {
2037 precedence=EquivalencyPrecedence;
2038 break;
2039 }
2040 case '&':
2041 {
2042 precedence=BitwiseAndPrecedence;
2043 break;
2044 }
2045 case '|':
2046 {
2047 precedence=BitwiseOrPrecedence;
2048 break;
2049 }
2050 case LogicalAndOperator:
2051 {
2052 precedence=LogicalAndPrecedence;
2053 break;
2054 }
2055 case LogicalOrOperator:
2056 {
2057 precedence=LogicalOrPrecedence;
2058 break;
2059 }
cristy116af162010-08-13 01:25:47 +00002060 case ExponentialNotation:
2061 {
2062 precedence=ExponentialNotationPrecedence;
2063 break;
2064 }
cristy3ed852e2009-09-05 21:47:34 +00002065 case ':':
2066 case '?':
2067 {
2068 precedence=TernaryPrecedence;
2069 break;
2070 }
2071 case '=':
2072 {
2073 precedence=AssignmentPrecedence;
2074 break;
2075 }
2076 case ',':
2077 {
2078 precedence=CommaPrecedence;
2079 break;
2080 }
2081 case ';':
2082 {
2083 precedence=SeparatorPrecedence;
2084 break;
2085 }
2086 }
2087 if ((precedence == BitwiseComplementPrecedence) ||
2088 (precedence == TernaryPrecedence) ||
2089 (precedence == AssignmentPrecedence))
2090 {
2091 if (precedence > target)
2092 {
2093 /*
2094 Right-to-left associativity.
2095 */
2096 target=precedence;
2097 subexpression=expression;
2098 }
2099 }
2100 else
2101 if (precedence >= target)
2102 {
2103 /*
2104 Left-to-right associativity.
2105 */
2106 target=precedence;
2107 subexpression=expression;
2108 }
2109 if (strchr("(",(int) *expression) != (char *) NULL)
2110 expression=FxSubexpression(expression,exception);
2111 c=(int) (*expression++);
2112 }
2113 return(subexpression);
2114}
2115
2116static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002117 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002118 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002119{
2120 char
2121 *q,
2122 subexpression[MaxTextExtent];
2123
2124 MagickRealType
2125 alpha,
2126 gamma;
2127
2128 register const char
2129 *p;
2130
2131 *beta=0.0;
2132 if (exception->severity != UndefinedException)
2133 return(0.0);
2134 while (isspace((int) *expression) != 0)
2135 expression++;
2136 if (*expression == '\0')
2137 {
2138 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2139 "MissingExpression","`%s'",expression);
2140 return(0.0);
2141 }
cristy66322f02010-05-17 11:40:48 +00002142 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002143 p=FxOperatorPrecedence(expression,exception);
2144 if (p != (const char *) NULL)
2145 {
2146 (void) CopyMagickString(subexpression,expression,(size_t)
2147 (p-expression+1));
2148 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2149 exception);
2150 switch ((unsigned char) *p)
2151 {
2152 case '~':
2153 {
2154 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002155 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002156 return(*beta);
2157 }
2158 case '!':
2159 {
2160 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2161 return(*beta == 0.0 ? 1.0 : 0.0);
2162 }
2163 case '^':
2164 {
2165 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2166 channel,x,y,++p,beta,exception));
2167 return(*beta);
2168 }
2169 case '*':
cristy116af162010-08-13 01:25:47 +00002170 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002171 {
2172 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2173 return(alpha*(*beta));
2174 }
2175 case '/':
2176 {
2177 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2178 if (*beta == 0.0)
2179 {
2180 if (exception->severity == UndefinedException)
2181 (void) ThrowMagickException(exception,GetMagickModule(),
2182 OptionError,"DivideByZero","`%s'",expression);
2183 return(0.0);
2184 }
2185 return(alpha/(*beta));
2186 }
2187 case '%':
2188 {
2189 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2190 *beta=fabs(floor(((double) *beta)+0.5));
2191 if (*beta == 0.0)
2192 {
2193 (void) ThrowMagickException(exception,GetMagickModule(),
2194 OptionError,"DivideByZero","`%s'",expression);
2195 return(0.0);
2196 }
2197 return(fmod((double) alpha,(double) *beta));
2198 }
2199 case '+':
2200 {
2201 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2202 return(alpha+(*beta));
2203 }
2204 case '-':
2205 {
2206 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2207 return(alpha-(*beta));
2208 }
2209 case LeftShiftOperator:
2210 {
2211 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002212 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002213 return(*beta);
2214 }
2215 case RightShiftOperator:
2216 {
2217 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002218 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002219 return(*beta);
2220 }
2221 case '<':
2222 {
2223 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2224 return(alpha < *beta ? 1.0 : 0.0);
2225 }
2226 case LessThanEqualOperator:
2227 {
2228 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2229 return(alpha <= *beta ? 1.0 : 0.0);
2230 }
2231 case '>':
2232 {
2233 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2234 return(alpha > *beta ? 1.0 : 0.0);
2235 }
2236 case GreaterThanEqualOperator:
2237 {
2238 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2239 return(alpha >= *beta ? 1.0 : 0.0);
2240 }
2241 case EqualOperator:
2242 {
2243 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2244 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2245 }
2246 case NotEqualOperator:
2247 {
2248 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2249 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2250 }
2251 case '&':
2252 {
2253 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002254 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002255 return(*beta);
2256 }
2257 case '|':
2258 {
2259 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002260 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002261 return(*beta);
2262 }
2263 case LogicalAndOperator:
2264 {
2265 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2266 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2267 return(*beta);
2268 }
2269 case LogicalOrOperator:
2270 {
2271 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2272 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2273 return(*beta);
2274 }
2275 case '?':
2276 {
2277 MagickRealType
2278 gamma;
2279
2280 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2281 q=subexpression;
2282 p=StringToken(":",&q);
2283 if (q == (char *) NULL)
2284 {
2285 (void) ThrowMagickException(exception,GetMagickModule(),
2286 OptionError,"UnableToParseExpression","`%s'",subexpression);
2287 return(0.0);
2288 }
2289 if (fabs((double) alpha) > MagickEpsilon)
2290 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2291 else
2292 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2293 return(gamma);
2294 }
2295 case '=':
2296 {
2297 char
2298 numeric[MaxTextExtent];
2299
2300 q=subexpression;
2301 while (isalpha((int) ((unsigned char) *q)) != 0)
2302 q++;
2303 if (*q != '\0')
2304 {
2305 (void) ThrowMagickException(exception,GetMagickModule(),
2306 OptionError,"UnableToParseExpression","`%s'",subexpression);
2307 return(0.0);
2308 }
2309 ClearMagickException(exception);
2310 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002311 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002312 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002313 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2314 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2315 subexpression),ConstantString(numeric));
2316 return(*beta);
2317 }
2318 case ',':
2319 {
2320 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2321 return(alpha);
2322 }
2323 case ';':
2324 {
2325 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2326 return(*beta);
2327 }
2328 default:
2329 {
2330 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2331 exception);
2332 return(gamma);
2333 }
2334 }
2335 }
2336 if (strchr("(",(int) *expression) != (char *) NULL)
2337 {
2338 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2339 subexpression[strlen(subexpression)-1]='\0';
2340 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2341 exception);
2342 return(gamma);
2343 }
cristy8b8a3ae2010-10-23 18:49:46 +00002344 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002345 {
2346 case '+':
2347 {
2348 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2349 exception);
2350 return(1.0*gamma);
2351 }
2352 case '-':
2353 {
2354 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2355 exception);
2356 return(-1.0*gamma);
2357 }
2358 case '~':
2359 {
2360 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2361 exception);
cristybb503372010-05-27 20:51:26 +00002362 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002363 }
2364 case 'A':
2365 case 'a':
2366 {
2367 if (LocaleNCompare(expression,"abs",3) == 0)
2368 {
2369 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2370 exception);
2371 return((MagickRealType) fabs((double) alpha));
2372 }
cristyb33454f2011-08-03 02:10:45 +00002373#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002374 if (LocaleNCompare(expression,"acosh",5) == 0)
2375 {
2376 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2377 exception);
2378 return((MagickRealType) acosh((double) alpha));
2379 }
cristyb33454f2011-08-03 02:10:45 +00002380#endif
cristy3ed852e2009-09-05 21:47:34 +00002381 if (LocaleNCompare(expression,"acos",4) == 0)
2382 {
2383 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2384 exception);
2385 return((MagickRealType) acos((double) alpha));
2386 }
cristy43c22f42010-03-30 12:34:07 +00002387#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002388 if (LocaleNCompare(expression,"airy",4) == 0)
2389 {
2390 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2391 exception);
2392 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002393 return(1.0);
2394 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002395 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002396 }
cristy43c22f42010-03-30 12:34:07 +00002397#endif
cristyb33454f2011-08-03 02:10:45 +00002398#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002399 if (LocaleNCompare(expression,"asinh",5) == 0)
2400 {
2401 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2402 exception);
2403 return((MagickRealType) asinh((double) alpha));
2404 }
cristyb33454f2011-08-03 02:10:45 +00002405#endif
cristy3ed852e2009-09-05 21:47:34 +00002406 if (LocaleNCompare(expression,"asin",4) == 0)
2407 {
2408 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2409 exception);
2410 return((MagickRealType) asin((double) alpha));
2411 }
2412 if (LocaleNCompare(expression,"alt",3) == 0)
2413 {
2414 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2415 exception);
cristybb503372010-05-27 20:51:26 +00002416 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002417 }
2418 if (LocaleNCompare(expression,"atan2",5) == 0)
2419 {
2420 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2421 exception);
2422 return((MagickRealType) atan2((double) alpha,(double) *beta));
2423 }
cristyb33454f2011-08-03 02:10:45 +00002424#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002425 if (LocaleNCompare(expression,"atanh",5) == 0)
2426 {
2427 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2428 exception);
2429 return((MagickRealType) atanh((double) alpha));
2430 }
cristyb33454f2011-08-03 02:10:45 +00002431#endif
cristy3ed852e2009-09-05 21:47:34 +00002432 if (LocaleNCompare(expression,"atan",4) == 0)
2433 {
2434 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2435 exception);
2436 return((MagickRealType) atan((double) alpha));
2437 }
2438 if (LocaleCompare(expression,"a") == 0)
2439 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2440 break;
2441 }
2442 case 'B':
2443 case 'b':
2444 {
2445 if (LocaleCompare(expression,"b") == 0)
2446 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2447 break;
2448 }
2449 case 'C':
2450 case 'c':
2451 {
2452 if (LocaleNCompare(expression,"ceil",4) == 0)
2453 {
2454 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2455 exception);
2456 return((MagickRealType) ceil((double) alpha));
2457 }
2458 if (LocaleNCompare(expression,"cosh",4) == 0)
2459 {
2460 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2461 exception);
2462 return((MagickRealType) cosh((double) alpha));
2463 }
2464 if (LocaleNCompare(expression,"cos",3) == 0)
2465 {
2466 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2467 exception);
2468 return((MagickRealType) cos((double) alpha));
2469 }
2470 if (LocaleCompare(expression,"c") == 0)
2471 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2472 break;
2473 }
2474 case 'D':
2475 case 'd':
2476 {
2477 if (LocaleNCompare(expression,"debug",5) == 0)
2478 {
2479 const char
2480 *type;
2481
2482 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2483 exception);
2484 if (fx_info->images->colorspace == CMYKColorspace)
2485 switch (channel)
2486 {
cristy0568ffc2011-07-25 16:54:14 +00002487 case CyanPixelChannel: type="cyan"; break;
2488 case MagentaPixelChannel: type="magenta"; break;
2489 case YellowPixelChannel: type="yellow"; break;
2490 case AlphaPixelChannel: type="opacity"; break;
2491 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002492 default: type="unknown"; break;
2493 }
2494 else
2495 switch (channel)
2496 {
cristy0568ffc2011-07-25 16:54:14 +00002497 case RedPixelChannel: type="red"; break;
2498 case GreenPixelChannel: type="green"; break;
2499 case BluePixelChannel: type="blue"; break;
2500 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002501 default: type="unknown"; break;
2502 }
2503 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2504 if (strlen(subexpression) > 1)
2505 subexpression[strlen(subexpression)-1]='\0';
2506 if (fx_info->file != (FILE *) NULL)
cristy1707c6c2012-01-18 23:30:54 +00002507 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2508 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2509 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002510 return(0.0);
2511 }
cristy5597a8d2011-11-04 00:25:32 +00002512 if (LocaleNCompare(expression,"drc",3) == 0)
2513 {
2514 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2515 exception);
2516 return((MagickRealType) (alpha/(*beta*(alpha-1.0)+1.0)));
2517 }
cristy3ed852e2009-09-05 21:47:34 +00002518 break;
2519 }
2520 case 'E':
2521 case 'e':
2522 {
2523 if (LocaleCompare(expression,"epsilon") == 0)
2524 return((MagickRealType) MagickEpsilon);
2525 if (LocaleNCompare(expression,"exp",3) == 0)
2526 {
2527 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2528 exception);
2529 return((MagickRealType) exp((double) alpha));
2530 }
2531 if (LocaleCompare(expression,"e") == 0)
2532 return((MagickRealType) 2.7182818284590452354);
2533 break;
2534 }
2535 case 'F':
2536 case 'f':
2537 {
2538 if (LocaleNCompare(expression,"floor",5) == 0)
2539 {
2540 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2541 exception);
2542 return((MagickRealType) floor((double) alpha));
2543 }
2544 break;
2545 }
2546 case 'G':
2547 case 'g':
2548 {
cristy9eeedea2011-11-02 19:04:05 +00002549 if (LocaleNCompare(expression,"gauss",5) == 0)
2550 {
2551 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2552 exception);
2553 gamma=exp((double) (-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2554 return((MagickRealType) gamma);
2555 }
cristyb0aad4c2011-11-02 19:30:35 +00002556 if (LocaleNCompare(expression,"gcd",3) == 0)
2557 {
2558 MagickOffsetType
2559 gcd;
2560
2561 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2562 exception);
cristy1707c6c2012-01-18 23:30:54 +00002563 gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2564 0.5));
cristyb0aad4c2011-11-02 19:30:35 +00002565 return((MagickRealType) gcd);
2566 }
cristy3ed852e2009-09-05 21:47:34 +00002567 if (LocaleCompare(expression,"g") == 0)
2568 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2569 break;
2570 }
2571 case 'H':
2572 case 'h':
2573 {
2574 if (LocaleCompare(expression,"h") == 0)
2575 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2576 if (LocaleCompare(expression,"hue") == 0)
2577 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2578 if (LocaleNCompare(expression,"hypot",5) == 0)
2579 {
2580 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2581 exception);
2582 return((MagickRealType) hypot((double) alpha,(double) *beta));
2583 }
2584 break;
2585 }
2586 case 'K':
2587 case 'k':
2588 {
2589 if (LocaleCompare(expression,"k") == 0)
2590 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2591 break;
2592 }
2593 case 'I':
2594 case 'i':
2595 {
2596 if (LocaleCompare(expression,"intensity") == 0)
2597 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2598 if (LocaleNCompare(expression,"int",3) == 0)
2599 {
2600 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2601 exception);
cristy16788e42010-08-13 13:44:26 +00002602 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002603 }
cristy82b20722011-11-05 21:52:36 +00002604#if defined(MAGICKCORE_HAVE_ISNAN)
cristy639399c2011-11-02 19:16:15 +00002605 if (LocaleNCompare(expression,"isnan",5) == 0)
2606 {
2607 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2608 exception);
cristy17a10202011-11-02 19:17:04 +00002609 return((MagickRealType) !!isnan((double) alpha));
cristy639399c2011-11-02 19:16:15 +00002610 }
cristy82b20722011-11-05 21:52:36 +00002611#endif
cristy3ed852e2009-09-05 21:47:34 +00002612 if (LocaleCompare(expression,"i") == 0)
2613 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2614 break;
2615 }
2616 case 'J':
2617 case 'j':
2618 {
2619 if (LocaleCompare(expression,"j") == 0)
2620 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002621#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002622 if (LocaleNCompare(expression,"j0",2) == 0)
2623 {
2624 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2625 exception);
2626 return((MagickRealType) j0((double) alpha));
2627 }
cristy161b9262010-03-20 19:34:32 +00002628#endif
2629#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002630 if (LocaleNCompare(expression,"j1",2) == 0)
2631 {
2632 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2633 exception);
2634 return((MagickRealType) j1((double) alpha));
2635 }
cristy161b9262010-03-20 19:34:32 +00002636#endif
cristyaa018fa2010-04-08 23:03:54 +00002637#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002638 if (LocaleNCompare(expression,"jinc",4) == 0)
2639 {
2640 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2641 exception);
cristy0946a822010-03-12 17:14:58 +00002642 if (alpha == 0.0)
2643 return(1.0);
cristy1707c6c2012-01-18 23:30:54 +00002644 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/(MagickPI*
2645 alpha));
cristyfce2f7b2010-03-12 00:29:49 +00002646 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002647 }
cristyaa018fa2010-04-08 23:03:54 +00002648#endif
cristy3ed852e2009-09-05 21:47:34 +00002649 break;
2650 }
2651 case 'L':
2652 case 'l':
2653 {
2654 if (LocaleNCompare(expression,"ln",2) == 0)
2655 {
2656 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2657 exception);
2658 return((MagickRealType) log((double) alpha));
2659 }
cristyc8ed5322010-08-31 12:07:59 +00002660 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002661 {
cristyc8ed5322010-08-31 12:07:59 +00002662 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002663 exception);
2664 return((MagickRealType) log10((double) alpha))/log10(2.0);
2665 }
2666 if (LocaleNCompare(expression,"log",3) == 0)
2667 {
2668 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2669 exception);
2670 return((MagickRealType) log10((double) alpha));
2671 }
2672 if (LocaleCompare(expression,"lightness") == 0)
2673 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2674 break;
2675 }
2676 case 'M':
2677 case 'm':
2678 {
2679 if (LocaleCompare(expression,"MaxRGB") == 0)
2680 return((MagickRealType) QuantumRange);
2681 if (LocaleNCompare(expression,"maxima",6) == 0)
2682 break;
2683 if (LocaleNCompare(expression,"max",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002684 {
2685 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2686 exception);
2687 return(alpha > *beta ? alpha : *beta);
2688 }
cristy3ed852e2009-09-05 21:47:34 +00002689 if (LocaleNCompare(expression,"minima",6) == 0)
2690 break;
2691 if (LocaleNCompare(expression,"min",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002692 {
2693 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2694 exception);
2695 return(alpha < *beta ? alpha : *beta);
2696 }
cristy3ed852e2009-09-05 21:47:34 +00002697 if (LocaleNCompare(expression,"mod",3) == 0)
2698 {
2699 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2700 exception);
cristy984049c2011-11-03 18:34:58 +00002701 gamma=alpha-floor((double) (alpha/(*beta)))*(*beta);
2702 return(gamma);
cristy3ed852e2009-09-05 21:47:34 +00002703 }
2704 if (LocaleCompare(expression,"m") == 0)
2705 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2706 break;
2707 }
2708 case 'N':
2709 case 'n':
2710 {
cristyad3502e2011-11-02 19:10:45 +00002711 if (LocaleNCompare(expression,"not",3) == 0)
2712 {
2713 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2714 exception);
2715 return((MagickRealType) (alpha < MagickEpsilon));
2716 }
cristy3ed852e2009-09-05 21:47:34 +00002717 if (LocaleCompare(expression,"n") == 0)
2718 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2719 break;
2720 }
2721 case 'O':
2722 case 'o':
2723 {
2724 if (LocaleCompare(expression,"Opaque") == 0)
2725 return(1.0);
2726 if (LocaleCompare(expression,"o") == 0)
2727 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2728 break;
2729 }
2730 case 'P':
2731 case 'p':
2732 {
cristy670aa3c2011-11-03 00:54:00 +00002733 if (LocaleCompare(expression,"phi") == 0)
2734 return((MagickRealType) MagickPHI);
cristy3ed852e2009-09-05 21:47:34 +00002735 if (LocaleCompare(expression,"pi") == 0)
2736 return((MagickRealType) MagickPI);
2737 if (LocaleNCompare(expression,"pow",3) == 0)
2738 {
2739 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2740 exception);
2741 return((MagickRealType) pow((double) alpha,(double) *beta));
2742 }
2743 if (LocaleCompare(expression,"p") == 0)
2744 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2745 break;
2746 }
2747 case 'Q':
2748 case 'q':
2749 {
2750 if (LocaleCompare(expression,"QuantumRange") == 0)
2751 return((MagickRealType) QuantumRange);
2752 if (LocaleCompare(expression,"QuantumScale") == 0)
2753 return((MagickRealType) QuantumScale);
2754 break;
2755 }
2756 case 'R':
2757 case 'r':
2758 {
2759 if (LocaleNCompare(expression,"rand",4) == 0)
2760 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2761 if (LocaleNCompare(expression,"round",5) == 0)
2762 {
2763 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2764 exception);
cristy16788e42010-08-13 13:44:26 +00002765 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002766 }
2767 if (LocaleCompare(expression,"r") == 0)
2768 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2769 break;
2770 }
2771 case 'S':
2772 case 's':
2773 {
2774 if (LocaleCompare(expression,"saturation") == 0)
2775 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2776 if (LocaleNCompare(expression,"sign",4) == 0)
2777 {
2778 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2779 exception);
2780 return(alpha < 0.0 ? -1.0 : 1.0);
2781 }
cristya6a09e72010-03-02 14:51:02 +00002782 if (LocaleNCompare(expression,"sinc",4) == 0)
2783 {
2784 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2785 exception);
2786 if (alpha == 0)
2787 return(1.0);
2788 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2789 (MagickPI*alpha));
2790 return(gamma);
2791 }
cristy3ed852e2009-09-05 21:47:34 +00002792 if (LocaleNCompare(expression,"sinh",4) == 0)
2793 {
2794 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2795 exception);
2796 return((MagickRealType) sinh((double) alpha));
2797 }
2798 if (LocaleNCompare(expression,"sin",3) == 0)
2799 {
2800 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2801 exception);
2802 return((MagickRealType) sin((double) alpha));
2803 }
2804 if (LocaleNCompare(expression,"sqrt",4) == 0)
2805 {
2806 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2807 exception);
2808 return((MagickRealType) sqrt((double) alpha));
2809 }
cristy9eeedea2011-11-02 19:04:05 +00002810 if (LocaleNCompare(expression,"squish",6) == 0)
2811 {
2812 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2813 exception);
2814 return((MagickRealType) (1.0/(1.0+exp((double) (4.0*alpha)))));
2815 }
cristy3ed852e2009-09-05 21:47:34 +00002816 if (LocaleCompare(expression,"s") == 0)
2817 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2818 break;
2819 }
2820 case 'T':
2821 case 't':
2822 {
2823 if (LocaleNCompare(expression,"tanh",4) == 0)
2824 {
2825 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2826 exception);
2827 return((MagickRealType) tanh((double) alpha));
2828 }
2829 if (LocaleNCompare(expression,"tan",3) == 0)
2830 {
2831 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2832 exception);
2833 return((MagickRealType) tan((double) alpha));
2834 }
2835 if (LocaleCompare(expression,"Transparent") == 0)
2836 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002837 if (LocaleNCompare(expression,"trunc",5) == 0)
2838 {
2839 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2840 exception);
2841 if (alpha >= 0.0)
2842 return((MagickRealType) floor((double) alpha));
2843 return((MagickRealType) ceil((double) alpha));
2844 }
cristy3ed852e2009-09-05 21:47:34 +00002845 if (LocaleCompare(expression,"t") == 0)
2846 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2847 break;
2848 }
2849 case 'U':
2850 case 'u':
2851 {
2852 if (LocaleCompare(expression,"u") == 0)
2853 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2854 break;
2855 }
2856 case 'V':
2857 case 'v':
2858 {
2859 if (LocaleCompare(expression,"v") == 0)
2860 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2861 break;
2862 }
2863 case 'W':
2864 case 'w':
2865 {
cristy9eeedea2011-11-02 19:04:05 +00002866 if (LocaleNCompare(expression,"while",5) == 0)
2867 {
2868 do
2869 {
2870 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2871 exception);
2872 } while (fabs((double) alpha) >= MagickEpsilon);
2873 return((MagickRealType) *beta);
2874 }
cristy3ed852e2009-09-05 21:47:34 +00002875 if (LocaleCompare(expression,"w") == 0)
2876 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2877 break;
2878 }
2879 case 'Y':
2880 case 'y':
2881 {
2882 if (LocaleCompare(expression,"y") == 0)
2883 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2884 break;
2885 }
2886 case 'Z':
2887 case 'z':
2888 {
2889 if (LocaleCompare(expression,"z") == 0)
2890 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2891 break;
2892 }
2893 default:
2894 break;
2895 }
2896 q=(char *) expression;
cristydbdd0e32011-11-04 23:29:40 +00002897 alpha=InterpretSiPrefixValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002898 if (q == expression)
2899 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2900 return(alpha);
2901}
2902
cristy7832dc22011-09-05 01:21:53 +00002903MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
cristy3ed852e2009-09-05 21:47:34 +00002904 MagickRealType *alpha,ExceptionInfo *exception)
2905{
2906 MagickBooleanType
2907 status;
2908
cristy541ae572011-08-05 19:08:59 +00002909 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2910 exception);
cristy3ed852e2009-09-05 21:47:34 +00002911 return(status);
2912}
2913
2914MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2915 MagickRealType *alpha,ExceptionInfo *exception)
2916{
2917 FILE
2918 *file;
2919
2920 MagickBooleanType
2921 status;
2922
2923 file=fx_info->file;
2924 fx_info->file=(FILE *) NULL;
cristy541ae572011-08-05 19:08:59 +00002925 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2926 exception);
cristy3ed852e2009-09-05 21:47:34 +00002927 fx_info->file=file;
2928 return(status);
2929}
2930
cristy7832dc22011-09-05 01:21:53 +00002931MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002932 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002933 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002934{
2935 MagickRealType
2936 beta;
2937
2938 beta=0.0;
2939 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2940 exception);
2941 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2942}
2943
2944/*
2945%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2946% %
2947% %
2948% %
2949% F x I m a g e %
2950% %
2951% %
2952% %
2953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2954%
2955% FxImage() applies a mathematical expression to the specified image.
2956%
2957% The format of the FxImage method is:
2958%
2959% Image *FxImage(const Image *image,const char *expression,
2960% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002961%
2962% A description of each parameter follows:
2963%
2964% o image: the image.
2965%
cristy3ed852e2009-09-05 21:47:34 +00002966% o expression: A mathematical expression.
2967%
2968% o exception: return any errors or warnings in this structure.
2969%
2970*/
2971
2972static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2973{
cristybb503372010-05-27 20:51:26 +00002974 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002975 i;
2976
2977 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002978 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002979 if (fx_info[i] != (FxInfo *) NULL)
2980 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002981 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002982 return(fx_info);
2983}
2984
2985static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2986 ExceptionInfo *exception)
2987{
2988 char
2989 *fx_expression;
2990
2991 FxInfo
2992 **fx_info;
2993
2994 MagickRealType
2995 alpha;
2996
cristybb503372010-05-27 20:51:26 +00002997 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002998 i;
2999
cristybb503372010-05-27 20:51:26 +00003000 size_t
cristy3ed852e2009-09-05 21:47:34 +00003001 number_threads;
3002
3003 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00003004 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00003005 if (fx_info == (FxInfo **) NULL)
3006 return((FxInfo **) NULL);
3007 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
3008 if (*expression != '@')
3009 fx_expression=ConstantString(expression);
3010 else
3011 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00003012 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00003013 {
3014 fx_info[i]=AcquireFxInfo(image,fx_expression);
3015 if (fx_info[i] == (FxInfo *) NULL)
3016 return(DestroyFxThreadSet(fx_info));
3017 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
3018 }
3019 fx_expression=DestroyString(fx_expression);
3020 return(fx_info);
3021}
3022
3023MagickExport Image *FxImage(const Image *image,const char *expression,
3024 ExceptionInfo *exception)
3025{
cristy3ed852e2009-09-05 21:47:34 +00003026#define FxImageTag "Fx/Image"
3027
cristyfa112112010-01-04 17:48:07 +00003028 CacheView
cristy79cedc72011-07-25 00:41:15 +00003029 *fx_view,
3030 *image_view;
cristyfa112112010-01-04 17:48:07 +00003031
cristy3ed852e2009-09-05 21:47:34 +00003032 FxInfo
cristyfa112112010-01-04 17:48:07 +00003033 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00003034
3035 Image
3036 *fx_image;
3037
cristy3ed852e2009-09-05 21:47:34 +00003038 MagickBooleanType
3039 status;
3040
cristybb503372010-05-27 20:51:26 +00003041 MagickOffsetType
3042 progress;
3043
cristy3ed852e2009-09-05 21:47:34 +00003044 MagickRealType
3045 alpha;
3046
cristybb503372010-05-27 20:51:26 +00003047 ssize_t
3048 y;
3049
cristy3ed852e2009-09-05 21:47:34 +00003050 assert(image != (Image *) NULL);
3051 assert(image->signature == MagickSignature);
3052 if (image->debug != MagickFalse)
3053 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00003054 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003055 if (fx_image == (Image *) NULL)
3056 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003057 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003058 {
cristy3ed852e2009-09-05 21:47:34 +00003059 fx_image=DestroyImage(fx_image);
3060 return((Image *) NULL);
3061 }
3062 fx_info=AcquireFxThreadSet(image,expression,exception);
3063 if (fx_info == (FxInfo **) NULL)
3064 {
3065 fx_image=DestroyImage(fx_image);
3066 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3067 }
3068 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3069 if (status == MagickFalse)
3070 {
3071 fx_image=DestroyImage(fx_image);
3072 fx_info=DestroyFxThreadSet(fx_info);
3073 return((Image *) NULL);
3074 }
3075 /*
3076 Fx image.
3077 */
3078 status=MagickTrue;
3079 progress=0;
cristy79cedc72011-07-25 00:41:15 +00003080 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003081 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003082#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003083 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003084#endif
cristybb503372010-05-27 20:51:26 +00003085 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003086 {
cristy5c9e6f22010-09-17 17:31:01 +00003087 const int
3088 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003089
cristy79cedc72011-07-25 00:41:15 +00003090 register const Quantum
3091 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003092
cristy4c08aed2011-07-01 19:47:50 +00003093 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003094 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003095
cristy79cedc72011-07-25 00:41:15 +00003096 register ssize_t
3097 x;
3098
cristy3ed852e2009-09-05 21:47:34 +00003099 if (status == MagickFalse)
3100 continue;
cristy79cedc72011-07-25 00:41:15 +00003101 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003102 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003103 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003104 {
3105 status=MagickFalse;
3106 continue;
3107 }
cristybb503372010-05-27 20:51:26 +00003108 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003109 {
cristy79cedc72011-07-25 00:41:15 +00003110 register ssize_t
3111 i;
3112
cristy10a6c612012-01-29 21:41:05 +00003113 if (GetPixelMask(image,p) != 0)
3114 {
3115 p+=GetPixelChannels(image);
3116 q+=GetPixelChannels(fx_image);
3117 continue;
3118 }
cristy79cedc72011-07-25 00:41:15 +00003119 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3120 {
3121 MagickRealType
3122 alpha;
3123
3124 PixelChannel
3125 channel;
3126
3127 PixelTrait
3128 fx_traits,
3129 traits;
3130
cristye2a912b2011-12-05 20:02:07 +00003131 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003132 traits=GetPixelChannelMapTraits(image,channel);
cristy79cedc72011-07-25 00:41:15 +00003133 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
cristy010d7d12011-08-31 01:02:48 +00003134 if ((traits == UndefinedPixelTrait) ||
3135 (fx_traits == UndefinedPixelTrait))
cristy79cedc72011-07-25 00:41:15 +00003136 continue;
3137 if ((fx_traits & CopyPixelTrait) != 0)
3138 {
cristy0beccfa2011-09-25 20:47:53 +00003139 SetPixelChannel(fx_image,channel,p[i],q);
cristy79cedc72011-07-25 00:41:15 +00003140 continue;
3141 }
3142 alpha=0.0;
cristya382aca2011-12-06 18:22:48 +00003143 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3144 exception);
cristyb3a73b52011-07-26 01:34:43 +00003145 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003146 }
3147 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003148 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003149 }
3150 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3151 status=MagickFalse;
3152 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3153 {
3154 MagickBooleanType
3155 proceed;
3156
cristyb5d5f722009-11-04 03:03:49 +00003157#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003158 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003159#endif
3160 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3161 if (proceed == MagickFalse)
3162 status=MagickFalse;
3163 }
3164 }
cristy3ed852e2009-09-05 21:47:34 +00003165 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003166 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003167 fx_info=DestroyFxThreadSet(fx_info);
3168 if (status == MagickFalse)
3169 fx_image=DestroyImage(fx_image);
3170 return(fx_image);
3171}
3172
3173/*
3174%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3175% %
3176% %
3177% %
3178% I m p l o d e I m a g e %
3179% %
3180% %
3181% %
3182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3183%
3184% ImplodeImage() creates a new image that is a copy of an existing
3185% one with the image pixels "implode" by the specified percentage. It
3186% allocates the memory necessary for the new Image structure and returns a
3187% pointer to the new image.
3188%
3189% The format of the ImplodeImage method is:
3190%
3191% Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003192% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003193%
3194% A description of each parameter follows:
3195%
3196% o implode_image: Method ImplodeImage returns a pointer to the image
3197% after it is implode. A null image is returned if there is a memory
3198% shortage.
3199%
3200% o image: the image.
3201%
3202% o amount: Define the extent of the implosion.
3203%
cristy76f512e2011-09-12 01:26:56 +00003204% o method: the pixel interpolation method.
3205%
cristy3ed852e2009-09-05 21:47:34 +00003206% o exception: return any errors or warnings in this structure.
3207%
3208*/
3209MagickExport Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003210 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003211{
3212#define ImplodeImageTag "Implode/Image"
3213
cristyfa112112010-01-04 17:48:07 +00003214 CacheView
3215 *image_view,
3216 *implode_view;
3217
cristy3ed852e2009-09-05 21:47:34 +00003218 Image
3219 *implode_image;
3220
cristy3ed852e2009-09-05 21:47:34 +00003221 MagickBooleanType
3222 status;
3223
cristybb503372010-05-27 20:51:26 +00003224 MagickOffsetType
3225 progress;
3226
cristy3ed852e2009-09-05 21:47:34 +00003227 MagickRealType
3228 radius;
3229
3230 PointInfo
3231 center,
3232 scale;
3233
cristybb503372010-05-27 20:51:26 +00003234 ssize_t
3235 y;
3236
cristy3ed852e2009-09-05 21:47:34 +00003237 /*
3238 Initialize implode image attributes.
3239 */
3240 assert(image != (Image *) NULL);
3241 assert(image->signature == MagickSignature);
3242 if (image->debug != MagickFalse)
3243 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3244 assert(exception != (ExceptionInfo *) NULL);
3245 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00003246 implode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3247 exception);
cristy3ed852e2009-09-05 21:47:34 +00003248 if (implode_image == (Image *) NULL)
3249 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003250 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003251 {
cristy3ed852e2009-09-05 21:47:34 +00003252 implode_image=DestroyImage(implode_image);
3253 return((Image *) NULL);
3254 }
cristy4c08aed2011-07-01 19:47:50 +00003255 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003256 implode_image->matte=MagickTrue;
3257 /*
3258 Compute scaling factor.
3259 */
3260 scale.x=1.0;
3261 scale.y=1.0;
3262 center.x=0.5*image->columns;
3263 center.y=0.5*image->rows;
3264 radius=center.x;
3265 if (image->columns > image->rows)
3266 scale.y=(double) image->columns/(double) image->rows;
3267 else
3268 if (image->columns < image->rows)
3269 {
3270 scale.x=(double) image->rows/(double) image->columns;
3271 radius=center.y;
3272 }
3273 /*
3274 Implode image.
3275 */
3276 status=MagickTrue;
3277 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003278 image_view=AcquireCacheView(image);
3279 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003280#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003281 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003282#endif
cristybb503372010-05-27 20:51:26 +00003283 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003284 {
cristy3ed852e2009-09-05 21:47:34 +00003285 MagickRealType
3286 distance;
3287
3288 PointInfo
3289 delta;
3290
cristy6d188022011-09-12 13:23:33 +00003291 register const Quantum
3292 *restrict p;
3293
cristybb503372010-05-27 20:51:26 +00003294 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003295 x;
3296
cristy4c08aed2011-07-01 19:47:50 +00003297 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003298 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003299
3300 if (status == MagickFalse)
3301 continue;
cristy6d188022011-09-12 13:23:33 +00003302 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003303 q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003304 exception);
cristy6d188022011-09-12 13:23:33 +00003305 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003306 {
3307 status=MagickFalse;
3308 continue;
3309 }
cristy3ed852e2009-09-05 21:47:34 +00003310 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00003311 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003312 {
cristy6d188022011-09-12 13:23:33 +00003313 register ssize_t
3314 i;
3315
cristy3ed852e2009-09-05 21:47:34 +00003316 /*
3317 Determine if the pixel is within an ellipse.
3318 */
cristy10a6c612012-01-29 21:41:05 +00003319 if (GetPixelMask(image,p) != 0)
3320 {
3321 p+=GetPixelChannels(image);
3322 q+=GetPixelChannels(implode_image);
3323 continue;
3324 }
cristy3ed852e2009-09-05 21:47:34 +00003325 delta.x=scale.x*(double) (x-center.x);
3326 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00003327 if (distance >= (radius*radius))
cristya6d7a9b2012-01-18 20:04:48 +00003328 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy6d188022011-09-12 13:23:33 +00003329 {
cristya6d7a9b2012-01-18 20:04:48 +00003330 PixelChannel
3331 channel;
3332
cristy1707c6c2012-01-18 23:30:54 +00003333 PixelTrait
3334 implode_traits,
3335 traits;
3336
cristya6d7a9b2012-01-18 20:04:48 +00003337 channel=GetPixelChannelMapChannel(image,i);
cristy1707c6c2012-01-18 23:30:54 +00003338 traits=GetPixelChannelMapTraits(image,channel);
3339 implode_traits=GetPixelChannelMapTraits(implode_image,channel);
3340 if ((traits == UndefinedPixelTrait) ||
3341 (implode_traits == UndefinedPixelTrait))
3342 continue;
cristya6d7a9b2012-01-18 20:04:48 +00003343 SetPixelChannel(implode_image,channel,p[i],q);
cristy6d188022011-09-12 13:23:33 +00003344 }
3345 else
cristy3ed852e2009-09-05 21:47:34 +00003346 {
3347 double
3348 factor;
3349
3350 /*
3351 Implode the pixel.
3352 */
3353 factor=1.0;
3354 if (distance > 0.0)
cristy1707c6c2012-01-18 23:30:54 +00003355 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/radius/
3356 2)),-amount);
cristy76f512e2011-09-12 01:26:56 +00003357 status=InterpolatePixelChannels(image,image_view,implode_image,method,
3358 (double) (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3359 scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00003360 }
cristy6d188022011-09-12 13:23:33 +00003361 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003362 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003363 }
3364 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3365 status=MagickFalse;
3366 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3367 {
3368 MagickBooleanType
3369 proceed;
3370
cristyb5d5f722009-11-04 03:03:49 +00003371#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003372 #pragma omp critical (MagickCore_ImplodeImage)
cristy3ed852e2009-09-05 21:47:34 +00003373#endif
3374 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3375 if (proceed == MagickFalse)
3376 status=MagickFalse;
3377 }
3378 }
3379 implode_view=DestroyCacheView(implode_view);
3380 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003381 if (status == MagickFalse)
3382 implode_image=DestroyImage(implode_image);
3383 return(implode_image);
3384}
3385
3386/*
3387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3388% %
3389% %
3390% %
3391% M o r p h I m a g e s %
3392% %
3393% %
3394% %
3395%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3396%
3397% The MorphImages() method requires a minimum of two images. The first
3398% image is transformed into the second by a number of intervening images
3399% as specified by frames.
3400%
3401% The format of the MorphImage method is:
3402%
cristybb503372010-05-27 20:51:26 +00003403% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003404% ExceptionInfo *exception)
3405%
3406% A description of each parameter follows:
3407%
3408% o image: the image.
3409%
3410% o number_frames: Define the number of in-between image to generate.
3411% The more in-between frames, the smoother the morph.
3412%
3413% o exception: return any errors or warnings in this structure.
3414%
3415*/
3416MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003417 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003418{
3419#define MorphImageTag "Morph/Image"
3420
3421 Image
3422 *morph_image,
3423 *morph_images;
3424
cristy9d314ff2011-03-09 01:30:28 +00003425 MagickBooleanType
3426 status;
cristy3ed852e2009-09-05 21:47:34 +00003427
3428 MagickOffsetType
3429 scene;
3430
3431 MagickRealType
3432 alpha,
3433 beta;
3434
3435 register const Image
3436 *next;
3437
cristybb503372010-05-27 20:51:26 +00003438 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003439 i;
3440
cristy9d314ff2011-03-09 01:30:28 +00003441 ssize_t
3442 y;
cristy3ed852e2009-09-05 21:47:34 +00003443
3444 /*
3445 Clone first frame in sequence.
3446 */
3447 assert(image != (Image *) NULL);
3448 assert(image->signature == MagickSignature);
3449 if (image->debug != MagickFalse)
3450 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3451 assert(exception != (ExceptionInfo *) NULL);
3452 assert(exception->signature == MagickSignature);
3453 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3454 if (morph_images == (Image *) NULL)
3455 return((Image *) NULL);
3456 if (GetNextImageInList(image) == (Image *) NULL)
3457 {
3458 /*
3459 Morph single image.
3460 */
cristybb503372010-05-27 20:51:26 +00003461 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003462 {
3463 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3464 if (morph_image == (Image *) NULL)
3465 {
3466 morph_images=DestroyImageList(morph_images);
3467 return((Image *) NULL);
3468 }
3469 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003470 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003471 {
cristy8b27a6d2010-02-14 03:31:15 +00003472 MagickBooleanType
3473 proceed;
3474
cristybb503372010-05-27 20:51:26 +00003475 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3476 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003477 if (proceed == MagickFalse)
3478 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003479 }
3480 }
3481 return(GetFirstImageInList(morph_images));
3482 }
3483 /*
3484 Morph image sequence.
3485 */
3486 status=MagickTrue;
3487 scene=0;
3488 next=image;
3489 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3490 {
cristybb503372010-05-27 20:51:26 +00003491 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003492 {
3493 CacheView
3494 *image_view,
3495 *morph_view;
3496
3497 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3498 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003499 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristybb503372010-05-27 20:51:26 +00003500 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
cristy15b98cd2010-09-12 19:42:50 +00003501 next->rows+beta*GetNextImageInList(next)->rows+0.5),
3502 next->filter,next->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003503 if (morph_image == (Image *) NULL)
3504 {
3505 morph_images=DestroyImageList(morph_images);
3506 return((Image *) NULL);
3507 }
cristy1707c6c2012-01-18 23:30:54 +00003508 status=SetImageStorageClass(morph_image,DirectClass,exception);
3509 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003510 {
cristy3ed852e2009-09-05 21:47:34 +00003511 morph_image=DestroyImage(morph_image);
3512 return((Image *) NULL);
3513 }
3514 AppendImageToList(&morph_images,morph_image);
3515 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003516 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3517 morph_images->rows,GetNextImageInList(next)->filter,
3518 GetNextImageInList(next)->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003519 if (morph_image == (Image *) NULL)
3520 {
3521 morph_images=DestroyImageList(morph_images);
3522 return((Image *) NULL);
3523 }
3524 image_view=AcquireCacheView(morph_image);
3525 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003526#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003527 #pragma omp parallel for schedule(static,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003528#endif
cristybb503372010-05-27 20:51:26 +00003529 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003530 {
3531 MagickBooleanType
3532 sync;
3533
cristy4c08aed2011-07-01 19:47:50 +00003534 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003535 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003536
cristybb503372010-05-27 20:51:26 +00003537 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003538 x;
3539
cristy4c08aed2011-07-01 19:47:50 +00003540 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003541 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003542
3543 if (status == MagickFalse)
3544 continue;
3545 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3546 exception);
3547 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3548 exception);
cristy4c08aed2011-07-01 19:47:50 +00003549 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003550 {
3551 status=MagickFalse;
3552 continue;
3553 }
cristybb503372010-05-27 20:51:26 +00003554 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003555 {
cristy1707c6c2012-01-18 23:30:54 +00003556 register ssize_t
3557 i;
3558
cristy10a6c612012-01-29 21:41:05 +00003559 if (GetPixelMask(morph_image,p) != 0)
3560 {
3561 p+=GetPixelChannels(morph_image);
3562 continue;
3563 }
3564 for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
cristy1707c6c2012-01-18 23:30:54 +00003565 {
3566 PixelChannel
3567 channel;
3568
3569 PixelTrait
3570 morph_traits,
3571 traits;
3572
3573 channel=GetPixelChannelMapChannel(image,i);
3574 traits=GetPixelChannelMapTraits(image,channel);
3575 morph_traits=GetPixelChannelMapTraits(morph_image,channel);
3576 if ((traits == UndefinedPixelTrait) ||
3577 (morph_traits == UndefinedPixelTrait))
3578 continue;
3579 if ((morph_traits & CopyPixelTrait) != 0)
3580 {
3581 SetPixelChannel(morph_image,channel,p[i],q);
3582 continue;
3583 }
3584 SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3585 GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3586 }
cristyed231572011-07-14 02:18:59 +00003587 p+=GetPixelChannels(morph_image);
3588 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003589 }
3590 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3591 if (sync == MagickFalse)
3592 status=MagickFalse;
3593 }
3594 morph_view=DestroyCacheView(morph_view);
3595 image_view=DestroyCacheView(image_view);
3596 morph_image=DestroyImage(morph_image);
3597 }
cristybb503372010-05-27 20:51:26 +00003598 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003599 break;
3600 /*
3601 Clone last frame in sequence.
3602 */
3603 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3604 if (morph_image == (Image *) NULL)
3605 {
3606 morph_images=DestroyImageList(morph_images);
3607 return((Image *) NULL);
3608 }
3609 AppendImageToList(&morph_images,morph_image);
3610 morph_images=GetLastImageInList(morph_images);
3611 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3612 {
3613 MagickBooleanType
3614 proceed;
3615
cristyb5d5f722009-11-04 03:03:49 +00003616#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003617 #pragma omp critical (MagickCore_MorphImages)
cristy3ed852e2009-09-05 21:47:34 +00003618#endif
3619 proceed=SetImageProgress(image,MorphImageTag,scene,
3620 GetImageListLength(image));
3621 if (proceed == MagickFalse)
3622 status=MagickFalse;
3623 }
3624 scene++;
3625 }
3626 if (GetNextImageInList(next) != (Image *) NULL)
3627 {
3628 morph_images=DestroyImageList(morph_images);
3629 return((Image *) NULL);
3630 }
3631 return(GetFirstImageInList(morph_images));
3632}
3633
3634/*
3635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3636% %
3637% %
3638% %
3639% P l a s m a I m a g e %
3640% %
3641% %
3642% %
3643%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3644%
3645% PlasmaImage() initializes an image with plasma fractal values. The image
3646% must be initialized with a base color and the random number generator
3647% seeded before this method is called.
3648%
3649% The format of the PlasmaImage method is:
3650%
3651% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristy5cbc0162011-08-29 00:36:28 +00003652% size_t attenuate,size_t depth,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003653%
3654% A description of each parameter follows:
3655%
3656% o image: the image.
3657%
3658% o segment: Define the region to apply plasma fractals values.
3659%
glennrp7dae1ca2010-09-16 12:17:35 +00003660% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003661%
3662% o depth: Limit the plasma recursion depth.
3663%
cristy5cbc0162011-08-29 00:36:28 +00003664% o exception: return any errors or warnings in this structure.
3665%
cristy3ed852e2009-09-05 21:47:34 +00003666*/
3667
3668static inline Quantum PlasmaPixel(RandomInfo *random_info,
3669 const MagickRealType pixel,const MagickRealType noise)
3670{
3671 Quantum
3672 plasma;
3673
cristyce70c172010-01-07 17:15:30 +00003674 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003675 noise/2.0);
3676 return(plasma);
3677}
3678
cristyda1f9c12011-10-02 21:39:49 +00003679static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
3680 CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3681 const SegmentInfo *segment,size_t attenuate,size_t depth,
3682 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003683{
cristy3ed852e2009-09-05 21:47:34 +00003684 MagickRealType
3685 plasma;
3686
cristyda1f9c12011-10-02 21:39:49 +00003687 PixelChannel
3688 channel;
3689
3690 PixelTrait
3691 traits;
3692
3693 register const Quantum
3694 *restrict u,
3695 *restrict v;
3696
3697 register Quantum
3698 *restrict q;
3699
3700 register ssize_t
3701 i;
cristy3ed852e2009-09-05 21:47:34 +00003702
cristy9d314ff2011-03-09 01:30:28 +00003703 ssize_t
3704 x,
3705 x_mid,
3706 y,
3707 y_mid;
3708
cristy3ed852e2009-09-05 21:47:34 +00003709 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3710 return(MagickTrue);
3711 if (depth != 0)
3712 {
3713 SegmentInfo
3714 local_info;
3715
3716 /*
3717 Divide the area into quadrants and recurse.
3718 */
3719 depth--;
3720 attenuate++;
cristybb503372010-05-27 20:51:26 +00003721 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3722 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003723 local_info=(*segment);
3724 local_info.x2=(double) x_mid;
3725 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003726 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3727 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003728 local_info=(*segment);
3729 local_info.y1=(double) y_mid;
3730 local_info.x2=(double) x_mid;
cristyda1f9c12011-10-02 21:39:49 +00003731 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3732 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003733 local_info=(*segment);
3734 local_info.x1=(double) x_mid;
3735 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003736 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3737 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003738 local_info=(*segment);
3739 local_info.x1=(double) x_mid;
3740 local_info.y1=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003741 return(PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3742 &local_info,attenuate,depth,exception));
cristy3ed852e2009-09-05 21:47:34 +00003743 }
cristybb503372010-05-27 20:51:26 +00003744 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3745 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003746 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3747 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3748 return(MagickFalse);
3749 /*
3750 Average pixels and apply plasma.
3751 */
cristy3ed852e2009-09-05 21:47:34 +00003752 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3753 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3754 {
cristy3ed852e2009-09-05 21:47:34 +00003755 /*
3756 Left pixel.
3757 */
cristybb503372010-05-27 20:51:26 +00003758 x=(ssize_t) ceil(segment->x1-0.5);
cristy1707c6c2012-01-18 23:30:54 +00003759 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3760 exception);
3761 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3762 exception);
cristyc5c6f662010-09-22 14:23:02 +00003763 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003764 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3765 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003766 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003767 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3768 {
cristye2a912b2011-12-05 20:02:07 +00003769 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003770 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003771 if (traits == UndefinedPixelTrait)
3772 continue;
3773 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3774 }
cristyc5c6f662010-09-22 14:23:02 +00003775 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003776 if (segment->x1 != segment->x2)
3777 {
3778 /*
3779 Right pixel.
3780 */
cristybb503372010-05-27 20:51:26 +00003781 x=(ssize_t) ceil(segment->x2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003782 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3783 1,1,exception);
3784 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3785 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003786 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003787 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3788 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003789 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003790 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3791 {
cristye2a912b2011-12-05 20:02:07 +00003792 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003793 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003794 if (traits == UndefinedPixelTrait)
3795 continue;
3796 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3797 }
cristyc5c6f662010-09-22 14:23:02 +00003798 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003799 }
3800 }
3801 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3802 {
3803 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3804 {
cristy3ed852e2009-09-05 21:47:34 +00003805 /*
3806 Bottom pixel.
3807 */
cristybb503372010-05-27 20:51:26 +00003808 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003809 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3810 1,1,exception);
3811 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3812 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003813 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003814 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3815 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003816 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003817 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3818 {
cristye2a912b2011-12-05 20:02:07 +00003819 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003820 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003821 if (traits == UndefinedPixelTrait)
3822 continue;
3823 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3824 }
cristyc5c6f662010-09-22 14:23:02 +00003825 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003826 }
3827 if (segment->y1 != segment->y2)
3828 {
cristy3ed852e2009-09-05 21:47:34 +00003829 /*
3830 Top pixel.
3831 */
cristybb503372010-05-27 20:51:26 +00003832 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003833 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3834 1,1,exception);
3835 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3836 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003837 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003838 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3839 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003840 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003841 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3842 {
cristye2a912b2011-12-05 20:02:07 +00003843 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003844 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003845 if (traits == UndefinedPixelTrait)
3846 continue;
3847 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3848 }
cristyc5c6f662010-09-22 14:23:02 +00003849 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003850 }
3851 }
3852 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3853 {
cristy3ed852e2009-09-05 21:47:34 +00003854 /*
3855 Middle pixel.
3856 */
cristybb503372010-05-27 20:51:26 +00003857 x=(ssize_t) ceil(segment->x1-0.5);
3858 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003859 u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
cristybb503372010-05-27 20:51:26 +00003860 x=(ssize_t) ceil(segment->x2-0.5);
3861 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003862 v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003863 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003864 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3865 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003866 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003867 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3868 {
cristye2a912b2011-12-05 20:02:07 +00003869 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003870 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003871 if (traits == UndefinedPixelTrait)
3872 continue;
3873 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3874 }
cristyc5c6f662010-09-22 14:23:02 +00003875 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003876 }
3877 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3878 return(MagickTrue);
3879 return(MagickFalse);
3880}
cristyda1f9c12011-10-02 21:39:49 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882MagickExport MagickBooleanType PlasmaImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00003883 const SegmentInfo *segment,size_t attenuate,size_t depth,
3884 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003885{
cristyc5c6f662010-09-22 14:23:02 +00003886 CacheView
cristyda1f9c12011-10-02 21:39:49 +00003887 *image_view,
3888 *u_view,
3889 *v_view;
cristyc5c6f662010-09-22 14:23:02 +00003890
cristy3ed852e2009-09-05 21:47:34 +00003891 MagickBooleanType
3892 status;
3893
3894 RandomInfo
3895 *random_info;
3896
3897 if (image->debug != MagickFalse)
3898 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3899 assert(image != (Image *) NULL);
3900 assert(image->signature == MagickSignature);
3901 if (image->debug != MagickFalse)
3902 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy5cbc0162011-08-29 00:36:28 +00003903 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristyc5c6f662010-09-22 14:23:02 +00003904 return(MagickFalse);
3905 image_view=AcquireCacheView(image);
cristyda1f9c12011-10-02 21:39:49 +00003906 u_view=AcquireCacheView(image);
3907 v_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003908 random_info=AcquireRandomInfo();
cristyda1f9c12011-10-02 21:39:49 +00003909 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
3910 attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003911 random_info=DestroyRandomInfo(random_info);
cristyda1f9c12011-10-02 21:39:49 +00003912 v_view=DestroyCacheView(v_view);
3913 u_view=DestroyCacheView(u_view);
cristyc5c6f662010-09-22 14:23:02 +00003914 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003915 return(status);
3916}
3917
3918/*
3919%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3920% %
3921% %
3922% %
3923% P o l a r o i d I m a g e %
3924% %
3925% %
3926% %
3927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3928%
3929% PolaroidImage() simulates a Polaroid picture.
3930%
3931% The format of the AnnotateImage method is:
3932%
3933% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003934% const char *caption,const double angle,
3935% const PixelInterpolateMethod method,ExceptionInfo exception)
cristy3ed852e2009-09-05 21:47:34 +00003936%
3937% A description of each parameter follows:
3938%
3939% o image: the image.
3940%
3941% o draw_info: the draw info.
3942%
cristye9e3d382011-12-14 01:50:13 +00003943% o caption: the Polaroid caption.
3944%
cristycee97112010-05-28 00:44:52 +00003945% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003946%
cristy5c4e2582011-09-11 19:21:03 +00003947% o method: the pixel interpolation method.
3948%
cristy3ed852e2009-09-05 21:47:34 +00003949% o exception: return any errors or warnings in this structure.
3950%
3951*/
3952MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003953 const char *caption,const double angle,const PixelInterpolateMethod method,
cristy5c4e2582011-09-11 19:21:03 +00003954 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003955{
cristy3ed852e2009-09-05 21:47:34 +00003956 Image
3957 *bend_image,
3958 *caption_image,
3959 *flop_image,
3960 *picture_image,
3961 *polaroid_image,
3962 *rotate_image,
3963 *trim_image;
3964
cristybb503372010-05-27 20:51:26 +00003965 size_t
cristy3ed852e2009-09-05 21:47:34 +00003966 height;
3967
cristy9d314ff2011-03-09 01:30:28 +00003968 ssize_t
3969 quantum;
3970
cristy3ed852e2009-09-05 21:47:34 +00003971 /*
3972 Simulate a Polaroid picture.
3973 */
3974 assert(image != (Image *) NULL);
3975 assert(image->signature == MagickSignature);
3976 if (image->debug != MagickFalse)
3977 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3978 assert(exception != (ExceptionInfo *) NULL);
3979 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003980 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003981 image->rows)/25.0,10.0);
3982 height=image->rows+2*quantum;
3983 caption_image=(Image *) NULL;
cristye9e3d382011-12-14 01:50:13 +00003984 if (caption != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003985 {
3986 char
cristye9e3d382011-12-14 01:50:13 +00003987 geometry[MaxTextExtent],
3988 *text;
cristy3ed852e2009-09-05 21:47:34 +00003989
3990 DrawInfo
3991 *annotate_info;
3992
cristy3ed852e2009-09-05 21:47:34 +00003993 MagickBooleanType
3994 status;
3995
cristy9d314ff2011-03-09 01:30:28 +00003996 ssize_t
3997 count;
3998
cristy3ed852e2009-09-05 21:47:34 +00003999 TypeMetric
4000 metrics;
4001
4002 /*
4003 Generate caption image.
4004 */
4005 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
4006 if (caption_image == (Image *) NULL)
4007 return((Image *) NULL);
4008 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
cristye9e3d382011-12-14 01:50:13 +00004009 text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
4010 exception);
4011 (void) CloneString(&annotate_info->text,text);
cristy6b1d05e2010-09-22 19:17:27 +00004012 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
cristye9e3d382011-12-14 01:50:13 +00004013 &text,exception);
4014 status=SetImageExtent(caption_image,image->columns,(size_t) ((count+1)*
4015 (metrics.ascent-metrics.descent)+0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00004016 if (status == MagickFalse)
4017 caption_image=DestroyImage(caption_image);
4018 else
4019 {
4020 caption_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004021 (void) SetImageBackgroundColor(caption_image,exception);
cristye9e3d382011-12-14 01:50:13 +00004022 (void) CloneString(&annotate_info->text,text);
cristyb51dff52011-05-19 16:55:47 +00004023 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00004024 metrics.ascent);
4025 if (annotate_info->gravity == UndefinedGravity)
4026 (void) CloneString(&annotate_info->geometry,AcquireString(
4027 geometry));
cristy5cbc0162011-08-29 00:36:28 +00004028 (void) AnnotateImage(caption_image,annotate_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004029 height+=caption_image->rows;
4030 }
4031 annotate_info=DestroyDrawInfo(annotate_info);
cristye9e3d382011-12-14 01:50:13 +00004032 text=DestroyString(text);
cristy3ed852e2009-09-05 21:47:34 +00004033 }
4034 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4035 exception);
4036 if (picture_image == (Image *) NULL)
4037 {
4038 if (caption_image != (Image *) NULL)
4039 caption_image=DestroyImage(caption_image);
4040 return((Image *) NULL);
4041 }
4042 picture_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004043 (void) SetImageBackgroundColor(picture_image,exception);
cristye941a752011-10-15 01:52:48 +00004044 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum,
4045 exception);
cristy3ed852e2009-09-05 21:47:34 +00004046 if (caption_image != (Image *) NULL)
4047 {
cristy1707c6c2012-01-18 23:30:54 +00004048 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,quantum,
4049 (ssize_t) (image->rows+3*quantum/2),exception);
cristy3ed852e2009-09-05 21:47:34 +00004050 caption_image=DestroyImage(caption_image);
4051 }
cristy9950d572011-10-01 18:22:35 +00004052 (void) QueryColorCompliance("none",AllCompliance,
4053 &picture_image->background_color,exception);
cristy63240882011-08-05 19:05:27 +00004054 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004055 rotate_image=RotateImage(picture_image,90.0,exception);
4056 picture_image=DestroyImage(picture_image);
4057 if (rotate_image == (Image *) NULL)
4058 return((Image *) NULL);
4059 picture_image=rotate_image;
4060 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
cristy5c4e2582011-09-11 19:21:03 +00004061 picture_image->columns,method,exception);
cristy3ed852e2009-09-05 21:47:34 +00004062 picture_image=DestroyImage(picture_image);
4063 if (bend_image == (Image *) NULL)
4064 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004065 picture_image=bend_image;
4066 rotate_image=RotateImage(picture_image,-90.0,exception);
4067 picture_image=DestroyImage(picture_image);
4068 if (rotate_image == (Image *) NULL)
4069 return((Image *) NULL);
4070 picture_image=rotate_image;
4071 picture_image->background_color=image->background_color;
cristy78ec1a92011-12-09 09:25:34 +00004072 polaroid_image=ShadowImage(picture_image,80.0,2.0,0.0,quantum/3,quantum/3,
cristy3ed852e2009-09-05 21:47:34 +00004073 exception);
4074 if (polaroid_image == (Image *) NULL)
4075 {
4076 picture_image=DestroyImage(picture_image);
4077 return(picture_image);
4078 }
4079 flop_image=FlopImage(polaroid_image,exception);
4080 polaroid_image=DestroyImage(polaroid_image);
4081 if (flop_image == (Image *) NULL)
4082 {
4083 picture_image=DestroyImage(picture_image);
4084 return(picture_image);
4085 }
4086 polaroid_image=flop_image;
cristy1707c6c2012-01-18 23:30:54 +00004087 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,(ssize_t)
4088 (-0.01*picture_image->columns/2.0),0L,exception);
cristy3ed852e2009-09-05 21:47:34 +00004089 picture_image=DestroyImage(picture_image);
cristy9950d572011-10-01 18:22:35 +00004090 (void) QueryColorCompliance("none",AllCompliance,
4091 &polaroid_image->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00004092 rotate_image=RotateImage(polaroid_image,angle,exception);
4093 polaroid_image=DestroyImage(polaroid_image);
4094 if (rotate_image == (Image *) NULL)
4095 return((Image *) NULL);
4096 polaroid_image=rotate_image;
4097 trim_image=TrimImage(polaroid_image,exception);
4098 polaroid_image=DestroyImage(polaroid_image);
4099 if (trim_image == (Image *) NULL)
4100 return((Image *) NULL);
4101 polaroid_image=trim_image;
4102 return(polaroid_image);
4103}
4104
4105/*
4106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4107% %
4108% %
4109% %
cristy3ed852e2009-09-05 21:47:34 +00004110% S e p i a T o n e I m a g e %
4111% %
4112% %
4113% %
4114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4115%
4116% MagickSepiaToneImage() applies a special effect to the image, similar to the
4117% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4118% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4119% threshold of 80% is a good starting point for a reasonable tone.
4120%
4121% The format of the SepiaToneImage method is:
4122%
4123% Image *SepiaToneImage(const Image *image,const double threshold,
4124% ExceptionInfo *exception)
4125%
4126% A description of each parameter follows:
4127%
4128% o image: the image.
4129%
4130% o threshold: the tone threshold.
4131%
4132% o exception: return any errors or warnings in this structure.
4133%
4134*/
4135MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4136 ExceptionInfo *exception)
4137{
4138#define SepiaToneImageTag "SepiaTone/Image"
4139
cristyc4c8d132010-01-07 01:58:38 +00004140 CacheView
4141 *image_view,
4142 *sepia_view;
4143
cristy3ed852e2009-09-05 21:47:34 +00004144 Image
4145 *sepia_image;
4146
cristy3ed852e2009-09-05 21:47:34 +00004147 MagickBooleanType
4148 status;
4149
cristybb503372010-05-27 20:51:26 +00004150 MagickOffsetType
4151 progress;
4152
4153 ssize_t
4154 y;
4155
cristy3ed852e2009-09-05 21:47:34 +00004156 /*
4157 Initialize sepia-toned image attributes.
4158 */
4159 assert(image != (const Image *) NULL);
4160 assert(image->signature == MagickSignature);
4161 if (image->debug != MagickFalse)
4162 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4163 assert(exception != (ExceptionInfo *) NULL);
4164 assert(exception->signature == MagickSignature);
cristy1707c6c2012-01-18 23:30:54 +00004165 sepia_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004166 if (sepia_image == (Image *) NULL)
4167 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004168 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004169 {
cristy3ed852e2009-09-05 21:47:34 +00004170 sepia_image=DestroyImage(sepia_image);
4171 return((Image *) NULL);
4172 }
4173 /*
4174 Tone each row of the image.
4175 */
4176 status=MagickTrue;
4177 progress=0;
4178 image_view=AcquireCacheView(image);
4179 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004180#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004181 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004182#endif
cristybb503372010-05-27 20:51:26 +00004183 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004184 {
cristy4c08aed2011-07-01 19:47:50 +00004185 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004186 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004187
cristybb503372010-05-27 20:51:26 +00004188 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004189 x;
4190
cristy4c08aed2011-07-01 19:47:50 +00004191 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004192 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004193
4194 if (status == MagickFalse)
4195 continue;
4196 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00004197 q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004198 exception);
cristy4c08aed2011-07-01 19:47:50 +00004199 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004200 {
4201 status=MagickFalse;
4202 continue;
4203 }
cristybb503372010-05-27 20:51:26 +00004204 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004205 {
4206 MagickRealType
4207 intensity,
4208 tone;
4209
cristy4c08aed2011-07-01 19:47:50 +00004210 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004211 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4212 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004213 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004214 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4215 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004216 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004217 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004218 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004219 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004220 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4221 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4222 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4223 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004224 p+=GetPixelChannels(image);
4225 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004226 }
4227 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4228 status=MagickFalse;
4229 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4230 {
4231 MagickBooleanType
4232 proceed;
4233
cristyb5d5f722009-11-04 03:03:49 +00004234#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004235 #pragma omp critical (MagickCore_SepiaToneImage)
cristy3ed852e2009-09-05 21:47:34 +00004236#endif
4237 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4238 image->rows);
4239 if (proceed == MagickFalse)
4240 status=MagickFalse;
4241 }
4242 }
4243 sepia_view=DestroyCacheView(sepia_view);
4244 image_view=DestroyCacheView(image_view);
cristye23ec9d2011-08-16 18:15:40 +00004245 (void) NormalizeImage(sepia_image,exception);
4246 (void) ContrastImage(sepia_image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004247 if (status == MagickFalse)
4248 sepia_image=DestroyImage(sepia_image);
4249 return(sepia_image);
4250}
4251
4252/*
4253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4254% %
4255% %
4256% %
4257% S h a d o w I m a g e %
4258% %
4259% %
4260% %
4261%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4262%
4263% ShadowImage() simulates a shadow from the specified image and returns it.
4264%
4265% The format of the ShadowImage method is:
4266%
cristy70cddf72011-12-10 22:42:42 +00004267% Image *ShadowImage(const Image *image,const double alpha,
cristyeb6e6582011-12-09 09:14:23 +00004268% const double sigma,const double bias,const ssize_t x_offset,
4269% const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004270%
4271% A description of each parameter follows:
4272%
4273% o image: the image.
4274%
cristy70cddf72011-12-10 22:42:42 +00004275% o alpha: percentage transparency.
cristy3ed852e2009-09-05 21:47:34 +00004276%
4277% o sigma: the standard deviation of the Gaussian, in pixels.
4278%
cristyeb6e6582011-12-09 09:14:23 +00004279% o bias: the bias.
4280%
cristy3ed852e2009-09-05 21:47:34 +00004281% o x_offset: the shadow x-offset.
4282%
4283% o y_offset: the shadow y-offset.
4284%
4285% o exception: return any errors or warnings in this structure.
4286%
4287*/
cristy70cddf72011-12-10 22:42:42 +00004288MagickExport Image *ShadowImage(const Image *image,const double alpha,
cristyeb6e6582011-12-09 09:14:23 +00004289 const double sigma,const double bias,const ssize_t x_offset,
4290 const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004291{
4292#define ShadowImageTag "Shadow/Image"
4293
cristy70cddf72011-12-10 22:42:42 +00004294 CacheView
4295 *image_view;
4296
cristybd5a96c2011-08-21 00:04:26 +00004297 ChannelType
4298 channel_mask;
4299
cristy3ed852e2009-09-05 21:47:34 +00004300 Image
4301 *border_image,
4302 *clone_image,
4303 *shadow_image;
4304
cristy70cddf72011-12-10 22:42:42 +00004305 MagickBooleanType
4306 status;
4307
cristy3ed852e2009-09-05 21:47:34 +00004308 RectangleInfo
4309 border_info;
4310
cristy70cddf72011-12-10 22:42:42 +00004311 ssize_t
4312 y;
4313
cristy3ed852e2009-09-05 21:47:34 +00004314 assert(image != (Image *) NULL);
4315 assert(image->signature == MagickSignature);
4316 if (image->debug != MagickFalse)
4317 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4318 assert(exception != (ExceptionInfo *) NULL);
4319 assert(exception->signature == MagickSignature);
4320 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4321 if (clone_image == (Image *) NULL)
4322 return((Image *) NULL);
4323 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
cristybb503372010-05-27 20:51:26 +00004324 border_info.width=(size_t) floor(2.0*sigma+0.5);
4325 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004326 border_info.x=0;
4327 border_info.y=0;
cristy9950d572011-10-01 18:22:35 +00004328 (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4329 exception);
cristy70cddf72011-12-10 22:42:42 +00004330 clone_image->matte=MagickTrue;
4331 border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
cristy3ed852e2009-09-05 21:47:34 +00004332 clone_image=DestroyImage(clone_image);
4333 if (border_image == (Image *) NULL)
4334 return((Image *) NULL);
4335 if (border_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004336 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004337 /*
4338 Shadow image.
4339 */
cristy70cddf72011-12-10 22:42:42 +00004340 status=MagickTrue;
4341 image_view=AcquireCacheView(border_image);
4342 for (y=0; y < (ssize_t) border_image->rows; y++)
4343 {
4344 PixelInfo
4345 background_color;
4346
4347 register Quantum
4348 *restrict q;
4349
4350 register ssize_t
4351 x;
4352
4353 if (status == MagickFalse)
4354 continue;
4355 q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4356 exception);
4357 if (q == (Quantum *) NULL)
4358 {
4359 status=MagickFalse;
4360 continue;
4361 }
4362 background_color=border_image->background_color;
4363 background_color.matte=MagickTrue;
4364 for (x=0; x < (ssize_t) border_image->columns; x++)
4365 {
4366 if (border_image->matte != MagickFalse)
4367 background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4368 SetPixelInfoPixel(border_image,&background_color,q);
4369 q+=GetPixelChannels(border_image);
4370 }
4371 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4372 status=MagickFalse;
4373 }
4374 image_view=DestroyCacheView(image_view);
4375 if (status == MagickFalse)
4376 {
4377 border_image=DestroyImage(border_image);
4378 return((Image *) NULL);
4379 }
cristybd5a96c2011-08-21 00:04:26 +00004380 channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
cristyb27bb772011-12-11 16:12:50 +00004381 shadow_image=BlurImage(border_image,0.0,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00004382 border_image=DestroyImage(border_image);
4383 if (shadow_image == (Image *) NULL)
4384 return((Image *) NULL);
cristyae1969f2011-12-10 03:07:36 +00004385 (void) SetPixelChannelMapMask(shadow_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004386 if (shadow_image->page.width == 0)
4387 shadow_image->page.width=shadow_image->columns;
4388 if (shadow_image->page.height == 0)
4389 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004390 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4391 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4392 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4393 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004394 return(shadow_image);
4395}
4396
4397/*
4398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4399% %
4400% %
4401% %
4402% S k e t c h I m a g e %
4403% %
4404% %
4405% %
4406%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4407%
4408% SketchImage() simulates a pencil sketch. We convolve the image with a
4409% Gaussian operator of the given radius and standard deviation (sigma). For
4410% reasonable results, radius should be larger than sigma. Use a radius of 0
4411% and SketchImage() selects a suitable radius for you. Angle gives the angle
4412% of the sketch.
4413%
4414% The format of the SketchImage method is:
4415%
4416% Image *SketchImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00004417% const double sigma,const double angle,const double bias,
4418% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004419%
4420% A description of each parameter follows:
4421%
4422% o image: the image.
4423%
cristy574cc262011-08-05 01:23:58 +00004424% o radius: the radius of the Gaussian, in pixels, not counting the
4425% center pixel.
cristy3ed852e2009-09-05 21:47:34 +00004426%
4427% o sigma: the standard deviation of the Gaussian, in pixels.
4428%
cristyf7ef0252011-09-09 14:50:06 +00004429% o angle: apply the effect along this angle.
4430%
4431% o bias: the bias.
cristy3ed852e2009-09-05 21:47:34 +00004432%
4433% o exception: return any errors or warnings in this structure.
4434%
4435*/
4436MagickExport Image *SketchImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00004437 const double sigma,const double angle,const double bias,
4438 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004439{
cristyfa112112010-01-04 17:48:07 +00004440 CacheView
4441 *random_view;
4442
cristy3ed852e2009-09-05 21:47:34 +00004443 Image
4444 *blend_image,
4445 *blur_image,
4446 *dodge_image,
4447 *random_image,
4448 *sketch_image;
4449
cristy3ed852e2009-09-05 21:47:34 +00004450 MagickBooleanType
4451 status;
4452
cristy3ed852e2009-09-05 21:47:34 +00004453 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004454 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004455
cristy9d314ff2011-03-09 01:30:28 +00004456 ssize_t
4457 y;
4458
cristy3ed852e2009-09-05 21:47:34 +00004459 /*
4460 Sketch image.
4461 */
4462 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4463 MagickTrue,exception);
4464 if (random_image == (Image *) NULL)
4465 return((Image *) NULL);
4466 status=MagickTrue;
cristy1b784432009-12-19 02:20:40 +00004467 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004468 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004469#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004470 #pragma omp parallel for schedule(static,4) shared(status)
cristy1b784432009-12-19 02:20:40 +00004471#endif
cristybb503372010-05-27 20:51:26 +00004472 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004473 {
cristy5c9e6f22010-09-17 17:31:01 +00004474 const int
4475 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004476
cristybb503372010-05-27 20:51:26 +00004477 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004478 x;
4479
cristy4c08aed2011-07-01 19:47:50 +00004480 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004481 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004482
cristy1b784432009-12-19 02:20:40 +00004483 if (status == MagickFalse)
4484 continue;
cristy3ed852e2009-09-05 21:47:34 +00004485 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4486 exception);
cristyacd2ed22011-08-30 01:44:23 +00004487 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004488 {
4489 status=MagickFalse;
4490 continue;
4491 }
cristybb503372010-05-27 20:51:26 +00004492 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004493 {
cristy76f512e2011-09-12 01:26:56 +00004494 MagickRealType
4495 value;
4496
4497 register ssize_t
4498 i;
4499
cristy10a6c612012-01-29 21:41:05 +00004500 if (GetPixelMask(random_image,q) != 0)
4501 {
4502 q+=GetPixelChannels(random_image);
4503 continue;
4504 }
cristy76f512e2011-09-12 01:26:56 +00004505 value=GetPseudoRandomValue(random_info[id]);
4506 for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4507 {
cristyabace412011-12-11 15:56:53 +00004508 PixelChannel
4509 channel;
4510
cristy76f512e2011-09-12 01:26:56 +00004511 PixelTrait
4512 traits;
4513
cristyabace412011-12-11 15:56:53 +00004514 channel=GetPixelChannelMapChannel(image,i);
4515 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004516 if (traits == UndefinedPixelTrait)
4517 continue;
4518 q[i]=ClampToQuantum(QuantumRange*value);
4519 }
cristyed231572011-07-14 02:18:59 +00004520 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004521 }
4522 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4523 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004524 }
4525 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004526 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004527 if (status == MagickFalse)
4528 {
4529 random_image=DestroyImage(random_image);
4530 return(random_image);
4531 }
cristyf7ef0252011-09-09 14:50:06 +00004532 blur_image=MotionBlurImage(random_image,radius,sigma,angle,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00004533 random_image=DestroyImage(random_image);
4534 if (blur_image == (Image *) NULL)
4535 return((Image *) NULL);
cristy6bfd6902011-12-09 01:33:45 +00004536 dodge_image=EdgeImage(blur_image,radius,1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004537 blur_image=DestroyImage(blur_image);
4538 if (dodge_image == (Image *) NULL)
4539 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +00004540 (void) NormalizeImage(dodge_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00004541 (void) NegateImage(dodge_image,MagickFalse,exception);
cristye941a752011-10-15 01:52:48 +00004542 (void) TransformImage(&dodge_image,(char *) NULL,"50%",exception);
cristy3ed852e2009-09-05 21:47:34 +00004543 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4544 if (sketch_image == (Image *) NULL)
4545 {
4546 dodge_image=DestroyImage(dodge_image);
4547 return((Image *) NULL);
4548 }
cristye941a752011-10-15 01:52:48 +00004549 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0,
4550 exception);
cristy3ed852e2009-09-05 21:47:34 +00004551 dodge_image=DestroyImage(dodge_image);
4552 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4553 if (blend_image == (Image *) NULL)
4554 {
4555 sketch_image=DestroyImage(sketch_image);
4556 return((Image *) NULL);
4557 }
4558 (void) SetImageArtifact(blend_image,"compose:args","20x80");
cristye941a752011-10-15 01:52:48 +00004559 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0,
4560 exception);
cristy3ed852e2009-09-05 21:47:34 +00004561 blend_image=DestroyImage(blend_image);
4562 return(sketch_image);
4563}
4564
4565/*
4566%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4567% %
4568% %
4569% %
4570% S o l a r i z e I m a g e %
4571% %
4572% %
4573% %
4574%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4575%
4576% SolarizeImage() applies a special effect to the image, similar to the effect
4577% achieved in a photo darkroom by selectively exposing areas of photo
4578% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4579% measure of the extent of the solarization.
4580%
4581% The format of the SolarizeImage method is:
4582%
cristy5cbc0162011-08-29 00:36:28 +00004583% MagickBooleanType SolarizeImage(Image *image,const double threshold,
4584% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004585%
4586% A description of each parameter follows:
4587%
4588% o image: the image.
4589%
4590% o threshold: Define the extent of the solarization.
4591%
cristy5cbc0162011-08-29 00:36:28 +00004592% o exception: return any errors or warnings in this structure.
4593%
cristy3ed852e2009-09-05 21:47:34 +00004594*/
4595MagickExport MagickBooleanType SolarizeImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00004596 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004597{
4598#define SolarizeImageTag "Solarize/Image"
4599
cristyc4c8d132010-01-07 01:58:38 +00004600 CacheView
4601 *image_view;
4602
cristy3ed852e2009-09-05 21:47:34 +00004603 MagickBooleanType
4604 status;
4605
cristybb503372010-05-27 20:51:26 +00004606 MagickOffsetType
4607 progress;
4608
4609 ssize_t
4610 y;
4611
cristy3ed852e2009-09-05 21:47:34 +00004612 assert(image != (Image *) NULL);
4613 assert(image->signature == MagickSignature);
4614 if (image->debug != MagickFalse)
4615 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4616 if (image->storage_class == PseudoClass)
4617 {
cristybb503372010-05-27 20:51:26 +00004618 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004619 i;
4620
4621 /*
4622 Solarize colormap.
4623 */
cristybb503372010-05-27 20:51:26 +00004624 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004625 {
4626 if ((MagickRealType) image->colormap[i].red > threshold)
4627 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4628 if ((MagickRealType) image->colormap[i].green > threshold)
4629 image->colormap[i].green=(Quantum) QuantumRange-
4630 image->colormap[i].green;
4631 if ((MagickRealType) image->colormap[i].blue > threshold)
4632 image->colormap[i].blue=(Quantum) QuantumRange-
4633 image->colormap[i].blue;
4634 }
4635 }
4636 /*
4637 Solarize image.
4638 */
4639 status=MagickTrue;
4640 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00004641 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004642#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004643 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004644#endif
cristybb503372010-05-27 20:51:26 +00004645 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004646 {
cristybb503372010-05-27 20:51:26 +00004647 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004648 x;
4649
cristy4c08aed2011-07-01 19:47:50 +00004650 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004651 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004652
4653 if (status == MagickFalse)
4654 continue;
cristy5cbc0162011-08-29 00:36:28 +00004655 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00004656 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004657 {
4658 status=MagickFalse;
4659 continue;
4660 }
cristybb503372010-05-27 20:51:26 +00004661 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004662 {
cristy76f512e2011-09-12 01:26:56 +00004663 register ssize_t
4664 i;
4665
cristy10a6c612012-01-29 21:41:05 +00004666 if (GetPixelMask(image,q) != 0)
4667 {
4668 q+=GetPixelChannels(image);
4669 continue;
4670 }
cristy76f512e2011-09-12 01:26:56 +00004671 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4672 {
cristyabace412011-12-11 15:56:53 +00004673 PixelChannel
4674 channel;
4675
cristy76f512e2011-09-12 01:26:56 +00004676 PixelTrait
4677 traits;
4678
cristyabace412011-12-11 15:56:53 +00004679 channel=GetPixelChannelMapChannel(image,i);
4680 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004681 if ((traits == UndefinedPixelTrait) ||
4682 ((traits & CopyPixelTrait) != 0))
4683 continue;
4684 if ((MagickRealType) q[i] > threshold)
4685 q[i]=QuantumRange-q[i];
4686 }
cristyed231572011-07-14 02:18:59 +00004687 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004688 }
4689 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4690 status=MagickFalse;
4691 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4692 {
4693 MagickBooleanType
4694 proceed;
4695
cristyb5d5f722009-11-04 03:03:49 +00004696#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004697 #pragma omp critical (MagickCore_SolarizeImage)
cristy3ed852e2009-09-05 21:47:34 +00004698#endif
4699 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4700 if (proceed == MagickFalse)
4701 status=MagickFalse;
4702 }
4703 }
4704 image_view=DestroyCacheView(image_view);
4705 return(status);
4706}
4707
4708/*
4709%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4710% %
4711% %
4712% %
4713% S t e g a n o I m a g e %
4714% %
4715% %
4716% %
4717%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4718%
4719% SteganoImage() hides a digital watermark within the image. Recover
4720% the hidden watermark later to prove that the authenticity of an image.
4721% Offset defines the start position within the image to hide the watermark.
4722%
4723% The format of the SteganoImage method is:
4724%
4725% Image *SteganoImage(const Image *image,Image *watermark,
4726% ExceptionInfo *exception)
4727%
4728% A description of each parameter follows:
4729%
4730% o image: the image.
4731%
4732% o watermark: the watermark image.
4733%
4734% o exception: return any errors or warnings in this structure.
4735%
4736*/
4737MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4738 ExceptionInfo *exception)
4739{
cristye1bf8ad2010-09-19 17:07:03 +00004740#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004741#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004742 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004743#define SteganoImageTag "Stegano/Image"
4744
cristyb0d3bb92010-09-22 14:37:58 +00004745 CacheView
4746 *stegano_view,
4747 *watermark_view;
4748
cristy3ed852e2009-09-05 21:47:34 +00004749 Image
4750 *stegano_image;
4751
4752 int
4753 c;
4754
cristy3ed852e2009-09-05 21:47:34 +00004755 MagickBooleanType
4756 status;
4757
cristy101ab702011-10-13 13:06:32 +00004758 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004759 pixel;
4760
cristy4c08aed2011-07-01 19:47:50 +00004761 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004762 *q;
4763
cristye1bf8ad2010-09-19 17:07:03 +00004764 register ssize_t
4765 x;
4766
cristybb503372010-05-27 20:51:26 +00004767 size_t
cristyeaedf062010-05-29 22:36:02 +00004768 depth,
4769 one;
cristy3ed852e2009-09-05 21:47:34 +00004770
cristye1bf8ad2010-09-19 17:07:03 +00004771 ssize_t
4772 i,
4773 j,
4774 k,
4775 y;
4776
cristy3ed852e2009-09-05 21:47:34 +00004777 /*
4778 Initialize steganographic image attributes.
4779 */
4780 assert(image != (const Image *) NULL);
4781 assert(image->signature == MagickSignature);
4782 if (image->debug != MagickFalse)
4783 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4784 assert(watermark != (const Image *) NULL);
4785 assert(watermark->signature == MagickSignature);
4786 assert(exception != (ExceptionInfo *) NULL);
4787 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004788 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004789 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4790 if (stegano_image == (Image *) NULL)
4791 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004792 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004793 {
cristy3ed852e2009-09-05 21:47:34 +00004794 stegano_image=DestroyImage(stegano_image);
4795 return((Image *) NULL);
4796 }
4797 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4798 /*
4799 Hide watermark in low-order bits of image.
4800 */
4801 c=0;
4802 i=0;
4803 j=0;
4804 depth=stegano_image->depth;
4805 k=image->offset;
cristyda16f162011-02-19 23:52:17 +00004806 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004807 watermark_view=AcquireCacheView(watermark);
4808 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004809 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004810 {
cristybb503372010-05-27 20:51:26 +00004811 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004812 {
cristybb503372010-05-27 20:51:26 +00004813 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004814 {
cristyda1f9c12011-10-02 21:39:49 +00004815 Quantum
cristy5f95f4f2011-10-23 01:01:01 +00004816 virtual_pixel[CompositePixelChannel];
cristyda1f9c12011-10-02 21:39:49 +00004817
cristy1707c6c2012-01-18 23:30:54 +00004818 ssize_t
4819 offset;
4820
cristyda1f9c12011-10-02 21:39:49 +00004821 (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,virtual_pixel,
4822 exception);
4823 pixel.red=(double) virtual_pixel[RedPixelChannel];
4824 pixel.green=(double) virtual_pixel[GreenPixelChannel];
4825 pixel.blue=(double) virtual_pixel[BluePixelChannel];
4826 pixel.alpha=(double) virtual_pixel[AlphaPixelChannel];
cristy1707c6c2012-01-18 23:30:54 +00004827 offset=k/(ssize_t) stegano_image->columns;
4828 if (offset >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004829 break;
cristyb0d3bb92010-09-22 14:37:58 +00004830 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4831 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4832 exception);
cristyacd2ed22011-08-30 01:44:23 +00004833 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004834 break;
4835 switch (c)
4836 {
4837 case 0:
4838 {
cristy4c08aed2011-07-01 19:47:50 +00004839 SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004840 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004841 break;
4842 }
4843 case 1:
4844 {
cristy4c08aed2011-07-01 19:47:50 +00004845 SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004846 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004847 break;
4848 }
4849 case 2:
4850 {
cristy4c08aed2011-07-01 19:47:50 +00004851 SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004852 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004853 break;
4854 }
4855 }
cristyb0d3bb92010-09-22 14:37:58 +00004856 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004857 break;
4858 c++;
4859 if (c == 3)
4860 c=0;
4861 k++;
cristybb503372010-05-27 20:51:26 +00004862 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004863 k=0;
4864 if (k == image->offset)
4865 j++;
4866 }
4867 }
cristy8b27a6d2010-02-14 03:31:15 +00004868 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004869 {
cristy8b27a6d2010-02-14 03:31:15 +00004870 MagickBooleanType
4871 proceed;
4872
4873 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4874 (depth-i),depth);
4875 if (proceed == MagickFalse)
4876 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004877 }
4878 }
cristyb0d3bb92010-09-22 14:37:58 +00004879 stegano_view=DestroyCacheView(stegano_view);
4880 watermark_view=DestroyCacheView(watermark_view);
cristy3ed852e2009-09-05 21:47:34 +00004881 if (stegano_image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00004882 (void) SyncImage(stegano_image,exception);
cristyda16f162011-02-19 23:52:17 +00004883 if (status == MagickFalse)
4884 {
4885 stegano_image=DestroyImage(stegano_image);
4886 return((Image *) NULL);
4887 }
cristy3ed852e2009-09-05 21:47:34 +00004888 return(stegano_image);
4889}
4890
4891/*
4892%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4893% %
4894% %
4895% %
4896% S t e r e o A n a g l y p h I m a g e %
4897% %
4898% %
4899% %
4900%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4901%
4902% StereoAnaglyphImage() combines two images and produces a single image that
4903% is the composite of a left and right image of a stereo pair. Special
4904% red-green stereo glasses are required to view this effect.
4905%
4906% The format of the StereoAnaglyphImage method is:
4907%
4908% Image *StereoImage(const Image *left_image,const Image *right_image,
4909% ExceptionInfo *exception)
4910% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004911% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004912% ExceptionInfo *exception)
4913%
4914% A description of each parameter follows:
4915%
4916% o left_image: the left image.
4917%
4918% o right_image: the right image.
4919%
4920% o exception: return any errors or warnings in this structure.
4921%
4922% o x_offset: amount, in pixels, by which the left image is offset to the
4923% right of the right image.
4924%
4925% o y_offset: amount, in pixels, by which the left image is offset to the
4926% bottom of the right image.
4927%
4928%
4929*/
4930MagickExport Image *StereoImage(const Image *left_image,
4931 const Image *right_image,ExceptionInfo *exception)
4932{
4933 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4934}
4935
4936MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004937 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004938 ExceptionInfo *exception)
4939{
4940#define StereoImageTag "Stereo/Image"
4941
4942 const Image
4943 *image;
4944
4945 Image
4946 *stereo_image;
4947
cristy3ed852e2009-09-05 21:47:34 +00004948 MagickBooleanType
4949 status;
4950
cristy9d314ff2011-03-09 01:30:28 +00004951 ssize_t
4952 y;
4953
cristy3ed852e2009-09-05 21:47:34 +00004954 assert(left_image != (const Image *) NULL);
4955 assert(left_image->signature == MagickSignature);
4956 if (left_image->debug != MagickFalse)
4957 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4958 left_image->filename);
4959 assert(right_image != (const Image *) NULL);
4960 assert(right_image->signature == MagickSignature);
4961 assert(exception != (ExceptionInfo *) NULL);
4962 assert(exception->signature == MagickSignature);
4963 assert(right_image != (const Image *) NULL);
4964 image=left_image;
4965 if ((left_image->columns != right_image->columns) ||
4966 (left_image->rows != right_image->rows))
4967 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4968 /*
4969 Initialize stereo image attributes.
4970 */
4971 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4972 MagickTrue,exception);
4973 if (stereo_image == (Image *) NULL)
4974 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004975 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004976 {
cristy3ed852e2009-09-05 21:47:34 +00004977 stereo_image=DestroyImage(stereo_image);
4978 return((Image *) NULL);
4979 }
4980 /*
4981 Copy left image to red channel and right image to blue channel.
4982 */
cristyda16f162011-02-19 23:52:17 +00004983 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004984 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004985 {
cristy4c08aed2011-07-01 19:47:50 +00004986 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004987 *restrict p,
4988 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004989
cristybb503372010-05-27 20:51:26 +00004990 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004991 x;
4992
cristy4c08aed2011-07-01 19:47:50 +00004993 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004994 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004995
4996 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4997 exception);
4998 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4999 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy76f512e2011-09-12 01:26:56 +00005000 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
5001 (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005002 break;
cristybb503372010-05-27 20:51:26 +00005003 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005004 {
cristy4c08aed2011-07-01 19:47:50 +00005005 SetPixelRed(image,GetPixelRed(left_image,p),r);
cristy76f512e2011-09-12 01:26:56 +00005006 SetPixelGreen(image,GetPixelGreen(right_image,q),r);
5007 SetPixelBlue(image,GetPixelBlue(right_image,q),r);
5008 if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
5009 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
5010 GetPixelAlpha(right_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00005011 p+=GetPixelChannels(left_image);
cristy76f512e2011-09-12 01:26:56 +00005012 q+=GetPixelChannels(right_image);
5013 r+=GetPixelChannels(stereo_image);
cristy3ed852e2009-09-05 21:47:34 +00005014 }
5015 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
5016 break;
cristy8b27a6d2010-02-14 03:31:15 +00005017 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005018 {
cristy8b27a6d2010-02-14 03:31:15 +00005019 MagickBooleanType
5020 proceed;
5021
cristybb503372010-05-27 20:51:26 +00005022 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
5023 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00005024 if (proceed == MagickFalse)
5025 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00005026 }
5027 }
cristyda16f162011-02-19 23:52:17 +00005028 if (status == MagickFalse)
5029 {
5030 stereo_image=DestroyImage(stereo_image);
5031 return((Image *) NULL);
5032 }
cristy3ed852e2009-09-05 21:47:34 +00005033 return(stereo_image);
5034}
5035
5036/*
5037%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5038% %
5039% %
5040% %
5041% S w i r l I m a g e %
5042% %
5043% %
5044% %
5045%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5046%
5047% SwirlImage() swirls the pixels about the center of the image, where
5048% degrees indicates the sweep of the arc through which each pixel is moved.
5049% You get a more dramatic effect as the degrees move from 1 to 360.
5050%
5051% The format of the SwirlImage method is:
5052%
5053% Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005054% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005055%
5056% A description of each parameter follows:
5057%
5058% o image: the image.
5059%
5060% o degrees: Define the tightness of the swirling effect.
5061%
cristy76f512e2011-09-12 01:26:56 +00005062% o method: the pixel interpolation method.
5063%
cristy3ed852e2009-09-05 21:47:34 +00005064% o exception: return any errors or warnings in this structure.
5065%
5066*/
5067MagickExport Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005068 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005069{
5070#define SwirlImageTag "Swirl/Image"
5071
cristyfa112112010-01-04 17:48:07 +00005072 CacheView
5073 *image_view,
5074 *swirl_view;
5075
cristy3ed852e2009-09-05 21:47:34 +00005076 Image
5077 *swirl_image;
5078
cristy3ed852e2009-09-05 21:47:34 +00005079 MagickBooleanType
5080 status;
5081
cristybb503372010-05-27 20:51:26 +00005082 MagickOffsetType
5083 progress;
5084
cristy3ed852e2009-09-05 21:47:34 +00005085 MagickRealType
5086 radius;
5087
5088 PointInfo
5089 center,
5090 scale;
5091
cristybb503372010-05-27 20:51:26 +00005092 ssize_t
5093 y;
5094
cristy3ed852e2009-09-05 21:47:34 +00005095 /*
5096 Initialize swirl image attributes.
5097 */
5098 assert(image != (const Image *) NULL);
5099 assert(image->signature == MagickSignature);
5100 if (image->debug != MagickFalse)
5101 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5102 assert(exception != (ExceptionInfo *) NULL);
5103 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00005104 swirl_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005105 if (swirl_image == (Image *) NULL)
5106 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005107 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005108 {
cristy3ed852e2009-09-05 21:47:34 +00005109 swirl_image=DestroyImage(swirl_image);
5110 return((Image *) NULL);
5111 }
cristy4c08aed2011-07-01 19:47:50 +00005112 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005113 swirl_image->matte=MagickTrue;
5114 /*
5115 Compute scaling factor.
5116 */
5117 center.x=(double) image->columns/2.0;
5118 center.y=(double) image->rows/2.0;
5119 radius=MagickMax(center.x,center.y);
5120 scale.x=1.0;
5121 scale.y=1.0;
5122 if (image->columns > image->rows)
5123 scale.y=(double) image->columns/(double) image->rows;
5124 else
5125 if (image->columns < image->rows)
5126 scale.x=(double) image->rows/(double) image->columns;
5127 degrees=(double) DegreesToRadians(degrees);
5128 /*
5129 Swirl image.
5130 */
5131 status=MagickTrue;
5132 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00005133 image_view=AcquireCacheView(image);
5134 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005135#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005136 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005137#endif
cristybb503372010-05-27 20:51:26 +00005138 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005139 {
cristy3ed852e2009-09-05 21:47:34 +00005140 MagickRealType
5141 distance;
5142
5143 PointInfo
5144 delta;
5145
cristy6d188022011-09-12 13:23:33 +00005146 register const Quantum
5147 *restrict p;
5148
cristybb503372010-05-27 20:51:26 +00005149 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005150 x;
5151
cristy4c08aed2011-07-01 19:47:50 +00005152 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005153 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005154
5155 if (status == MagickFalse)
5156 continue;
cristy6d188022011-09-12 13:23:33 +00005157 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00005158 q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00005159 exception);
cristy6d188022011-09-12 13:23:33 +00005160 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005161 {
5162 status=MagickFalse;
5163 continue;
5164 }
cristy3ed852e2009-09-05 21:47:34 +00005165 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00005166 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005167 {
5168 /*
5169 Determine if the pixel is within an ellipse.
5170 */
cristy10a6c612012-01-29 21:41:05 +00005171 if (GetPixelMask(image,p) != 0)
5172 {
5173 p+=GetPixelChannels(image);
5174 q+=GetPixelChannels(swirl_image);
5175 continue;
5176 }
cristy3ed852e2009-09-05 21:47:34 +00005177 delta.x=scale.x*(double) (x-center.x);
5178 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00005179 if (distance >= (radius*radius))
5180 {
cristy1707c6c2012-01-18 23:30:54 +00005181 register ssize_t
5182 i;
5183
cristy6d188022011-09-12 13:23:33 +00005184 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy1707c6c2012-01-18 23:30:54 +00005185 {
5186 PixelChannel
5187 channel;
5188
5189 PixelTrait
5190 swirl_traits,
5191 traits;
5192
5193 channel=GetPixelChannelMapChannel(image,i);
5194 traits=GetPixelChannelMapTraits(image,channel);
5195 swirl_traits=GetPixelChannelMapTraits(swirl_image,channel);
5196 if ((traits == UndefinedPixelTrait) ||
5197 (swirl_traits == UndefinedPixelTrait))
5198 continue;
5199 SetPixelChannel(swirl_image,channel,p[i],q);
5200 }
cristy6d188022011-09-12 13:23:33 +00005201 }
5202 else
cristy3ed852e2009-09-05 21:47:34 +00005203 {
5204 MagickRealType
5205 cosine,
5206 factor,
5207 sine;
5208
5209 /*
5210 Swirl the pixel.
5211 */
5212 factor=1.0-sqrt((double) distance)/radius;
5213 sine=sin((double) (degrees*factor*factor));
5214 cosine=cos((double) (degrees*factor*factor));
cristy76f512e2011-09-12 01:26:56 +00005215 status=InterpolatePixelChannels(image,image_view,swirl_image,method,
5216 ((cosine*delta.x-sine*delta.y)/scale.x+center.x),(double)
5217 ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00005218 }
cristy6d188022011-09-12 13:23:33 +00005219 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00005220 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00005221 }
5222 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5223 status=MagickFalse;
5224 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5225 {
5226 MagickBooleanType
5227 proceed;
5228
cristyb5d5f722009-11-04 03:03:49 +00005229#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005230 #pragma omp critical (MagickCore_SwirlImage)
cristy3ed852e2009-09-05 21:47:34 +00005231#endif
5232 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5233 if (proceed == MagickFalse)
5234 status=MagickFalse;
5235 }
5236 }
5237 swirl_view=DestroyCacheView(swirl_view);
5238 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005239 if (status == MagickFalse)
5240 swirl_image=DestroyImage(swirl_image);
5241 return(swirl_image);
5242}
5243
5244/*
5245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5246% %
5247% %
5248% %
5249% T i n t I m a g e %
5250% %
5251% %
5252% %
5253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5254%
5255% TintImage() applies a color vector to each pixel in the image. The length
5256% of the vector is 0 for black and white and at its maximum for the midtones.
5257% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5258%
5259% The format of the TintImage method is:
5260%
cristyb817c3f2011-10-03 14:00:35 +00005261% Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005262% const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005263%
5264% A description of each parameter follows:
5265%
5266% o image: the image.
5267%
cristyb817c3f2011-10-03 14:00:35 +00005268% o blend: A color value used for tinting.
cristy3ed852e2009-09-05 21:47:34 +00005269%
5270% o tint: A color value used for tinting.
5271%
5272% o exception: return any errors or warnings in this structure.
5273%
5274*/
cristyb817c3f2011-10-03 14:00:35 +00005275MagickExport Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005276 const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005277{
5278#define TintImageTag "Tint/Image"
5279
cristyc4c8d132010-01-07 01:58:38 +00005280 CacheView
5281 *image_view,
5282 *tint_view;
5283
cristy3ed852e2009-09-05 21:47:34 +00005284 GeometryInfo
5285 geometry_info;
5286
5287 Image
5288 *tint_image;
5289
cristy3ed852e2009-09-05 21:47:34 +00005290 MagickBooleanType
5291 status;
5292
cristybb503372010-05-27 20:51:26 +00005293 MagickOffsetType
5294 progress;
cristy3ed852e2009-09-05 21:47:34 +00005295
cristy28474bf2011-09-11 23:32:52 +00005296 MagickRealType
5297 intensity;
5298
cristy4c08aed2011-07-01 19:47:50 +00005299 PixelInfo
cristy1707c6c2012-01-18 23:30:54 +00005300 color_vector;
cristy3ed852e2009-09-05 21:47:34 +00005301
cristybb503372010-05-27 20:51:26 +00005302 MagickStatusType
5303 flags;
5304
5305 ssize_t
5306 y;
5307
cristy3ed852e2009-09-05 21:47:34 +00005308 /*
5309 Allocate tint image.
5310 */
5311 assert(image != (const Image *) NULL);
5312 assert(image->signature == MagickSignature);
5313 if (image->debug != MagickFalse)
5314 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5315 assert(exception != (ExceptionInfo *) NULL);
5316 assert(exception->signature == MagickSignature);
5317 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5318 if (tint_image == (Image *) NULL)
5319 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005320 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005321 {
cristy3ed852e2009-09-05 21:47:34 +00005322 tint_image=DestroyImage(tint_image);
5323 return((Image *) NULL);
5324 }
cristyaed9c382011-10-03 17:54:21 +00005325 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005326 return(tint_image);
5327 /*
5328 Determine RGB values of the color.
5329 */
cristy1707c6c2012-01-18 23:30:54 +00005330 GetPixelInfo(image,&color_vector);
cristyb817c3f2011-10-03 14:00:35 +00005331 flags=ParseGeometry(blend,&geometry_info);
cristy1707c6c2012-01-18 23:30:54 +00005332 color_vector.red=geometry_info.rho;
5333 color_vector.green=geometry_info.rho;
5334 color_vector.blue=geometry_info.rho;
5335 color_vector.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005336 if ((flags & SigmaValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005337 color_vector.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00005338 if ((flags & XiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005339 color_vector.blue=geometry_info.xi;
cristyb817c3f2011-10-03 14:00:35 +00005340 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005341 color_vector.alpha=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005342 if (image->colorspace == CMYKColorspace)
5343 {
cristy1707c6c2012-01-18 23:30:54 +00005344 color_vector.black=geometry_info.rho;
cristy76f512e2011-09-12 01:26:56 +00005345 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005346 color_vector.black=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005347 if ((flags & ChiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005348 color_vector.alpha=geometry_info.chi;
cristy76f512e2011-09-12 01:26:56 +00005349 }
cristy28474bf2011-09-11 23:32:52 +00005350 intensity=(MagickRealType) GetPixelInfoIntensity(tint);
cristy1707c6c2012-01-18 23:30:54 +00005351 color_vector.red=(MagickRealType) (color_vector.red*tint->red/100.0-
5352 intensity);
5353 color_vector.green=(MagickRealType) (color_vector.green*tint->green/100.0-
5354 intensity);
5355 color_vector.blue=(MagickRealType) (color_vector.blue*tint->blue/100.0-
5356 intensity);
5357 color_vector.black=(MagickRealType) (color_vector.black*tint->black/100.0-
5358 intensity);
5359 color_vector.alpha=(MagickRealType) (color_vector.alpha*tint->alpha/100.0-
5360 intensity);
cristy3ed852e2009-09-05 21:47:34 +00005361 /*
5362 Tint image.
5363 */
5364 status=MagickTrue;
5365 progress=0;
5366 image_view=AcquireCacheView(image);
5367 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005368#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005369 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005370#endif
cristybb503372010-05-27 20:51:26 +00005371 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005372 {
cristy4c08aed2011-07-01 19:47:50 +00005373 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005374 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005375
cristy4c08aed2011-07-01 19:47:50 +00005376 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005377 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005378
cristy6b91acb2011-04-19 12:23:54 +00005379 register ssize_t
5380 x;
5381
cristy3ed852e2009-09-05 21:47:34 +00005382 if (status == MagickFalse)
5383 continue;
5384 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5385 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5386 exception);
cristy4c08aed2011-07-01 19:47:50 +00005387 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005388 {
5389 status=MagickFalse;
5390 continue;
5391 }
cristybb503372010-05-27 20:51:26 +00005392 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005393 {
cristy4c08aed2011-07-01 19:47:50 +00005394 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005395 pixel;
5396
5397 MagickRealType
5398 weight;
5399
cristy1707c6c2012-01-18 23:30:54 +00005400 register ssize_t
5401 i;
5402
cristy10a6c612012-01-29 21:41:05 +00005403 if (GetPixelMask(image,p) != 0)
5404 {
5405 p+=GetPixelChannels(image);
5406 q+=GetPixelChannels(tint_image);
5407 continue;
5408 }
cristy1707c6c2012-01-18 23:30:54 +00005409 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
5410 {
5411 PixelChannel
5412 channel;
5413
5414 PixelTrait
5415 tint_traits,
5416 traits;
5417
5418 channel=GetPixelChannelMapChannel(image,i);
5419 traits=GetPixelChannelMapTraits(image,channel);
5420 tint_traits=GetPixelChannelMapTraits(tint_image,channel);
5421 if ((traits == UndefinedPixelTrait) ||
5422 (tint_traits == UndefinedPixelTrait))
5423 continue;
5424 if ((tint_traits & CopyPixelTrait) != 0)
5425 {
5426 SetPixelChannel(tint_image,channel,p[i],q);
5427 continue;
5428 }
5429 }
5430 GetPixelInfo(image,&pixel);
5431 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5432 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5433 (1.0-(4.0*(weight*weight)));
5434 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5435 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5436 (1.0-(4.0*(weight*weight)));
5437 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5438 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5439 (1.0-(4.0*(weight*weight)));
5440 weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5441 pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5442 (1.0-(4.0*(weight*weight)));
5443 SetPixelInfoPixel(tint_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005444 p+=GetPixelChannels(image);
5445 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005446 }
5447 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5448 status=MagickFalse;
5449 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5450 {
5451 MagickBooleanType
5452 proceed;
5453
cristyb5d5f722009-11-04 03:03:49 +00005454#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005455 #pragma omp critical (MagickCore_TintImage)
cristy3ed852e2009-09-05 21:47:34 +00005456#endif
5457 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5458 if (proceed == MagickFalse)
5459 status=MagickFalse;
5460 }
5461 }
5462 tint_view=DestroyCacheView(tint_view);
5463 image_view=DestroyCacheView(image_view);
5464 if (status == MagickFalse)
5465 tint_image=DestroyImage(tint_image);
5466 return(tint_image);
5467}
5468
5469/*
5470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5471% %
5472% %
5473% %
5474% V i g n e t t e I m a g e %
5475% %
5476% %
5477% %
5478%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5479%
5480% VignetteImage() softens the edges of the image in vignette style.
5481%
5482% The format of the VignetteImage method is:
5483%
5484% Image *VignetteImage(const Image *image,const double radius,
cristyeb6e6582011-12-09 09:14:23 +00005485% const double sigma,const double bias,const ssize_t x,const ssize_t y,
cristy05c0c9a2011-09-05 23:16:13 +00005486% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005487%
5488% A description of each parameter follows:
5489%
5490% o image: the image.
5491%
5492% o radius: the radius of the pixel neighborhood.
5493%
5494% o sigma: the standard deviation of the Gaussian, in pixels.
5495%
cristyeb6e6582011-12-09 09:14:23 +00005496% o bias: the bias.
5497%
cristy3ed852e2009-09-05 21:47:34 +00005498% o x, y: Define the x and y ellipse offset.
5499%
5500% o exception: return any errors or warnings in this structure.
5501%
5502*/
5503MagickExport Image *VignetteImage(const Image *image,const double radius,
cristyeb6e6582011-12-09 09:14:23 +00005504 const double sigma,const double bias,const ssize_t x,const ssize_t y,
5505 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005506{
5507 char
5508 ellipse[MaxTextExtent];
5509
5510 DrawInfo
5511 *draw_info;
5512
5513 Image
5514 *canvas_image,
5515 *blur_image,
5516 *oval_image,
5517 *vignette_image;
5518
5519 assert(image != (Image *) NULL);
5520 assert(image->signature == MagickSignature);
5521 if (image->debug != MagickFalse)
5522 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5523 assert(exception != (ExceptionInfo *) NULL);
5524 assert(exception->signature == MagickSignature);
5525 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5526 if (canvas_image == (Image *) NULL)
5527 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005528 if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005529 {
cristy3ed852e2009-09-05 21:47:34 +00005530 canvas_image=DestroyImage(canvas_image);
5531 return((Image *) NULL);
5532 }
5533 canvas_image->matte=MagickTrue;
cristy98621462011-12-31 22:31:11 +00005534 oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
5535 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005536 if (oval_image == (Image *) NULL)
5537 {
5538 canvas_image=DestroyImage(canvas_image);
5539 return((Image *) NULL);
5540 }
cristy9950d572011-10-01 18:22:35 +00005541 (void) QueryColorCompliance("#000000",AllCompliance,
5542 &oval_image->background_color,exception);
cristyea1a8aa2011-10-20 13:24:06 +00005543 (void) SetImageBackgroundColor(oval_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005544 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00005545 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5546 exception);
5547 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5548 exception);
cristy1707c6c2012-01-18 23:30:54 +00005549 (void) FormatLocaleString(ellipse,MaxTextExtent,"ellipse %g,%g,%g,%g,"
5550 "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5551 image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005552 draw_info->primitive=AcquireString(ellipse);
cristy018f07f2011-09-04 21:15:19 +00005553 (void) DrawImage(oval_image,draw_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005554 draw_info=DestroyDrawInfo(draw_info);
cristyeb6e6582011-12-09 09:14:23 +00005555 blur_image=BlurImage(oval_image,radius,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00005556 oval_image=DestroyImage(oval_image);
5557 if (blur_image == (Image *) NULL)
5558 {
5559 canvas_image=DestroyImage(canvas_image);
5560 return((Image *) NULL);
5561 }
5562 blur_image->matte=MagickFalse;
cristy98621462011-12-31 22:31:11 +00005563 (void) CompositeImage(canvas_image,IntensityCompositeOp,blur_image,0,0,
cristye941a752011-10-15 01:52:48 +00005564 exception);
cristy3ed852e2009-09-05 21:47:34 +00005565 blur_image=DestroyImage(blur_image);
5566 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5567 canvas_image=DestroyImage(canvas_image);
5568 return(vignette_image);
5569}
5570
5571/*
5572%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5573% %
5574% %
5575% %
5576% W a v e I m a g e %
5577% %
5578% %
5579% %
5580%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5581%
5582% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005583% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005584% by the given parameters.
5585%
5586% The format of the WaveImage method is:
5587%
5588% Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005589% const double wave_length,const PixelInterpolateMethod method,
5590% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005591%
5592% A description of each parameter follows:
5593%
5594% o image: the image.
5595%
5596% o amplitude, wave_length: Define the amplitude and wave length of the
5597% sine wave.
5598%
cristy5c4e2582011-09-11 19:21:03 +00005599% o interpolate: the pixel interpolation method.
5600%
cristy3ed852e2009-09-05 21:47:34 +00005601% o exception: return any errors or warnings in this structure.
5602%
5603*/
5604MagickExport Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005605 const double wave_length,const PixelInterpolateMethod method,
5606 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005607{
5608#define WaveImageTag "Wave/Image"
5609
cristyfa112112010-01-04 17:48:07 +00005610 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005611 *image_view,
cristyfa112112010-01-04 17:48:07 +00005612 *wave_view;
5613
cristy3ed852e2009-09-05 21:47:34 +00005614 Image
5615 *wave_image;
5616
cristy3ed852e2009-09-05 21:47:34 +00005617 MagickBooleanType
5618 status;
5619
cristybb503372010-05-27 20:51:26 +00005620 MagickOffsetType
5621 progress;
5622
cristy3ed852e2009-09-05 21:47:34 +00005623 MagickRealType
5624 *sine_map;
5625
cristybb503372010-05-27 20:51:26 +00005626 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005627 i;
5628
cristybb503372010-05-27 20:51:26 +00005629 ssize_t
5630 y;
5631
cristy3ed852e2009-09-05 21:47:34 +00005632 /*
5633 Initialize wave image attributes.
5634 */
5635 assert(image != (Image *) NULL);
5636 assert(image->signature == MagickSignature);
5637 if (image->debug != MagickFalse)
5638 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5639 assert(exception != (ExceptionInfo *) NULL);
5640 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005641 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005642 fabs(amplitude)),MagickTrue,exception);
5643 if (wave_image == (Image *) NULL)
5644 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005645 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005646 {
cristy3ed852e2009-09-05 21:47:34 +00005647 wave_image=DestroyImage(wave_image);
5648 return((Image *) NULL);
5649 }
cristy4c08aed2011-07-01 19:47:50 +00005650 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005651 wave_image->matte=MagickTrue;
5652 /*
5653 Allocate sine map.
5654 */
5655 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5656 sizeof(*sine_map));
5657 if (sine_map == (MagickRealType *) NULL)
5658 {
5659 wave_image=DestroyImage(wave_image);
5660 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5661 }
cristybb503372010-05-27 20:51:26 +00005662 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005663 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5664 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005665 /*
5666 Wave image.
5667 */
5668 status=MagickTrue;
5669 progress=0;
cristyd76c51e2011-03-26 00:21:26 +00005670 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005671 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005672 (void) SetCacheViewVirtualPixelMethod(image_view,
5673 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005674#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005675 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005676#endif
cristybb503372010-05-27 20:51:26 +00005677 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005678 {
cristy4c08aed2011-07-01 19:47:50 +00005679 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005680 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005681
cristye97bb922011-04-03 01:36:52 +00005682 register ssize_t
5683 x;
5684
cristy3ed852e2009-09-05 21:47:34 +00005685 if (status == MagickFalse)
5686 continue;
5687 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5688 exception);
cristyacd2ed22011-08-30 01:44:23 +00005689 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005690 {
5691 status=MagickFalse;
5692 continue;
5693 }
cristybb503372010-05-27 20:51:26 +00005694 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005695 {
cristy5c4e2582011-09-11 19:21:03 +00005696 status=InterpolatePixelChannels(image,image_view,wave_image,method,
5697 (double) x,(double) (y-sine_map[x]),q,exception);
cristyed231572011-07-14 02:18:59 +00005698 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005699 }
5700 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5701 status=MagickFalse;
5702 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5703 {
5704 MagickBooleanType
5705 proceed;
5706
cristyb5d5f722009-11-04 03:03:49 +00005707#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005708 #pragma omp critical (MagickCore_WaveImage)
cristy3ed852e2009-09-05 21:47:34 +00005709#endif
5710 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5711 if (proceed == MagickFalse)
5712 status=MagickFalse;
5713 }
5714 }
5715 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005716 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005717 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5718 if (status == MagickFalse)
5719 wave_image=DestroyImage(wave_image);
5720 return(wave_image);
5721}