blob: 2ca0240a17c45d44c86538d9e79d683bc119d9ea [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% FFFFF X X %
7% F X X %
8% FFF X %
9% F X X %
10% F X X %
11% %
12% %
13% MagickCore Image Special Effects Methods %
14% %
15% Software Design %
16% John Cristy %
17% October 1996 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/annotate.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/cache.h"
48#include "MagickCore/cache-view.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/composite.h"
52#include "MagickCore/decorate.h"
cristyc53413d2011-11-17 13:04:26 +000053#include "MagickCore/distort.h"
cristy4c08aed2011-07-01 19:47:50 +000054#include "MagickCore/draw.h"
55#include "MagickCore/effect.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/fx.h"
60#include "MagickCore/fx-private.h"
61#include "MagickCore/gem.h"
cristy8ea81222011-09-04 10:33:32 +000062#include "MagickCore/gem-private.h"
cristy4c08aed2011-07-01 19:47:50 +000063#include "MagickCore/geometry.h"
64#include "MagickCore/layer.h"
65#include "MagickCore/list.h"
66#include "MagickCore/log.h"
67#include "MagickCore/image.h"
68#include "MagickCore/image-private.h"
69#include "MagickCore/magick.h"
70#include "MagickCore/memory_.h"
71#include "MagickCore/monitor.h"
72#include "MagickCore/monitor-private.h"
73#include "MagickCore/option.h"
74#include "MagickCore/pixel.h"
75#include "MagickCore/pixel-accessor.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum.h"
78#include "MagickCore/quantum-private.h"
79#include "MagickCore/random_.h"
80#include "MagickCore/random-private.h"
81#include "MagickCore/resample.h"
82#include "MagickCore/resample-private.h"
83#include "MagickCore/resize.h"
cristy4c08aed2011-07-01 19:47:50 +000084#include "MagickCore/splay-tree.h"
85#include "MagickCore/statistic.h"
86#include "MagickCore/string_.h"
87#include "MagickCore/string-private.h"
88#include "MagickCore/thread-private.h"
89#include "MagickCore/transform.h"
90#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000091
92/*
93 Define declarations.
94*/
95#define LeftShiftOperator 0xf5
96#define RightShiftOperator 0xf6
97#define LessThanEqualOperator 0xf7
98#define GreaterThanEqualOperator 0xf8
99#define EqualOperator 0xf9
100#define NotEqualOperator 0xfa
101#define LogicalAndOperator 0xfb
102#define LogicalOrOperator 0xfc
cristy116af162010-08-13 01:25:47 +0000103#define ExponentialNotation 0xfd
cristy3ed852e2009-09-05 21:47:34 +0000104
105struct _FxInfo
106{
107 const Image
108 *images;
109
cristy3ed852e2009-09-05 21:47:34 +0000110 char
111 *expression;
112
113 FILE
114 *file;
115
116 SplayTreeInfo
117 *colors,
118 *symbols;
119
cristyd76c51e2011-03-26 00:21:26 +0000120 CacheView
121 **view;
cristy3ed852e2009-09-05 21:47:34 +0000122
123 RandomInfo
124 *random_info;
125
126 ExceptionInfo
127 *exception;
128};
129
130/*
131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
132% %
133% %
134% %
135+ A c q u i r e F x I n f o %
136% %
137% %
138% %
139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140%
141% AcquireFxInfo() allocates the FxInfo structure.
142%
143% The format of the AcquireFxInfo method is:
144%
145% FxInfo *AcquireFxInfo(Image *image,const char *expression)
cristy0a9b3722010-10-23 18:45:49 +0000146%
cristy3ed852e2009-09-05 21:47:34 +0000147% A description of each parameter follows:
148%
149% o image: the image.
150%
151% o expression: the expression.
152%
153*/
cristy7832dc22011-09-05 01:21:53 +0000154MagickPrivate FxInfo *AcquireFxInfo(const Image *image,const char *expression)
cristy3ed852e2009-09-05 21:47:34 +0000155{
156 char
157 fx_op[2];
158
cristycb180922011-03-11 14:41:24 +0000159 const Image
160 *next;
161
cristy3ed852e2009-09-05 21:47:34 +0000162 FxInfo
163 *fx_info;
164
cristybb503372010-05-27 20:51:26 +0000165 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000166 i;
167
cristy73bd4a52010-10-05 11:24:23 +0000168 fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +0000169 if (fx_info == (FxInfo *) NULL)
170 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
171 (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
172 fx_info->exception=AcquireExceptionInfo();
anthony7d86e172011-03-23 12:37:06 +0000173 fx_info->images=image;
cristy3ed852e2009-09-05 21:47:34 +0000174 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
175 RelinquishMagickMemory);
176 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
177 RelinquishMagickMemory);
cristyd76c51e2011-03-26 00:21:26 +0000178 fx_info->view=(CacheView **) AcquireQuantumMemory(GetImageListLength(
179 fx_info->images),sizeof(*fx_info->view));
180 if (fx_info->view == (CacheView **) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000181 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristya2262262011-03-11 02:50:37 +0000182 i=0;
cristy0ea377f2011-03-24 00:54:19 +0000183 next=GetFirstImageInList(fx_info->images);
184 for ( ; next != (Image *) NULL; next=next->next)
cristy3ed852e2009-09-05 21:47:34 +0000185 {
cristyd76c51e2011-03-26 00:21:26 +0000186 fx_info->view[i]=AcquireCacheView(next);
cristya2262262011-03-11 02:50:37 +0000187 i++;
cristy3ed852e2009-09-05 21:47:34 +0000188 }
189 fx_info->random_info=AcquireRandomInfo();
190 fx_info->expression=ConstantString(expression);
191 fx_info->file=stderr;
192 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
cristy37af0912011-05-23 16:09:42 +0000193 /*
194 Force right-to-left associativity for unary negation.
195 */
196 (void) SubstituteString(&fx_info->expression,"-","-1.0*");
cristy8b8a3ae2010-10-23 18:49:46 +0000197 /*
cristy3ed852e2009-09-05 21:47:34 +0000198 Convert complex to simple operators.
199 */
200 fx_op[1]='\0';
201 *fx_op=(char) LeftShiftOperator;
202 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
203 *fx_op=(char) RightShiftOperator;
204 (void) SubstituteString(&fx_info->expression,">>",fx_op);
205 *fx_op=(char) LessThanEqualOperator;
206 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
207 *fx_op=(char) GreaterThanEqualOperator;
208 (void) SubstituteString(&fx_info->expression,">=",fx_op);
209 *fx_op=(char) EqualOperator;
210 (void) SubstituteString(&fx_info->expression,"==",fx_op);
211 *fx_op=(char) NotEqualOperator;
212 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
213 *fx_op=(char) LogicalAndOperator;
214 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
215 *fx_op=(char) LogicalOrOperator;
216 (void) SubstituteString(&fx_info->expression,"||",fx_op);
cristy116af162010-08-13 01:25:47 +0000217 *fx_op=(char) ExponentialNotation;
218 (void) SubstituteString(&fx_info->expression,"**",fx_op);
cristy3ed852e2009-09-05 21:47:34 +0000219 return(fx_info);
220}
221
222/*
223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
224% %
225% %
226% %
227% A d d N o i s e I m a g e %
228% %
229% %
230% %
231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
232%
233% AddNoiseImage() adds random noise to the image.
234%
235% The format of the AddNoiseImage method is:
236%
237% Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
cristy9ed1f812011-10-08 02:00:08 +0000238% const double attenuate,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000239%
240% A description of each parameter follows:
241%
242% o image: the image.
243%
244% o channel: the channel type.
245%
246% o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
247% Impulse, Laplacian, or Poisson.
248%
cristy9ed1f812011-10-08 02:00:08 +0000249% o attenuate: attenuate the random distribution.
250%
cristy3ed852e2009-09-05 21:47:34 +0000251% o exception: return any errors or warnings in this structure.
252%
253*/
cristy9ed1f812011-10-08 02:00:08 +0000254MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
255 const double attenuate,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000256{
257#define AddNoiseImageTag "AddNoise/Image"
258
cristyfa112112010-01-04 17:48:07 +0000259 CacheView
260 *image_view,
261 *noise_view;
262
cristy3ed852e2009-09-05 21:47:34 +0000263 Image
264 *noise_image;
265
cristy3ed852e2009-09-05 21:47:34 +0000266 MagickBooleanType
267 status;
268
cristybb503372010-05-27 20:51:26 +0000269 MagickOffsetType
270 progress;
271
cristy3ed852e2009-09-05 21:47:34 +0000272 RandomInfo
cristyfa112112010-01-04 17:48:07 +0000273 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +0000274
cristybb503372010-05-27 20:51:26 +0000275 ssize_t
276 y;
277
cristy3ed852e2009-09-05 21:47:34 +0000278 /*
279 Initialize noise image attributes.
280 */
281 assert(image != (const Image *) NULL);
282 assert(image->signature == MagickSignature);
283 if (image->debug != MagickFalse)
284 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
285 assert(exception != (ExceptionInfo *) NULL);
286 assert(exception->signature == MagickSignature);
cristyb2145892011-10-10 00:55:32 +0000287 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000288 if (noise_image == (Image *) NULL)
289 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000290 if (SetImageStorageClass(noise_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000291 {
cristy3ed852e2009-09-05 21:47:34 +0000292 noise_image=DestroyImage(noise_image);
293 return((Image *) NULL);
294 }
295 /*
296 Add noise in each row.
297 */
cristy3ed852e2009-09-05 21:47:34 +0000298 status=MagickTrue;
299 progress=0;
300 random_info=AcquireRandomInfoThreadSet();
301 image_view=AcquireCacheView(image);
302 noise_view=AcquireCacheView(noise_image);
cristy319a1e72010-02-21 15:13:11 +0000303#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000304 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000305#endif
cristybb503372010-05-27 20:51:26 +0000306 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000307 {
cristy5c9e6f22010-09-17 17:31:01 +0000308 const int
309 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000310
cristy3ed852e2009-09-05 21:47:34 +0000311 MagickBooleanType
312 sync;
313
cristy4c08aed2011-07-01 19:47:50 +0000314 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000315 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000316
cristybb503372010-05-27 20:51:26 +0000317 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000318 x;
319
cristy4c08aed2011-07-01 19:47:50 +0000320 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000321 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000322
323 if (status == MagickFalse)
324 continue;
325 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +0000326 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +0000327 exception);
cristy4c08aed2011-07-01 19:47:50 +0000328 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000329 {
330 status=MagickFalse;
331 continue;
332 }
cristybb503372010-05-27 20:51:26 +0000333 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000334 {
cristy850b3072011-10-08 01:38:05 +0000335 register ssize_t
336 i;
337
338 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
339 {
340 PixelChannel
341 channel;
342
343 PixelTrait
344 noise_traits,
345 traits;
346
cristye2a912b2011-12-05 20:02:07 +0000347 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000348 traits=GetPixelChannelMapTraits(image,channel);
cristy850b3072011-10-08 01:38:05 +0000349 noise_traits=GetPixelChannelMapTraits(noise_image,channel);
350 if ((traits == UndefinedPixelTrait) ||
351 (noise_traits == UndefinedPixelTrait))
352 continue;
cristy9eeedea2011-11-02 19:04:05 +0000353 if ((noise_traits & CopyPixelTrait) != 0)
cristyb2145892011-10-10 00:55:32 +0000354 {
355 SetPixelChannel(noise_image,channel,p[i],q);
356 continue;
357 }
cristy850b3072011-10-08 01:38:05 +0000358 SetPixelChannel(noise_image,channel,ClampToQuantum(
359 GenerateDifferentialNoise(random_info[id],p[i],noise_type,attenuate)),
360 q);
361 }
cristyed231572011-07-14 02:18:59 +0000362 p+=GetPixelChannels(image);
363 q+=GetPixelChannels(noise_image);
cristy3ed852e2009-09-05 21:47:34 +0000364 }
365 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
366 if (sync == MagickFalse)
367 status=MagickFalse;
368 if (image->progress_monitor != (MagickProgressMonitor) NULL)
369 {
370 MagickBooleanType
371 proceed;
372
cristyb5d5f722009-11-04 03:03:49 +0000373#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy319a1e72010-02-21 15:13:11 +0000374 #pragma omp critical (MagickCore_AddNoiseImage)
cristy3ed852e2009-09-05 21:47:34 +0000375#endif
376 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
377 image->rows);
378 if (proceed == MagickFalse)
379 status=MagickFalse;
380 }
381 }
382 noise_view=DestroyCacheView(noise_view);
383 image_view=DestroyCacheView(image_view);
384 random_info=DestroyRandomInfoThreadSet(random_info);
385 if (status == MagickFalse)
386 noise_image=DestroyImage(noise_image);
387 return(noise_image);
388}
389
390/*
391%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
392% %
393% %
394% %
395% B l u e S h i f t I m a g e %
396% %
397% %
398% %
399%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400%
401% BlueShiftImage() mutes the colors of the image to simulate a scene at
402% nighttime in the moonlight.
403%
404% The format of the BlueShiftImage method is:
405%
406% Image *BlueShiftImage(const Image *image,const double factor,
407% ExceptionInfo *exception)
408%
409% A description of each parameter follows:
410%
411% o image: the image.
412%
413% o factor: the shift factor.
414%
415% o exception: return any errors or warnings in this structure.
416%
417*/
418MagickExport Image *BlueShiftImage(const Image *image,const double factor,
419 ExceptionInfo *exception)
420{
421#define BlueShiftImageTag "BlueShift/Image"
422
cristyc4c8d132010-01-07 01:58:38 +0000423 CacheView
424 *image_view,
425 *shift_view;
426
cristy3ed852e2009-09-05 21:47:34 +0000427 Image
428 *shift_image;
429
cristy3ed852e2009-09-05 21:47:34 +0000430 MagickBooleanType
431 status;
432
cristybb503372010-05-27 20:51:26 +0000433 MagickOffsetType
434 progress;
435
436 ssize_t
437 y;
438
cristy3ed852e2009-09-05 21:47:34 +0000439 /*
440 Allocate blue shift image.
441 */
442 assert(image != (const Image *) NULL);
443 assert(image->signature == MagickSignature);
444 if (image->debug != MagickFalse)
445 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
446 assert(exception != (ExceptionInfo *) NULL);
447 assert(exception->signature == MagickSignature);
cristya6d7a9b2012-01-18 20:04:48 +0000448 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000449 if (shift_image == (Image *) NULL)
450 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000451 if (SetImageStorageClass(shift_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000452 {
cristy3ed852e2009-09-05 21:47:34 +0000453 shift_image=DestroyImage(shift_image);
454 return((Image *) NULL);
455 }
456 /*
457 Blue-shift DirectClass image.
458 */
459 status=MagickTrue;
460 progress=0;
461 image_view=AcquireCacheView(image);
462 shift_view=AcquireCacheView(shift_image);
cristy319a1e72010-02-21 15:13:11 +0000463#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000464 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000465#endif
cristybb503372010-05-27 20:51:26 +0000466 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000467 {
468 MagickBooleanType
469 sync;
470
cristy4c08aed2011-07-01 19:47:50 +0000471 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000472 pixel;
473
474 Quantum
475 quantum;
476
cristy4c08aed2011-07-01 19:47:50 +0000477 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000478 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000479
cristybb503372010-05-27 20:51:26 +0000480 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000481 x;
482
cristy4c08aed2011-07-01 19:47:50 +0000483 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000484 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000485
486 if (status == MagickFalse)
487 continue;
488 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
489 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
490 exception);
cristy4c08aed2011-07-01 19:47:50 +0000491 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000492 {
493 status=MagickFalse;
494 continue;
495 }
cristybb503372010-05-27 20:51:26 +0000496 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000497 {
cristy4c08aed2011-07-01 19:47:50 +0000498 quantum=GetPixelRed(image,p);
499 if (GetPixelGreen(image,p) < quantum)
500 quantum=GetPixelGreen(image,p);
501 if (GetPixelBlue(image,p) < quantum)
502 quantum=GetPixelBlue(image,p);
503 pixel.red=0.5*(GetPixelRed(image,p)+factor*quantum);
504 pixel.green=0.5*(GetPixelGreen(image,p)+factor*quantum);
505 pixel.blue=0.5*(GetPixelBlue(image,p)+factor*quantum);
506 quantum=GetPixelRed(image,p);
507 if (GetPixelGreen(image,p) > quantum)
508 quantum=GetPixelGreen(image,p);
509 if (GetPixelBlue(image,p) > quantum)
510 quantum=GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000511 pixel.red=0.5*(pixel.red+factor*quantum);
512 pixel.green=0.5*(pixel.green+factor*quantum);
513 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristy4c08aed2011-07-01 19:47:50 +0000514 SetPixelRed(shift_image,ClampToQuantum(pixel.red),q);
515 SetPixelGreen(shift_image,ClampToQuantum(pixel.green),q);
516 SetPixelBlue(shift_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000517 p+=GetPixelChannels(image);
518 q+=GetPixelChannels(shift_image);
cristy3ed852e2009-09-05 21:47:34 +0000519 }
520 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
521 if (sync == MagickFalse)
522 status=MagickFalse;
523 if (image->progress_monitor != (MagickProgressMonitor) NULL)
524 {
525 MagickBooleanType
526 proceed;
527
cristy319a1e72010-02-21 15:13:11 +0000528#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000529 #pragma omp critical (MagickCore_BlueShiftImage)
530#endif
531 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
532 image->rows);
533 if (proceed == MagickFalse)
534 status=MagickFalse;
535 }
536 }
537 image_view=DestroyCacheView(image_view);
538 shift_view=DestroyCacheView(shift_view);
539 if (status == MagickFalse)
540 shift_image=DestroyImage(shift_image);
541 return(shift_image);
542}
543
544/*
545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
546% %
547% %
548% %
549% C h a r c o a l I m a g e %
550% %
551% %
552% %
553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
554%
555% CharcoalImage() creates a new image that is a copy of an existing one with
556% the edge highlighted. It allocates the memory necessary for the new Image
557% structure and returns a pointer to the new image.
558%
559% The format of the CharcoalImage method is:
560%
561% Image *CharcoalImage(const Image *image,const double radius,
cristy05c0c9a2011-09-05 23:16:13 +0000562% const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000563%
564% A description of each parameter follows:
565%
566% o image: the image.
567%
568% o radius: the radius of the pixel neighborhood.
569%
570% o sigma: the standard deviation of the Gaussian, in pixels.
571%
cristy05c0c9a2011-09-05 23:16:13 +0000572% o bias: the bias.
573%
cristy3ed852e2009-09-05 21:47:34 +0000574% o exception: return any errors or warnings in this structure.
575%
576*/
577MagickExport Image *CharcoalImage(const Image *image,const double radius,
cristy05c0c9a2011-09-05 23:16:13 +0000578 const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000579{
580 Image
581 *charcoal_image,
582 *clone_image,
583 *edge_image;
584
585 assert(image != (Image *) NULL);
586 assert(image->signature == MagickSignature);
587 if (image->debug != MagickFalse)
588 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
589 assert(exception != (ExceptionInfo *) NULL);
590 assert(exception->signature == MagickSignature);
591 clone_image=CloneImage(image,0,0,MagickTrue,exception);
592 if (clone_image == (Image *) NULL)
593 return((Image *) NULL);
cristy018f07f2011-09-04 21:15:19 +0000594 (void) SetImageType(clone_image,GrayscaleType,exception);
cristy8ae632d2011-09-05 17:29:53 +0000595 edge_image=EdgeImage(clone_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000596 clone_image=DestroyImage(clone_image);
597 if (edge_image == (Image *) NULL)
598 return((Image *) NULL);
cristy05c0c9a2011-09-05 23:16:13 +0000599 charcoal_image=BlurImage(edge_image,radius,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +0000600 edge_image=DestroyImage(edge_image);
601 if (charcoal_image == (Image *) NULL)
602 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +0000603 (void) NormalizeImage(charcoal_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +0000604 (void) NegateImage(charcoal_image,MagickFalse,exception);
cristy018f07f2011-09-04 21:15:19 +0000605 (void) SetImageType(charcoal_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000606 return(charcoal_image);
607}
608
609/*
610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
611% %
612% %
613% %
614% C o l o r i z e I m a g e %
615% %
616% %
617% %
618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
619%
620% ColorizeImage() blends the fill color with each pixel in the image.
621% A percentage blend is specified with opacity. Control the application
622% of different color components by specifying a different percentage for
623% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
624%
625% The format of the ColorizeImage method is:
626%
cristyc7e6ff62011-10-03 13:46:11 +0000627% Image *ColorizeImage(const Image *image,const char *blend,
628% const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000629%
630% A description of each parameter follows:
631%
632% o image: the image.
633%
cristyc7e6ff62011-10-03 13:46:11 +0000634% o blend: A character string indicating the level of blending as a
cristy3ed852e2009-09-05 21:47:34 +0000635% percentage.
636%
637% o colorize: A color value.
638%
639% o exception: return any errors or warnings in this structure.
640%
641*/
cristyc7e6ff62011-10-03 13:46:11 +0000642MagickExport Image *ColorizeImage(const Image *image,const char *blend,
643 const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000644{
645#define ColorizeImageTag "Colorize/Image"
646
cristyc4c8d132010-01-07 01:58:38 +0000647 CacheView
648 *colorize_view,
649 *image_view;
650
cristy3ed852e2009-09-05 21:47:34 +0000651 GeometryInfo
652 geometry_info;
653
654 Image
655 *colorize_image;
656
cristy3ed852e2009-09-05 21:47:34 +0000657 MagickBooleanType
658 status;
659
cristybb503372010-05-27 20:51:26 +0000660 MagickOffsetType
661 progress;
662
cristy3ed852e2009-09-05 21:47:34 +0000663 MagickStatusType
664 flags;
665
cristyc7e6ff62011-10-03 13:46:11 +0000666 PixelInfo
667 pixel;
668
cristybb503372010-05-27 20:51:26 +0000669 ssize_t
670 y;
671
cristy3ed852e2009-09-05 21:47:34 +0000672 /*
673 Allocate colorized image.
674 */
675 assert(image != (const Image *) NULL);
676 assert(image->signature == MagickSignature);
677 if (image->debug != MagickFalse)
678 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
679 assert(exception != (ExceptionInfo *) NULL);
680 assert(exception->signature == MagickSignature);
681 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
682 exception);
683 if (colorize_image == (Image *) NULL)
684 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000685 if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000686 {
cristy3ed852e2009-09-05 21:47:34 +0000687 colorize_image=DestroyImage(colorize_image);
688 return((Image *) NULL);
689 }
cristyc7e6ff62011-10-03 13:46:11 +0000690 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000691 return(colorize_image);
692 /*
anthonyfd706f92012-01-19 04:22:02 +0000693 Determine RGB values of the fill color for pixel
cristy3ed852e2009-09-05 21:47:34 +0000694 */
cristyc7e6ff62011-10-03 13:46:11 +0000695 GetPixelInfo(image,&pixel);
cristyb817c3f2011-10-03 14:00:35 +0000696 flags=ParseGeometry(blend,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000697 pixel.red=geometry_info.rho;
698 pixel.green=geometry_info.rho;
699 pixel.blue=geometry_info.rho;
cristyc7e6ff62011-10-03 13:46:11 +0000700 pixel.alpha=100.0;
cristy3ed852e2009-09-05 21:47:34 +0000701 if ((flags & SigmaValue) != 0)
702 pixel.green=geometry_info.sigma;
703 if ((flags & XiValue) != 0)
704 pixel.blue=geometry_info.xi;
705 if ((flags & PsiValue) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000706 pixel.alpha=geometry_info.psi;
cristyc7e6ff62011-10-03 13:46:11 +0000707 if (pixel.colorspace == CMYKColorspace)
708 {
709 pixel.black=geometry_info.rho;
710 if ((flags & PsiValue) != 0)
711 pixel.black=geometry_info.psi;
712 if ((flags & ChiValue) != 0)
713 pixel.alpha=geometry_info.chi;
714 }
cristy3ed852e2009-09-05 21:47:34 +0000715 /*
716 Colorize DirectClass image.
717 */
718 status=MagickTrue;
719 progress=0;
720 image_view=AcquireCacheView(image);
721 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000722#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000723 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000724#endif
cristybb503372010-05-27 20:51:26 +0000725 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000726 {
727 MagickBooleanType
728 sync;
729
cristy4c08aed2011-07-01 19:47:50 +0000730 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000731 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000732
cristybb503372010-05-27 20:51:26 +0000733 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000734 x;
735
cristy4c08aed2011-07-01 19:47:50 +0000736 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000737 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000738
739 if (status == MagickFalse)
740 continue;
741 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
742 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
743 exception);
cristy4c08aed2011-07-01 19:47:50 +0000744 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000745 {
746 status=MagickFalse;
747 continue;
748 }
cristybb503372010-05-27 20:51:26 +0000749 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000750 {
cristyc7e6ff62011-10-03 13:46:11 +0000751 register ssize_t
752 i;
753
754 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
755 {
756 PixelChannel
757 channel;
758
759 PixelTrait
760 colorize_traits,
761 traits;
762
cristye2a912b2011-12-05 20:02:07 +0000763 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000764 traits=GetPixelChannelMapTraits(image,channel);
cristyc7e6ff62011-10-03 13:46:11 +0000765 colorize_traits=GetPixelChannelMapTraits(colorize_image,channel);
766 if ((traits == UndefinedPixelTrait) ||
767 (colorize_traits == UndefinedPixelTrait))
768 continue;
769 if ((colorize_traits & CopyPixelTrait) != 0)
770 {
771 SetPixelChannel(colorize_image,channel,p[i],q);
772 continue;
773 }
774 switch (channel)
775 {
776 case RedPixelChannel:
777 {
778 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
779 (100.0-pixel.red)+colorize->red*pixel.red)/100.0),q);
780 break;
781 }
782 case GreenPixelChannel:
783 {
784 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
785 (100.0-pixel.green)+colorize->green*pixel.green)/100.0),q);
786 break;
787 }
788 case BluePixelChannel:
789 {
790 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
791 (100.0-pixel.blue)+colorize->blue*pixel.blue)/100.0),q);
792 break;
793 }
794 case BlackPixelChannel:
795 {
796 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
797 (100.0-pixel.black)+colorize->black*pixel.black)/100.0),q);
798 break;
799 }
800 case AlphaPixelChannel:
801 {
802 SetPixelChannel(colorize_image,channel,ClampToQuantum((p[i]*
803 (100.0-pixel.alpha)+colorize->alpha*pixel.alpha)/100.0),q);
804 break;
805 }
806 default:
807 {
808 SetPixelChannel(colorize_image,channel,p[i],q);
809 break;
810 }
811 }
812 }
cristyed231572011-07-14 02:18:59 +0000813 p+=GetPixelChannels(image);
814 q+=GetPixelChannels(colorize_image);
cristy3ed852e2009-09-05 21:47:34 +0000815 }
816 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
817 if (sync == MagickFalse)
818 status=MagickFalse;
819 if (image->progress_monitor != (MagickProgressMonitor) NULL)
820 {
821 MagickBooleanType
822 proceed;
823
cristy319a1e72010-02-21 15:13:11 +0000824#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000825 #pragma omp critical (MagickCore_ColorizeImage)
826#endif
827 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
828 if (proceed == MagickFalse)
829 status=MagickFalse;
830 }
831 }
832 image_view=DestroyCacheView(image_view);
833 colorize_view=DestroyCacheView(colorize_view);
834 if (status == MagickFalse)
835 colorize_image=DestroyImage(colorize_image);
836 return(colorize_image);
837}
838
839/*
840%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
841% %
842% %
843% %
cristye6365592010-04-02 17:31:23 +0000844% C o l o r M a t r i x I m a g e %
845% %
846% %
847% %
848%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
849%
850% ColorMatrixImage() applies color transformation to an image. This method
851% permits saturation changes, hue rotation, luminance to alpha, and various
852% other effects. Although variable-sized transformation matrices can be used,
853% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
854% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
855% except offsets are in column 6 rather than 5 (in support of CMYKA images)
856% and offsets are normalized (divide Flash offset by 255).
857%
858% The format of the ColorMatrixImage method is:
859%
860% Image *ColorMatrixImage(const Image *image,
861% const KernelInfo *color_matrix,ExceptionInfo *exception)
862%
863% A description of each parameter follows:
864%
865% o image: the image.
866%
867% o color_matrix: the color matrix.
868%
869% o exception: return any errors or warnings in this structure.
870%
871*/
anthonyfd706f92012-01-19 04:22:02 +0000872/* FUTURE: modify to make use of a MagickMatrix Mutliply function
873 That should be provided in "matrix.c"
874 (ASIDE: actually distorts should do this too but currently doesn't)
875*/
876
cristye6365592010-04-02 17:31:23 +0000877MagickExport Image *ColorMatrixImage(const Image *image,
878 const KernelInfo *color_matrix,ExceptionInfo *exception)
879{
880#define ColorMatrixImageTag "ColorMatrix/Image"
881
882 CacheView
883 *color_view,
884 *image_view;
885
886 double
887 ColorMatrix[6][6] =
888 {
889 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
890 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
891 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
892 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
893 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
894 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
895 };
896
897 Image
898 *color_image;
899
cristye6365592010-04-02 17:31:23 +0000900 MagickBooleanType
901 status;
902
cristybb503372010-05-27 20:51:26 +0000903 MagickOffsetType
904 progress;
905
906 register ssize_t
cristye6365592010-04-02 17:31:23 +0000907 i;
908
cristybb503372010-05-27 20:51:26 +0000909 ssize_t
910 u,
911 v,
912 y;
913
cristye6365592010-04-02 17:31:23 +0000914 /*
anthonyfd706f92012-01-19 04:22:02 +0000915 Map given color_matrix, into a 6x6 matrix RGBKA and a constant
cristye6365592010-04-02 17:31:23 +0000916 */
917 assert(image != (Image *) NULL);
918 assert(image->signature == MagickSignature);
919 if (image->debug != MagickFalse)
920 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
921 assert(exception != (ExceptionInfo *) NULL);
922 assert(exception->signature == MagickSignature);
923 i=0;
cristybb503372010-05-27 20:51:26 +0000924 for (v=0; v < (ssize_t) color_matrix->height; v++)
925 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000926 {
927 if ((v < 6) && (u < 6))
928 ColorMatrix[v][u]=color_matrix->values[i];
929 i++;
930 }
931 /*
932 Initialize color image.
933 */
cristy12550e62010-06-07 12:46:40 +0000934 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000935 if (color_image == (Image *) NULL)
936 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000937 if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
cristye6365592010-04-02 17:31:23 +0000938 {
cristye6365592010-04-02 17:31:23 +0000939 color_image=DestroyImage(color_image);
940 return((Image *) NULL);
941 }
942 if (image->debug != MagickFalse)
943 {
944 char
945 format[MaxTextExtent],
946 *message;
947
948 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
949 " ColorMatrix image with color matrix:");
950 message=AcquireString("");
951 for (v=0; v < 6; v++)
952 {
953 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000954 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000955 (void) ConcatenateString(&message,format);
956 for (u=0; u < 6; u++)
957 {
cristyb51dff52011-05-19 16:55:47 +0000958 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
cristye6365592010-04-02 17:31:23 +0000959 ColorMatrix[v][u]);
960 (void) ConcatenateString(&message,format);
961 }
962 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
963 }
964 message=DestroyString(message);
965 }
966 /*
anthonyfd706f92012-01-19 04:22:02 +0000967 Apply the ColorMatrix to image.
cristye6365592010-04-02 17:31:23 +0000968 */
969 status=MagickTrue;
970 progress=0;
971 image_view=AcquireCacheView(image);
972 color_view=AcquireCacheView(color_image);
973#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000974 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye6365592010-04-02 17:31:23 +0000975#endif
cristybb503372010-05-27 20:51:26 +0000976 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000977 {
978 MagickRealType
979 pixel;
980
cristy4c08aed2011-07-01 19:47:50 +0000981 register const Quantum
cristye6365592010-04-02 17:31:23 +0000982 *restrict p;
983
cristy4c08aed2011-07-01 19:47:50 +0000984 register Quantum
985 *restrict q;
986
cristybb503372010-05-27 20:51:26 +0000987 register ssize_t
cristye6365592010-04-02 17:31:23 +0000988 x;
989
cristye6365592010-04-02 17:31:23 +0000990 if (status == MagickFalse)
991 continue;
992 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
993 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
994 exception);
cristy4c08aed2011-07-01 19:47:50 +0000995 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristye6365592010-04-02 17:31:23 +0000996 {
997 status=MagickFalse;
998 continue;
999 }
cristybb503372010-05-27 20:51:26 +00001000 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +00001001 {
cristybb503372010-05-27 20:51:26 +00001002 register ssize_t
cristye6365592010-04-02 17:31:23 +00001003 v;
1004
cristybb503372010-05-27 20:51:26 +00001005 size_t
cristye6365592010-04-02 17:31:23 +00001006 height;
1007
1008 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +00001009 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +00001010 {
cristy4c08aed2011-07-01 19:47:50 +00001011 pixel=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
1012 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +00001013 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001014 pixel+=ColorMatrix[v][3]*GetPixelBlack(image,p);
1015 if (image->matte != MagickFalse)
1016 pixel+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
cristye6365592010-04-02 17:31:23 +00001017 pixel+=QuantumRange*ColorMatrix[v][5];
1018 switch (v)
1019 {
cristy4c08aed2011-07-01 19:47:50 +00001020 case 0: SetPixelRed(color_image,ClampToQuantum(pixel),q); break;
1021 case 1: SetPixelGreen(color_image,ClampToQuantum(pixel),q); break;
1022 case 2: SetPixelBlue(color_image,ClampToQuantum(pixel),q); break;
cristye6365592010-04-02 17:31:23 +00001023 case 3:
1024 {
cristy4c08aed2011-07-01 19:47:50 +00001025 if (image->colorspace == CMYKColorspace)
1026 SetPixelBlack(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001027 break;
1028 }
1029 case 4:
1030 {
cristy4c08aed2011-07-01 19:47:50 +00001031 if (image->matte != MagickFalse)
1032 SetPixelAlpha(color_image,ClampToQuantum(pixel),q);
cristye6365592010-04-02 17:31:23 +00001033 break;
1034 }
1035 }
1036 }
cristyed231572011-07-14 02:18:59 +00001037 p+=GetPixelChannels(image);
1038 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +00001039 }
1040 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1041 status=MagickFalse;
1042 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1043 {
1044 MagickBooleanType
1045 proceed;
1046
1047#if defined(MAGICKCORE_OPENMP_SUPPORT)
1048 #pragma omp critical (MagickCore_ColorMatrixImage)
1049#endif
1050 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1051 image->rows);
1052 if (proceed == MagickFalse)
1053 status=MagickFalse;
1054 }
1055 }
1056 color_view=DestroyCacheView(color_view);
1057 image_view=DestroyCacheView(image_view);
1058 if (status == MagickFalse)
1059 color_image=DestroyImage(color_image);
1060 return(color_image);
1061}
1062
1063/*
1064%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1065% %
1066% %
1067% %
cristy3ed852e2009-09-05 21:47:34 +00001068+ D e s t r o y F x I n f o %
1069% %
1070% %
1071% %
1072%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1073%
1074% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1075%
1076% The format of the DestroyFxInfo method is:
1077%
1078% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1079%
1080% A description of each parameter follows:
1081%
1082% o fx_info: the fx info.
1083%
1084*/
cristy7832dc22011-09-05 01:21:53 +00001085MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
cristy3ed852e2009-09-05 21:47:34 +00001086{
cristybb503372010-05-27 20:51:26 +00001087 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001088 i;
1089
1090 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1091 fx_info->expression=DestroyString(fx_info->expression);
1092 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1093 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001094 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001095 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1096 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001097 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1098 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1099 return(fx_info);
1100}
1101
1102/*
1103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1104% %
1105% %
1106% %
cristy3ed852e2009-09-05 21:47:34 +00001107+ F x E v a l u a t e C h a n n e l E x p r e s s i o n %
1108% %
1109% %
1110% %
1111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1112%
1113% FxEvaluateChannelExpression() evaluates an expression and returns the
1114% results.
1115%
1116% The format of the FxEvaluateExpression method is:
1117%
1118% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001119% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001120% MagickRealType *alpha,Exceptioninfo *exception)
1121% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1122% MagickRealType *alpha,Exceptioninfo *exception)
1123%
1124% A description of each parameter follows:
1125%
1126% o fx_info: the fx info.
1127%
1128% o channel: the channel.
1129%
1130% o x,y: the pixel position.
1131%
1132% o alpha: the result.
1133%
1134% o exception: return any errors or warnings in this structure.
1135%
1136*/
1137
cristy351842f2010-03-07 15:27:38 +00001138static inline double MagickMax(const double x,const double y)
1139{
1140 if (x > y)
1141 return(x);
1142 return(y);
1143}
1144
1145static inline double MagickMin(const double x,const double y)
1146{
1147 if (x < y)
1148 return(x);
1149 return(y);
1150}
1151
cristy3ed852e2009-09-05 21:47:34 +00001152static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001153 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001154{
1155 char
1156 key[MaxTextExtent],
1157 statistic[MaxTextExtent];
1158
1159 const char
1160 *value;
1161
1162 register const char
1163 *p;
1164
1165 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1166 if (*p == '.')
1167 switch (*++p) /* e.g. depth.r */
1168 {
cristy541ae572011-08-05 19:08:59 +00001169 case 'r': channel=RedPixelChannel; break;
1170 case 'g': channel=GreenPixelChannel; break;
1171 case 'b': channel=BluePixelChannel; break;
1172 case 'c': channel=CyanPixelChannel; break;
1173 case 'm': channel=MagentaPixelChannel; break;
1174 case 'y': channel=YellowPixelChannel; break;
1175 case 'k': channel=BlackPixelChannel; break;
cristy3ed852e2009-09-05 21:47:34 +00001176 default: break;
1177 }
cristyb51dff52011-05-19 16:55:47 +00001178 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001179 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001180 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1181 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001182 return(QuantumScale*StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001183 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1184 if (LocaleNCompare(symbol,"depth",5) == 0)
1185 {
cristybb503372010-05-27 20:51:26 +00001186 size_t
cristy3ed852e2009-09-05 21:47:34 +00001187 depth;
1188
cristyfefab1b2011-07-05 00:33:22 +00001189 depth=GetImageDepth(image,exception);
1190 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001191 }
1192 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1193 {
1194 double
1195 kurtosis,
1196 skewness;
1197
cristyd42d9952011-07-08 14:21:50 +00001198 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001199 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001200 }
1201 if (LocaleNCompare(symbol,"maxima",6) == 0)
1202 {
1203 double
1204 maxima,
1205 minima;
1206
cristyd42d9952011-07-08 14:21:50 +00001207 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001208 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001209 }
1210 if (LocaleNCompare(symbol,"mean",4) == 0)
1211 {
1212 double
1213 mean,
1214 standard_deviation;
1215
cristyd42d9952011-07-08 14:21:50 +00001216 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001217 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001218 }
1219 if (LocaleNCompare(symbol,"minima",6) == 0)
1220 {
1221 double
1222 maxima,
1223 minima;
1224
cristyd42d9952011-07-08 14:21:50 +00001225 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001226 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001227 }
1228 if (LocaleNCompare(symbol,"skewness",8) == 0)
1229 {
1230 double
1231 kurtosis,
1232 skewness;
1233
cristyd42d9952011-07-08 14:21:50 +00001234 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001235 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001236 }
1237 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1238 {
1239 double
1240 mean,
1241 standard_deviation;
1242
cristyd42d9952011-07-08 14:21:50 +00001243 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001244 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001245 standard_deviation);
1246 }
1247 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1248 ConstantString(statistic));
cristydbdd0e32011-11-04 23:29:40 +00001249 return(QuantumScale*StringToDouble(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001250}
1251
1252static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001253 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001254 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001255
cristyb0aad4c2011-11-02 19:30:35 +00001256static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
1257{
1258 if (beta != 0)
1259 return(FxGCD(beta,alpha % beta));
1260 return(alpha);
1261}
1262
cristy3ed852e2009-09-05 21:47:34 +00001263static inline const char *FxSubexpression(const char *expression,
1264 ExceptionInfo *exception)
1265{
1266 const char
1267 *subexpression;
1268
cristybb503372010-05-27 20:51:26 +00001269 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001270 level;
1271
1272 level=0;
1273 subexpression=expression;
1274 while ((*subexpression != '\0') &&
1275 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1276 {
1277 if (strchr("(",(int) *subexpression) != (char *) NULL)
1278 level++;
1279 else
1280 if (strchr(")",(int) *subexpression) != (char *) NULL)
1281 level--;
1282 subexpression++;
1283 }
1284 if (*subexpression == '\0')
1285 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1286 "UnbalancedParenthesis","`%s'",expression);
1287 return(subexpression);
1288}
1289
cristy0568ffc2011-07-25 16:54:14 +00001290static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001291 const ssize_t x,const ssize_t y,const char *expression,
1292 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001293{
1294 char
1295 *q,
1296 subexpression[MaxTextExtent],
1297 symbol[MaxTextExtent];
1298
1299 const char
1300 *p,
1301 *value;
1302
1303 Image
1304 *image;
1305
cristy4c08aed2011-07-01 19:47:50 +00001306 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001307 pixel;
1308
1309 MagickRealType
1310 alpha,
1311 beta;
1312
1313 PointInfo
1314 point;
1315
cristybb503372010-05-27 20:51:26 +00001316 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001317 i;
1318
1319 size_t
cristy1707c6c2012-01-18 23:30:54 +00001320 length,
cristy3ed852e2009-09-05 21:47:34 +00001321 level;
1322
1323 p=expression;
1324 i=GetImageIndexInList(fx_info->images);
1325 level=0;
1326 point.x=(double) x;
1327 point.y=(double) y;
1328 if (isalpha((int) *(p+1)) == 0)
1329 {
1330 if (strchr("suv",(int) *p) != (char *) NULL)
1331 {
1332 switch (*p)
1333 {
1334 case 's':
1335 default:
1336 {
1337 i=GetImageIndexInList(fx_info->images);
1338 break;
1339 }
1340 case 'u': i=0; break;
1341 case 'v': i=1; break;
1342 }
1343 p++;
1344 if (*p == '[')
1345 {
1346 level++;
1347 q=subexpression;
1348 for (p++; *p != '\0'; )
1349 {
1350 if (*p == '[')
1351 level++;
1352 else
1353 if (*p == ']')
1354 {
1355 level--;
1356 if (level == 0)
1357 break;
1358 }
1359 *q++=(*p++);
1360 }
1361 *q='\0';
1362 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1363 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001364 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001365 p++;
1366 }
1367 if (*p == '.')
1368 p++;
1369 }
1370 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1371 {
1372 p++;
1373 if (*p == '{')
1374 {
1375 level++;
1376 q=subexpression;
1377 for (p++; *p != '\0'; )
1378 {
1379 if (*p == '{')
1380 level++;
1381 else
1382 if (*p == '}')
1383 {
1384 level--;
1385 if (level == 0)
1386 break;
1387 }
1388 *q++=(*p++);
1389 }
1390 *q='\0';
1391 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1392 &beta,exception);
1393 point.x=alpha;
1394 point.y=beta;
1395 p++;
1396 }
1397 else
1398 if (*p == '[')
1399 {
1400 level++;
1401 q=subexpression;
1402 for (p++; *p != '\0'; )
1403 {
1404 if (*p == '[')
1405 level++;
1406 else
1407 if (*p == ']')
1408 {
1409 level--;
1410 if (level == 0)
1411 break;
1412 }
1413 *q++=(*p++);
1414 }
1415 *q='\0';
1416 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1417 &beta,exception);
1418 point.x+=alpha;
1419 point.y+=beta;
1420 p++;
1421 }
1422 if (*p == '.')
1423 p++;
1424 }
1425 }
1426 length=GetImageListLength(fx_info->images);
1427 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001428 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001429 i%=length;
1430 image=GetImageFromList(fx_info->images,i);
1431 if (image == (Image *) NULL)
1432 {
1433 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1434 "NoSuchImage","`%s'",expression);
1435 return(0.0);
1436 }
cristy4c08aed2011-07-01 19:47:50 +00001437 GetPixelInfo(image,&pixel);
1438 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001439 point.x,point.y,&pixel,exception);
cristy1707c6c2012-01-18 23:30:54 +00001440 if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1441 (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001442 (LocaleCompare(p,"saturation") != 0) &&
1443 (LocaleCompare(p,"lightness") != 0))
1444 {
1445 char
1446 name[MaxTextExtent];
1447
1448 (void) CopyMagickString(name,p,MaxTextExtent);
1449 for (q=name+(strlen(name)-1); q > name; q--)
1450 {
1451 if (*q == ')')
1452 break;
1453 if (*q == '.')
1454 {
1455 *q='\0';
1456 break;
1457 }
1458 }
1459 if ((strlen(name) > 2) &&
1460 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1461 {
cristy4c08aed2011-07-01 19:47:50 +00001462 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001463 *color;
1464
cristy4c08aed2011-07-01 19:47:50 +00001465 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1466 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001467 {
1468 pixel=(*color);
1469 p+=strlen(name);
1470 }
1471 else
cristy1707c6c2012-01-18 23:30:54 +00001472 {
1473 MagickBooleanType
1474 status;
1475
1476 status=QueryColorCompliance(name,AllCompliance,&pixel,
1477 fx_info->exception);
1478 if (status != MagickFalse)
1479 {
1480 (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1481 name),ClonePixelInfo(&pixel));
1482 p+=strlen(name);
1483 }
1484 }
cristy3ed852e2009-09-05 21:47:34 +00001485 }
1486 }
1487 (void) CopyMagickString(symbol,p,MaxTextExtent);
1488 StripString(symbol);
1489 if (*symbol == '\0')
1490 {
1491 switch (channel)
1492 {
cristy0568ffc2011-07-25 16:54:14 +00001493 case RedPixelChannel: return(QuantumScale*pixel.red);
1494 case GreenPixelChannel: return(QuantumScale*pixel.green);
1495 case BluePixelChannel: return(QuantumScale*pixel.blue);
1496 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001497 {
1498 if (image->colorspace != CMYKColorspace)
1499 {
1500 (void) ThrowMagickException(exception,GetMagickModule(),
cristy1a020e42011-12-06 18:13:23 +00001501 ImageError,"ColorSeparatedImageRequired","`%s'",
cristy3ed852e2009-09-05 21:47:34 +00001502 image->filename);
1503 return(0.0);
1504 }
cristy4c08aed2011-07-01 19:47:50 +00001505 return(QuantumScale*pixel.black);
1506 }
cristy0568ffc2011-07-25 16:54:14 +00001507 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001508 {
1509 MagickRealType
1510 alpha;
1511
1512 if (pixel.matte == MagickFalse)
1513 return(1.0);
1514 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1515 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001516 }
cristya382aca2011-12-06 18:22:48 +00001517 case IndexPixelChannel:
1518 return(0.0);
cristyb3a73b52011-07-26 01:34:43 +00001519 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001520 {
cristy4c08aed2011-07-01 19:47:50 +00001521 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001522 }
cristy3ed852e2009-09-05 21:47:34 +00001523 default:
1524 break;
1525 }
1526 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1527 "UnableToParseExpression","`%s'",p);
1528 return(0.0);
1529 }
1530 switch (*symbol)
1531 {
1532 case 'A':
1533 case 'a':
1534 {
1535 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001536 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001537 break;
1538 }
1539 case 'B':
1540 case 'b':
1541 {
1542 if (LocaleCompare(symbol,"b") == 0)
1543 return(QuantumScale*pixel.blue);
1544 break;
1545 }
1546 case 'C':
1547 case 'c':
1548 {
1549 if (LocaleNCompare(symbol,"channel",7) == 0)
1550 {
1551 GeometryInfo
1552 channel_info;
1553
1554 MagickStatusType
1555 flags;
1556
1557 flags=ParseGeometry(symbol+7,&channel_info);
1558 if (image->colorspace == CMYKColorspace)
1559 switch (channel)
1560 {
cristy0568ffc2011-07-25 16:54:14 +00001561 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001562 {
1563 if ((flags & RhoValue) == 0)
1564 return(0.0);
1565 return(channel_info.rho);
1566 }
cristy0568ffc2011-07-25 16:54:14 +00001567 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001568 {
1569 if ((flags & SigmaValue) == 0)
1570 return(0.0);
1571 return(channel_info.sigma);
1572 }
cristy0568ffc2011-07-25 16:54:14 +00001573 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001574 {
1575 if ((flags & XiValue) == 0)
1576 return(0.0);
1577 return(channel_info.xi);
1578 }
cristy0568ffc2011-07-25 16:54:14 +00001579 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001580 {
1581 if ((flags & PsiValue) == 0)
1582 return(0.0);
1583 return(channel_info.psi);
1584 }
cristy0568ffc2011-07-25 16:54:14 +00001585 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001586 {
1587 if ((flags & ChiValue) == 0)
1588 return(0.0);
1589 return(channel_info.chi);
1590 }
1591 default:
1592 return(0.0);
1593 }
1594 switch (channel)
1595 {
cristy0568ffc2011-07-25 16:54:14 +00001596 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001597 {
1598 if ((flags & RhoValue) == 0)
1599 return(0.0);
1600 return(channel_info.rho);
1601 }
cristy0568ffc2011-07-25 16:54:14 +00001602 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001603 {
1604 if ((flags & SigmaValue) == 0)
1605 return(0.0);
1606 return(channel_info.sigma);
1607 }
cristy0568ffc2011-07-25 16:54:14 +00001608 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001609 {
1610 if ((flags & XiValue) == 0)
1611 return(0.0);
1612 return(channel_info.xi);
1613 }
cristy0568ffc2011-07-25 16:54:14 +00001614 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001615 {
1616 if ((flags & ChiValue) == 0)
1617 return(0.0);
1618 return(channel_info.chi);
1619 }
cristy0568ffc2011-07-25 16:54:14 +00001620 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001621 {
1622 if ((flags & PsiValue) == 0)
1623 return(0.0);
1624 return(channel_info.psi);
1625 }
cristy3ed852e2009-09-05 21:47:34 +00001626 default:
1627 return(0.0);
1628 }
1629 return(0.0);
1630 }
1631 if (LocaleCompare(symbol,"c") == 0)
1632 return(QuantumScale*pixel.red);
1633 break;
1634 }
1635 case 'D':
1636 case 'd':
1637 {
1638 if (LocaleNCompare(symbol,"depth",5) == 0)
1639 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1640 break;
1641 }
1642 case 'G':
1643 case 'g':
1644 {
1645 if (LocaleCompare(symbol,"g") == 0)
1646 return(QuantumScale*pixel.green);
1647 break;
1648 }
1649 case 'K':
1650 case 'k':
1651 {
1652 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1653 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1654 if (LocaleCompare(symbol,"k") == 0)
1655 {
1656 if (image->colorspace != CMYKColorspace)
1657 {
1658 (void) ThrowMagickException(exception,GetMagickModule(),
1659 OptionError,"ColorSeparatedImageRequired","`%s'",
1660 image->filename);
1661 return(0.0);
1662 }
cristy4c08aed2011-07-01 19:47:50 +00001663 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001664 }
1665 break;
1666 }
1667 case 'H':
1668 case 'h':
1669 {
1670 if (LocaleCompare(symbol,"h") == 0)
1671 return((MagickRealType) image->rows);
1672 if (LocaleCompare(symbol,"hue") == 0)
1673 {
1674 double
1675 hue,
1676 lightness,
1677 saturation;
1678
cristyda1f9c12011-10-02 21:39:49 +00001679 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1680 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001681 return(hue);
1682 }
1683 break;
1684 }
1685 case 'I':
1686 case 'i':
1687 {
1688 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1689 (LocaleCompare(symbol,"image.minima") == 0) ||
1690 (LocaleCompare(symbol,"image.maxima") == 0) ||
1691 (LocaleCompare(symbol,"image.mean") == 0) ||
1692 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1693 (LocaleCompare(symbol,"image.skewness") == 0) ||
1694 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1695 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1696 if (LocaleCompare(symbol,"image.resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001697 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001698 if (LocaleCompare(symbol,"image.resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001699 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001700 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001701 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001702 if (LocaleCompare(symbol,"i") == 0)
1703 return((MagickRealType) x);
1704 break;
1705 }
1706 case 'J':
1707 case 'j':
1708 {
1709 if (LocaleCompare(symbol,"j") == 0)
1710 return((MagickRealType) y);
1711 break;
1712 }
1713 case 'L':
1714 case 'l':
1715 {
1716 if (LocaleCompare(symbol,"lightness") == 0)
1717 {
1718 double
1719 hue,
1720 lightness,
1721 saturation;
1722
cristyda1f9c12011-10-02 21:39:49 +00001723 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1724 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001725 return(lightness);
1726 }
1727 if (LocaleCompare(symbol,"luminance") == 0)
1728 {
1729 double
1730 luminence;
1731
1732 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1733 return(QuantumScale*luminence);
1734 }
1735 break;
1736 }
1737 case 'M':
1738 case 'm':
1739 {
1740 if (LocaleNCompare(symbol,"maxima",6) == 0)
1741 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1742 if (LocaleNCompare(symbol,"mean",4) == 0)
1743 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1744 if (LocaleNCompare(symbol,"minima",6) == 0)
1745 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1746 if (LocaleCompare(symbol,"m") == 0)
1747 return(QuantumScale*pixel.blue);
1748 break;
1749 }
1750 case 'N':
1751 case 'n':
1752 {
1753 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001754 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001755 break;
1756 }
1757 case 'O':
1758 case 'o':
1759 {
1760 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001761 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001762 break;
1763 }
1764 case 'P':
1765 case 'p':
1766 {
1767 if (LocaleCompare(symbol,"page.height") == 0)
1768 return((MagickRealType) image->page.height);
1769 if (LocaleCompare(symbol,"page.width") == 0)
1770 return((MagickRealType) image->page.width);
1771 if (LocaleCompare(symbol,"page.x") == 0)
1772 return((MagickRealType) image->page.x);
1773 if (LocaleCompare(symbol,"page.y") == 0)
1774 return((MagickRealType) image->page.y);
1775 break;
1776 }
1777 case 'R':
1778 case 'r':
1779 {
1780 if (LocaleCompare(symbol,"resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001781 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001782 if (LocaleCompare(symbol,"resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001783 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001784 if (LocaleCompare(symbol,"r") == 0)
1785 return(QuantumScale*pixel.red);
1786 break;
1787 }
1788 case 'S':
1789 case 's':
1790 {
1791 if (LocaleCompare(symbol,"saturation") == 0)
1792 {
1793 double
1794 hue,
1795 lightness,
1796 saturation;
1797
cristyda1f9c12011-10-02 21:39:49 +00001798 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1799 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001800 return(saturation);
1801 }
1802 if (LocaleNCompare(symbol,"skewness",8) == 0)
1803 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1804 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1805 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1806 break;
1807 }
1808 case 'T':
1809 case 't':
1810 {
1811 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001812 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001813 break;
1814 }
1815 case 'W':
1816 case 'w':
1817 {
1818 if (LocaleCompare(symbol,"w") == 0)
1819 return((MagickRealType) image->columns);
1820 break;
1821 }
1822 case 'Y':
1823 case 'y':
1824 {
1825 if (LocaleCompare(symbol,"y") == 0)
1826 return(QuantumScale*pixel.green);
1827 break;
1828 }
1829 case 'Z':
1830 case 'z':
1831 {
1832 if (LocaleCompare(symbol,"z") == 0)
1833 {
1834 MagickRealType
1835 depth;
1836
cristyfefab1b2011-07-05 00:33:22 +00001837 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001838 return(depth);
1839 }
1840 break;
1841 }
1842 default:
1843 break;
1844 }
1845 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1846 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001847 return((MagickRealType) StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001848 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1849 "UnableToParseExpression","`%s'",symbol);
1850 return(0.0);
1851}
1852
1853static const char *FxOperatorPrecedence(const char *expression,
1854 ExceptionInfo *exception)
1855{
1856 typedef enum
1857 {
1858 UndefinedPrecedence,
1859 NullPrecedence,
1860 BitwiseComplementPrecedence,
1861 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001862 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001863 MultiplyPrecedence,
1864 AdditionPrecedence,
1865 ShiftPrecedence,
1866 RelationalPrecedence,
1867 EquivalencyPrecedence,
1868 BitwiseAndPrecedence,
1869 BitwiseOrPrecedence,
1870 LogicalAndPrecedence,
1871 LogicalOrPrecedence,
1872 TernaryPrecedence,
1873 AssignmentPrecedence,
1874 CommaPrecedence,
1875 SeparatorPrecedence
1876 } FxPrecedence;
1877
1878 FxPrecedence
1879 precedence,
1880 target;
1881
1882 register const char
1883 *subexpression;
1884
1885 register int
1886 c;
1887
cristybb503372010-05-27 20:51:26 +00001888 size_t
cristy3ed852e2009-09-05 21:47:34 +00001889 level;
1890
1891 c=0;
1892 level=0;
1893 subexpression=(const char *) NULL;
1894 target=NullPrecedence;
1895 while (*expression != '\0')
1896 {
1897 precedence=UndefinedPrecedence;
1898 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1899 {
1900 expression++;
1901 continue;
1902 }
cristy488fa882010-03-01 22:34:24 +00001903 switch (*expression)
1904 {
1905 case 'A':
1906 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001907 {
cristyb33454f2011-08-03 02:10:45 +00001908#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001909 if (LocaleNCompare(expression,"acosh",5) == 0)
1910 {
1911 expression+=5;
1912 break;
1913 }
cristyb33454f2011-08-03 02:10:45 +00001914#endif
1915#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001916 if (LocaleNCompare(expression,"asinh",5) == 0)
1917 {
1918 expression+=5;
1919 break;
1920 }
cristyb33454f2011-08-03 02:10:45 +00001921#endif
1922#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001923 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001924 {
1925 expression+=5;
1926 break;
1927 }
cristyb33454f2011-08-03 02:10:45 +00001928#endif
cristy488fa882010-03-01 22:34:24 +00001929 break;
cristy3ed852e2009-09-05 21:47:34 +00001930 }
cristy62d455f2011-11-03 11:42:28 +00001931 case 'E':
1932 case 'e':
1933 {
1934 if ((LocaleNCompare(expression,"E+",2) == 0) ||
1935 (LocaleNCompare(expression,"E-",2) == 0))
1936 {
1937 expression+=2; /* scientific notation */
1938 break;
1939 }
1940 }
cristy488fa882010-03-01 22:34:24 +00001941 case 'J':
1942 case 'j':
1943 {
1944 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1945 (LocaleNCompare(expression,"j1",2) == 0))
1946 {
1947 expression+=2;
1948 break;
1949 }
1950 break;
1951 }
cristy2def9322010-06-18 23:59:37 +00001952 case '#':
1953 {
1954 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1955 expression++;
1956 break;
1957 }
cristy488fa882010-03-01 22:34:24 +00001958 default:
1959 break;
1960 }
cristy3ed852e2009-09-05 21:47:34 +00001961 if ((c == (int) '{') || (c == (int) '['))
1962 level++;
1963 else
1964 if ((c == (int) '}') || (c == (int) ']'))
1965 level--;
1966 if (level == 0)
1967 switch ((unsigned char) *expression)
1968 {
1969 case '~':
1970 case '!':
1971 {
1972 precedence=BitwiseComplementPrecedence;
1973 break;
1974 }
1975 case '^':
cristy6621e252010-08-13 00:42:57 +00001976 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001977 {
1978 precedence=ExponentPrecedence;
1979 break;
1980 }
1981 default:
1982 {
1983 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1984 (strchr(")",c) != (char *) NULL))) &&
1985 (((islower((int) ((char) *expression)) != 0) ||
1986 (strchr("(",(int) *expression) != (char *) NULL)) ||
1987 ((isdigit((int) ((char) c)) == 0) &&
1988 (isdigit((int) ((char) *expression)) != 0))) &&
1989 (strchr("xy",(int) *expression) == (char *) NULL))
1990 precedence=MultiplyPrecedence;
1991 break;
1992 }
1993 case '*':
1994 case '/':
1995 case '%':
1996 {
1997 precedence=MultiplyPrecedence;
1998 break;
1999 }
2000 case '+':
2001 case '-':
2002 {
2003 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
2004 (isalpha(c) != 0))
2005 precedence=AdditionPrecedence;
2006 break;
2007 }
2008 case LeftShiftOperator:
2009 case RightShiftOperator:
2010 {
2011 precedence=ShiftPrecedence;
2012 break;
2013 }
2014 case '<':
2015 case LessThanEqualOperator:
2016 case GreaterThanEqualOperator:
2017 case '>':
2018 {
2019 precedence=RelationalPrecedence;
2020 break;
2021 }
2022 case EqualOperator:
2023 case NotEqualOperator:
2024 {
2025 precedence=EquivalencyPrecedence;
2026 break;
2027 }
2028 case '&':
2029 {
2030 precedence=BitwiseAndPrecedence;
2031 break;
2032 }
2033 case '|':
2034 {
2035 precedence=BitwiseOrPrecedence;
2036 break;
2037 }
2038 case LogicalAndOperator:
2039 {
2040 precedence=LogicalAndPrecedence;
2041 break;
2042 }
2043 case LogicalOrOperator:
2044 {
2045 precedence=LogicalOrPrecedence;
2046 break;
2047 }
cristy116af162010-08-13 01:25:47 +00002048 case ExponentialNotation:
2049 {
2050 precedence=ExponentialNotationPrecedence;
2051 break;
2052 }
cristy3ed852e2009-09-05 21:47:34 +00002053 case ':':
2054 case '?':
2055 {
2056 precedence=TernaryPrecedence;
2057 break;
2058 }
2059 case '=':
2060 {
2061 precedence=AssignmentPrecedence;
2062 break;
2063 }
2064 case ',':
2065 {
2066 precedence=CommaPrecedence;
2067 break;
2068 }
2069 case ';':
2070 {
2071 precedence=SeparatorPrecedence;
2072 break;
2073 }
2074 }
2075 if ((precedence == BitwiseComplementPrecedence) ||
2076 (precedence == TernaryPrecedence) ||
2077 (precedence == AssignmentPrecedence))
2078 {
2079 if (precedence > target)
2080 {
2081 /*
2082 Right-to-left associativity.
2083 */
2084 target=precedence;
2085 subexpression=expression;
2086 }
2087 }
2088 else
2089 if (precedence >= target)
2090 {
2091 /*
2092 Left-to-right associativity.
2093 */
2094 target=precedence;
2095 subexpression=expression;
2096 }
2097 if (strchr("(",(int) *expression) != (char *) NULL)
2098 expression=FxSubexpression(expression,exception);
2099 c=(int) (*expression++);
2100 }
2101 return(subexpression);
2102}
2103
2104static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002105 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002106 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002107{
2108 char
2109 *q,
2110 subexpression[MaxTextExtent];
2111
2112 MagickRealType
2113 alpha,
2114 gamma;
2115
2116 register const char
2117 *p;
2118
2119 *beta=0.0;
2120 if (exception->severity != UndefinedException)
2121 return(0.0);
2122 while (isspace((int) *expression) != 0)
2123 expression++;
2124 if (*expression == '\0')
2125 {
2126 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2127 "MissingExpression","`%s'",expression);
2128 return(0.0);
2129 }
cristy66322f02010-05-17 11:40:48 +00002130 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002131 p=FxOperatorPrecedence(expression,exception);
2132 if (p != (const char *) NULL)
2133 {
2134 (void) CopyMagickString(subexpression,expression,(size_t)
2135 (p-expression+1));
2136 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2137 exception);
2138 switch ((unsigned char) *p)
2139 {
2140 case '~':
2141 {
2142 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002143 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002144 return(*beta);
2145 }
2146 case '!':
2147 {
2148 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2149 return(*beta == 0.0 ? 1.0 : 0.0);
2150 }
2151 case '^':
2152 {
2153 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2154 channel,x,y,++p,beta,exception));
2155 return(*beta);
2156 }
2157 case '*':
cristy116af162010-08-13 01:25:47 +00002158 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002159 {
2160 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2161 return(alpha*(*beta));
2162 }
2163 case '/':
2164 {
2165 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2166 if (*beta == 0.0)
2167 {
2168 if (exception->severity == UndefinedException)
2169 (void) ThrowMagickException(exception,GetMagickModule(),
2170 OptionError,"DivideByZero","`%s'",expression);
2171 return(0.0);
2172 }
2173 return(alpha/(*beta));
2174 }
2175 case '%':
2176 {
2177 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2178 *beta=fabs(floor(((double) *beta)+0.5));
2179 if (*beta == 0.0)
2180 {
2181 (void) ThrowMagickException(exception,GetMagickModule(),
2182 OptionError,"DivideByZero","`%s'",expression);
2183 return(0.0);
2184 }
2185 return(fmod((double) alpha,(double) *beta));
2186 }
2187 case '+':
2188 {
2189 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2190 return(alpha+(*beta));
2191 }
2192 case '-':
2193 {
2194 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2195 return(alpha-(*beta));
2196 }
2197 case LeftShiftOperator:
2198 {
2199 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002200 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002201 return(*beta);
2202 }
2203 case RightShiftOperator:
2204 {
2205 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002206 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002207 return(*beta);
2208 }
2209 case '<':
2210 {
2211 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2212 return(alpha < *beta ? 1.0 : 0.0);
2213 }
2214 case LessThanEqualOperator:
2215 {
2216 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2217 return(alpha <= *beta ? 1.0 : 0.0);
2218 }
2219 case '>':
2220 {
2221 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2222 return(alpha > *beta ? 1.0 : 0.0);
2223 }
2224 case GreaterThanEqualOperator:
2225 {
2226 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2227 return(alpha >= *beta ? 1.0 : 0.0);
2228 }
2229 case EqualOperator:
2230 {
2231 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2232 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2233 }
2234 case NotEqualOperator:
2235 {
2236 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2237 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2238 }
2239 case '&':
2240 {
2241 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002242 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002243 return(*beta);
2244 }
2245 case '|':
2246 {
2247 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002248 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002249 return(*beta);
2250 }
2251 case LogicalAndOperator:
2252 {
2253 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2254 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2255 return(*beta);
2256 }
2257 case LogicalOrOperator:
2258 {
2259 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2260 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2261 return(*beta);
2262 }
2263 case '?':
2264 {
2265 MagickRealType
2266 gamma;
2267
2268 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2269 q=subexpression;
2270 p=StringToken(":",&q);
2271 if (q == (char *) NULL)
2272 {
2273 (void) ThrowMagickException(exception,GetMagickModule(),
2274 OptionError,"UnableToParseExpression","`%s'",subexpression);
2275 return(0.0);
2276 }
2277 if (fabs((double) alpha) > MagickEpsilon)
2278 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2279 else
2280 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2281 return(gamma);
2282 }
2283 case '=':
2284 {
2285 char
2286 numeric[MaxTextExtent];
2287
2288 q=subexpression;
2289 while (isalpha((int) ((unsigned char) *q)) != 0)
2290 q++;
2291 if (*q != '\0')
2292 {
2293 (void) ThrowMagickException(exception,GetMagickModule(),
2294 OptionError,"UnableToParseExpression","`%s'",subexpression);
2295 return(0.0);
2296 }
2297 ClearMagickException(exception);
2298 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002299 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002300 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002301 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2302 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2303 subexpression),ConstantString(numeric));
2304 return(*beta);
2305 }
2306 case ',':
2307 {
2308 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2309 return(alpha);
2310 }
2311 case ';':
2312 {
2313 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2314 return(*beta);
2315 }
2316 default:
2317 {
2318 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2319 exception);
2320 return(gamma);
2321 }
2322 }
2323 }
2324 if (strchr("(",(int) *expression) != (char *) NULL)
2325 {
2326 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2327 subexpression[strlen(subexpression)-1]='\0';
2328 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2329 exception);
2330 return(gamma);
2331 }
cristy8b8a3ae2010-10-23 18:49:46 +00002332 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002333 {
2334 case '+':
2335 {
2336 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2337 exception);
2338 return(1.0*gamma);
2339 }
2340 case '-':
2341 {
2342 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2343 exception);
2344 return(-1.0*gamma);
2345 }
2346 case '~':
2347 {
2348 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2349 exception);
cristybb503372010-05-27 20:51:26 +00002350 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002351 }
2352 case 'A':
2353 case 'a':
2354 {
2355 if (LocaleNCompare(expression,"abs",3) == 0)
2356 {
2357 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2358 exception);
2359 return((MagickRealType) fabs((double) alpha));
2360 }
cristyb33454f2011-08-03 02:10:45 +00002361#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002362 if (LocaleNCompare(expression,"acosh",5) == 0)
2363 {
2364 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2365 exception);
2366 return((MagickRealType) acosh((double) alpha));
2367 }
cristyb33454f2011-08-03 02:10:45 +00002368#endif
cristy3ed852e2009-09-05 21:47:34 +00002369 if (LocaleNCompare(expression,"acos",4) == 0)
2370 {
2371 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2372 exception);
2373 return((MagickRealType) acos((double) alpha));
2374 }
cristy43c22f42010-03-30 12:34:07 +00002375#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002376 if (LocaleNCompare(expression,"airy",4) == 0)
2377 {
2378 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2379 exception);
2380 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002381 return(1.0);
2382 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002383 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002384 }
cristy43c22f42010-03-30 12:34:07 +00002385#endif
cristyb33454f2011-08-03 02:10:45 +00002386#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002387 if (LocaleNCompare(expression,"asinh",5) == 0)
2388 {
2389 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2390 exception);
2391 return((MagickRealType) asinh((double) alpha));
2392 }
cristyb33454f2011-08-03 02:10:45 +00002393#endif
cristy3ed852e2009-09-05 21:47:34 +00002394 if (LocaleNCompare(expression,"asin",4) == 0)
2395 {
2396 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2397 exception);
2398 return((MagickRealType) asin((double) alpha));
2399 }
2400 if (LocaleNCompare(expression,"alt",3) == 0)
2401 {
2402 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2403 exception);
cristybb503372010-05-27 20:51:26 +00002404 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002405 }
2406 if (LocaleNCompare(expression,"atan2",5) == 0)
2407 {
2408 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2409 exception);
2410 return((MagickRealType) atan2((double) alpha,(double) *beta));
2411 }
cristyb33454f2011-08-03 02:10:45 +00002412#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002413 if (LocaleNCompare(expression,"atanh",5) == 0)
2414 {
2415 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2416 exception);
2417 return((MagickRealType) atanh((double) alpha));
2418 }
cristyb33454f2011-08-03 02:10:45 +00002419#endif
cristy3ed852e2009-09-05 21:47:34 +00002420 if (LocaleNCompare(expression,"atan",4) == 0)
2421 {
2422 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2423 exception);
2424 return((MagickRealType) atan((double) alpha));
2425 }
2426 if (LocaleCompare(expression,"a") == 0)
2427 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2428 break;
2429 }
2430 case 'B':
2431 case 'b':
2432 {
2433 if (LocaleCompare(expression,"b") == 0)
2434 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2435 break;
2436 }
2437 case 'C':
2438 case 'c':
2439 {
2440 if (LocaleNCompare(expression,"ceil",4) == 0)
2441 {
2442 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2443 exception);
2444 return((MagickRealType) ceil((double) alpha));
2445 }
2446 if (LocaleNCompare(expression,"cosh",4) == 0)
2447 {
2448 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2449 exception);
2450 return((MagickRealType) cosh((double) alpha));
2451 }
2452 if (LocaleNCompare(expression,"cos",3) == 0)
2453 {
2454 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2455 exception);
2456 return((MagickRealType) cos((double) alpha));
2457 }
2458 if (LocaleCompare(expression,"c") == 0)
2459 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2460 break;
2461 }
2462 case 'D':
2463 case 'd':
2464 {
2465 if (LocaleNCompare(expression,"debug",5) == 0)
2466 {
2467 const char
2468 *type;
2469
2470 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2471 exception);
2472 if (fx_info->images->colorspace == CMYKColorspace)
2473 switch (channel)
2474 {
cristy0568ffc2011-07-25 16:54:14 +00002475 case CyanPixelChannel: type="cyan"; break;
2476 case MagentaPixelChannel: type="magenta"; break;
2477 case YellowPixelChannel: type="yellow"; break;
2478 case AlphaPixelChannel: type="opacity"; break;
2479 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002480 default: type="unknown"; break;
2481 }
2482 else
2483 switch (channel)
2484 {
cristy0568ffc2011-07-25 16:54:14 +00002485 case RedPixelChannel: type="red"; break;
2486 case GreenPixelChannel: type="green"; break;
2487 case BluePixelChannel: type="blue"; break;
2488 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002489 default: type="unknown"; break;
2490 }
2491 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2492 if (strlen(subexpression) > 1)
2493 subexpression[strlen(subexpression)-1]='\0';
2494 if (fx_info->file != (FILE *) NULL)
cristy1707c6c2012-01-18 23:30:54 +00002495 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2496 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2497 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002498 return(0.0);
2499 }
cristy5597a8d2011-11-04 00:25:32 +00002500 if (LocaleNCompare(expression,"drc",3) == 0)
2501 {
2502 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2503 exception);
2504 return((MagickRealType) (alpha/(*beta*(alpha-1.0)+1.0)));
2505 }
cristy3ed852e2009-09-05 21:47:34 +00002506 break;
2507 }
2508 case 'E':
2509 case 'e':
2510 {
2511 if (LocaleCompare(expression,"epsilon") == 0)
2512 return((MagickRealType) MagickEpsilon);
2513 if (LocaleNCompare(expression,"exp",3) == 0)
2514 {
2515 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2516 exception);
2517 return((MagickRealType) exp((double) alpha));
2518 }
2519 if (LocaleCompare(expression,"e") == 0)
2520 return((MagickRealType) 2.7182818284590452354);
2521 break;
2522 }
2523 case 'F':
2524 case 'f':
2525 {
2526 if (LocaleNCompare(expression,"floor",5) == 0)
2527 {
2528 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2529 exception);
2530 return((MagickRealType) floor((double) alpha));
2531 }
2532 break;
2533 }
2534 case 'G':
2535 case 'g':
2536 {
cristy9eeedea2011-11-02 19:04:05 +00002537 if (LocaleNCompare(expression,"gauss",5) == 0)
2538 {
2539 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2540 exception);
2541 gamma=exp((double) (-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2542 return((MagickRealType) gamma);
2543 }
cristyb0aad4c2011-11-02 19:30:35 +00002544 if (LocaleNCompare(expression,"gcd",3) == 0)
2545 {
2546 MagickOffsetType
2547 gcd;
2548
2549 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2550 exception);
cristy1707c6c2012-01-18 23:30:54 +00002551 gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2552 0.5));
cristyb0aad4c2011-11-02 19:30:35 +00002553 return((MagickRealType) gcd);
2554 }
cristy3ed852e2009-09-05 21:47:34 +00002555 if (LocaleCompare(expression,"g") == 0)
2556 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2557 break;
2558 }
2559 case 'H':
2560 case 'h':
2561 {
2562 if (LocaleCompare(expression,"h") == 0)
2563 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2564 if (LocaleCompare(expression,"hue") == 0)
2565 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2566 if (LocaleNCompare(expression,"hypot",5) == 0)
2567 {
2568 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2569 exception);
2570 return((MagickRealType) hypot((double) alpha,(double) *beta));
2571 }
2572 break;
2573 }
2574 case 'K':
2575 case 'k':
2576 {
2577 if (LocaleCompare(expression,"k") == 0)
2578 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2579 break;
2580 }
2581 case 'I':
2582 case 'i':
2583 {
2584 if (LocaleCompare(expression,"intensity") == 0)
2585 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2586 if (LocaleNCompare(expression,"int",3) == 0)
2587 {
2588 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2589 exception);
cristy16788e42010-08-13 13:44:26 +00002590 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002591 }
cristy82b20722011-11-05 21:52:36 +00002592#if defined(MAGICKCORE_HAVE_ISNAN)
cristy639399c2011-11-02 19:16:15 +00002593 if (LocaleNCompare(expression,"isnan",5) == 0)
2594 {
2595 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2596 exception);
cristy17a10202011-11-02 19:17:04 +00002597 return((MagickRealType) !!isnan((double) alpha));
cristy639399c2011-11-02 19:16:15 +00002598 }
cristy82b20722011-11-05 21:52:36 +00002599#endif
cristy3ed852e2009-09-05 21:47:34 +00002600 if (LocaleCompare(expression,"i") == 0)
2601 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2602 break;
2603 }
2604 case 'J':
2605 case 'j':
2606 {
2607 if (LocaleCompare(expression,"j") == 0)
2608 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002609#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002610 if (LocaleNCompare(expression,"j0",2) == 0)
2611 {
2612 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2613 exception);
2614 return((MagickRealType) j0((double) alpha));
2615 }
cristy161b9262010-03-20 19:34:32 +00002616#endif
2617#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002618 if (LocaleNCompare(expression,"j1",2) == 0)
2619 {
2620 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2621 exception);
2622 return((MagickRealType) j1((double) alpha));
2623 }
cristy161b9262010-03-20 19:34:32 +00002624#endif
cristyaa018fa2010-04-08 23:03:54 +00002625#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002626 if (LocaleNCompare(expression,"jinc",4) == 0)
2627 {
2628 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2629 exception);
cristy0946a822010-03-12 17:14:58 +00002630 if (alpha == 0.0)
2631 return(1.0);
cristy1707c6c2012-01-18 23:30:54 +00002632 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/(MagickPI*
2633 alpha));
cristyfce2f7b2010-03-12 00:29:49 +00002634 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002635 }
cristyaa018fa2010-04-08 23:03:54 +00002636#endif
cristy3ed852e2009-09-05 21:47:34 +00002637 break;
2638 }
2639 case 'L':
2640 case 'l':
2641 {
2642 if (LocaleNCompare(expression,"ln",2) == 0)
2643 {
2644 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2645 exception);
2646 return((MagickRealType) log((double) alpha));
2647 }
cristyc8ed5322010-08-31 12:07:59 +00002648 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002649 {
cristyc8ed5322010-08-31 12:07:59 +00002650 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002651 exception);
2652 return((MagickRealType) log10((double) alpha))/log10(2.0);
2653 }
2654 if (LocaleNCompare(expression,"log",3) == 0)
2655 {
2656 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2657 exception);
2658 return((MagickRealType) log10((double) alpha));
2659 }
2660 if (LocaleCompare(expression,"lightness") == 0)
2661 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2662 break;
2663 }
2664 case 'M':
2665 case 'm':
2666 {
2667 if (LocaleCompare(expression,"MaxRGB") == 0)
2668 return((MagickRealType) QuantumRange);
2669 if (LocaleNCompare(expression,"maxima",6) == 0)
2670 break;
2671 if (LocaleNCompare(expression,"max",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002672 {
2673 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2674 exception);
2675 return(alpha > *beta ? alpha : *beta);
2676 }
cristy3ed852e2009-09-05 21:47:34 +00002677 if (LocaleNCompare(expression,"minima",6) == 0)
2678 break;
2679 if (LocaleNCompare(expression,"min",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002680 {
2681 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2682 exception);
2683 return(alpha < *beta ? alpha : *beta);
2684 }
cristy3ed852e2009-09-05 21:47:34 +00002685 if (LocaleNCompare(expression,"mod",3) == 0)
2686 {
2687 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2688 exception);
cristy984049c2011-11-03 18:34:58 +00002689 gamma=alpha-floor((double) (alpha/(*beta)))*(*beta);
2690 return(gamma);
cristy3ed852e2009-09-05 21:47:34 +00002691 }
2692 if (LocaleCompare(expression,"m") == 0)
2693 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2694 break;
2695 }
2696 case 'N':
2697 case 'n':
2698 {
cristyad3502e2011-11-02 19:10:45 +00002699 if (LocaleNCompare(expression,"not",3) == 0)
2700 {
2701 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2702 exception);
2703 return((MagickRealType) (alpha < MagickEpsilon));
2704 }
cristy3ed852e2009-09-05 21:47:34 +00002705 if (LocaleCompare(expression,"n") == 0)
2706 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2707 break;
2708 }
2709 case 'O':
2710 case 'o':
2711 {
2712 if (LocaleCompare(expression,"Opaque") == 0)
2713 return(1.0);
2714 if (LocaleCompare(expression,"o") == 0)
2715 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2716 break;
2717 }
2718 case 'P':
2719 case 'p':
2720 {
cristy670aa3c2011-11-03 00:54:00 +00002721 if (LocaleCompare(expression,"phi") == 0)
2722 return((MagickRealType) MagickPHI);
cristy3ed852e2009-09-05 21:47:34 +00002723 if (LocaleCompare(expression,"pi") == 0)
2724 return((MagickRealType) MagickPI);
2725 if (LocaleNCompare(expression,"pow",3) == 0)
2726 {
2727 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2728 exception);
2729 return((MagickRealType) pow((double) alpha,(double) *beta));
2730 }
2731 if (LocaleCompare(expression,"p") == 0)
2732 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2733 break;
2734 }
2735 case 'Q':
2736 case 'q':
2737 {
2738 if (LocaleCompare(expression,"QuantumRange") == 0)
2739 return((MagickRealType) QuantumRange);
2740 if (LocaleCompare(expression,"QuantumScale") == 0)
2741 return((MagickRealType) QuantumScale);
2742 break;
2743 }
2744 case 'R':
2745 case 'r':
2746 {
2747 if (LocaleNCompare(expression,"rand",4) == 0)
2748 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2749 if (LocaleNCompare(expression,"round",5) == 0)
2750 {
2751 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2752 exception);
cristy16788e42010-08-13 13:44:26 +00002753 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002754 }
2755 if (LocaleCompare(expression,"r") == 0)
2756 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2757 break;
2758 }
2759 case 'S':
2760 case 's':
2761 {
2762 if (LocaleCompare(expression,"saturation") == 0)
2763 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2764 if (LocaleNCompare(expression,"sign",4) == 0)
2765 {
2766 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2767 exception);
2768 return(alpha < 0.0 ? -1.0 : 1.0);
2769 }
cristya6a09e72010-03-02 14:51:02 +00002770 if (LocaleNCompare(expression,"sinc",4) == 0)
2771 {
2772 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2773 exception);
2774 if (alpha == 0)
2775 return(1.0);
2776 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2777 (MagickPI*alpha));
2778 return(gamma);
2779 }
cristy3ed852e2009-09-05 21:47:34 +00002780 if (LocaleNCompare(expression,"sinh",4) == 0)
2781 {
2782 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2783 exception);
2784 return((MagickRealType) sinh((double) alpha));
2785 }
2786 if (LocaleNCompare(expression,"sin",3) == 0)
2787 {
2788 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2789 exception);
2790 return((MagickRealType) sin((double) alpha));
2791 }
2792 if (LocaleNCompare(expression,"sqrt",4) == 0)
2793 {
2794 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2795 exception);
2796 return((MagickRealType) sqrt((double) alpha));
2797 }
cristy9eeedea2011-11-02 19:04:05 +00002798 if (LocaleNCompare(expression,"squish",6) == 0)
2799 {
2800 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2801 exception);
2802 return((MagickRealType) (1.0/(1.0+exp((double) (4.0*alpha)))));
2803 }
cristy3ed852e2009-09-05 21:47:34 +00002804 if (LocaleCompare(expression,"s") == 0)
2805 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2806 break;
2807 }
2808 case 'T':
2809 case 't':
2810 {
2811 if (LocaleNCompare(expression,"tanh",4) == 0)
2812 {
2813 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2814 exception);
2815 return((MagickRealType) tanh((double) alpha));
2816 }
2817 if (LocaleNCompare(expression,"tan",3) == 0)
2818 {
2819 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2820 exception);
2821 return((MagickRealType) tan((double) alpha));
2822 }
2823 if (LocaleCompare(expression,"Transparent") == 0)
2824 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002825 if (LocaleNCompare(expression,"trunc",5) == 0)
2826 {
2827 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2828 exception);
2829 if (alpha >= 0.0)
2830 return((MagickRealType) floor((double) alpha));
2831 return((MagickRealType) ceil((double) alpha));
2832 }
cristy3ed852e2009-09-05 21:47:34 +00002833 if (LocaleCompare(expression,"t") == 0)
2834 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2835 break;
2836 }
2837 case 'U':
2838 case 'u':
2839 {
2840 if (LocaleCompare(expression,"u") == 0)
2841 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2842 break;
2843 }
2844 case 'V':
2845 case 'v':
2846 {
2847 if (LocaleCompare(expression,"v") == 0)
2848 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2849 break;
2850 }
2851 case 'W':
2852 case 'w':
2853 {
cristy9eeedea2011-11-02 19:04:05 +00002854 if (LocaleNCompare(expression,"while",5) == 0)
2855 {
2856 do
2857 {
2858 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2859 exception);
2860 } while (fabs((double) alpha) >= MagickEpsilon);
2861 return((MagickRealType) *beta);
2862 }
cristy3ed852e2009-09-05 21:47:34 +00002863 if (LocaleCompare(expression,"w") == 0)
2864 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2865 break;
2866 }
2867 case 'Y':
2868 case 'y':
2869 {
2870 if (LocaleCompare(expression,"y") == 0)
2871 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2872 break;
2873 }
2874 case 'Z':
2875 case 'z':
2876 {
2877 if (LocaleCompare(expression,"z") == 0)
2878 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2879 break;
2880 }
2881 default:
2882 break;
2883 }
2884 q=(char *) expression;
cristydbdd0e32011-11-04 23:29:40 +00002885 alpha=InterpretSiPrefixValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002886 if (q == expression)
2887 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2888 return(alpha);
2889}
2890
cristy7832dc22011-09-05 01:21:53 +00002891MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
cristy3ed852e2009-09-05 21:47:34 +00002892 MagickRealType *alpha,ExceptionInfo *exception)
2893{
2894 MagickBooleanType
2895 status;
2896
cristy541ae572011-08-05 19:08:59 +00002897 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2898 exception);
cristy3ed852e2009-09-05 21:47:34 +00002899 return(status);
2900}
2901
2902MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2903 MagickRealType *alpha,ExceptionInfo *exception)
2904{
2905 FILE
2906 *file;
2907
2908 MagickBooleanType
2909 status;
2910
2911 file=fx_info->file;
2912 fx_info->file=(FILE *) NULL;
cristy541ae572011-08-05 19:08:59 +00002913 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2914 exception);
cristy3ed852e2009-09-05 21:47:34 +00002915 fx_info->file=file;
2916 return(status);
2917}
2918
cristy7832dc22011-09-05 01:21:53 +00002919MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002920 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002921 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002922{
2923 MagickRealType
2924 beta;
2925
2926 beta=0.0;
2927 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2928 exception);
2929 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2930}
2931
2932/*
2933%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2934% %
2935% %
2936% %
2937% F x I m a g e %
2938% %
2939% %
2940% %
2941%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2942%
2943% FxImage() applies a mathematical expression to the specified image.
2944%
2945% The format of the FxImage method is:
2946%
2947% Image *FxImage(const Image *image,const char *expression,
2948% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002949%
2950% A description of each parameter follows:
2951%
2952% o image: the image.
2953%
cristy3ed852e2009-09-05 21:47:34 +00002954% o expression: A mathematical expression.
2955%
2956% o exception: return any errors or warnings in this structure.
2957%
2958*/
2959
2960static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2961{
cristybb503372010-05-27 20:51:26 +00002962 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002963 i;
2964
2965 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002966 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002967 if (fx_info[i] != (FxInfo *) NULL)
2968 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002969 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002970 return(fx_info);
2971}
2972
2973static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2974 ExceptionInfo *exception)
2975{
2976 char
2977 *fx_expression;
2978
2979 FxInfo
2980 **fx_info;
2981
2982 MagickRealType
2983 alpha;
2984
cristybb503372010-05-27 20:51:26 +00002985 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002986 i;
2987
cristybb503372010-05-27 20:51:26 +00002988 size_t
cristy3ed852e2009-09-05 21:47:34 +00002989 number_threads;
2990
2991 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002992 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002993 if (fx_info == (FxInfo **) NULL)
2994 return((FxInfo **) NULL);
2995 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2996 if (*expression != '@')
2997 fx_expression=ConstantString(expression);
2998 else
2999 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00003000 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00003001 {
3002 fx_info[i]=AcquireFxInfo(image,fx_expression);
3003 if (fx_info[i] == (FxInfo *) NULL)
3004 return(DestroyFxThreadSet(fx_info));
3005 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
3006 }
3007 fx_expression=DestroyString(fx_expression);
3008 return(fx_info);
3009}
3010
3011MagickExport Image *FxImage(const Image *image,const char *expression,
3012 ExceptionInfo *exception)
3013{
cristy3ed852e2009-09-05 21:47:34 +00003014#define FxImageTag "Fx/Image"
3015
cristyfa112112010-01-04 17:48:07 +00003016 CacheView
cristy79cedc72011-07-25 00:41:15 +00003017 *fx_view,
3018 *image_view;
cristyfa112112010-01-04 17:48:07 +00003019
cristy3ed852e2009-09-05 21:47:34 +00003020 FxInfo
cristyfa112112010-01-04 17:48:07 +00003021 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00003022
3023 Image
3024 *fx_image;
3025
cristy3ed852e2009-09-05 21:47:34 +00003026 MagickBooleanType
3027 status;
3028
cristybb503372010-05-27 20:51:26 +00003029 MagickOffsetType
3030 progress;
3031
cristy3ed852e2009-09-05 21:47:34 +00003032 MagickRealType
3033 alpha;
3034
cristybb503372010-05-27 20:51:26 +00003035 ssize_t
3036 y;
3037
cristy3ed852e2009-09-05 21:47:34 +00003038 assert(image != (Image *) NULL);
3039 assert(image->signature == MagickSignature);
3040 if (image->debug != MagickFalse)
3041 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00003042 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003043 if (fx_image == (Image *) NULL)
3044 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003045 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003046 {
cristy3ed852e2009-09-05 21:47:34 +00003047 fx_image=DestroyImage(fx_image);
3048 return((Image *) NULL);
3049 }
3050 fx_info=AcquireFxThreadSet(image,expression,exception);
3051 if (fx_info == (FxInfo **) NULL)
3052 {
3053 fx_image=DestroyImage(fx_image);
3054 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3055 }
3056 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3057 if (status == MagickFalse)
3058 {
3059 fx_image=DestroyImage(fx_image);
3060 fx_info=DestroyFxThreadSet(fx_info);
3061 return((Image *) NULL);
3062 }
3063 /*
3064 Fx image.
3065 */
3066 status=MagickTrue;
3067 progress=0;
cristy79cedc72011-07-25 00:41:15 +00003068 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003069 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003070#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003071 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003072#endif
cristybb503372010-05-27 20:51:26 +00003073 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003074 {
cristy5c9e6f22010-09-17 17:31:01 +00003075 const int
3076 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003077
cristy79cedc72011-07-25 00:41:15 +00003078 register const Quantum
3079 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003080
cristy4c08aed2011-07-01 19:47:50 +00003081 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003082 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003083
cristy79cedc72011-07-25 00:41:15 +00003084 register ssize_t
3085 x;
3086
cristy3ed852e2009-09-05 21:47:34 +00003087 if (status == MagickFalse)
3088 continue;
cristy79cedc72011-07-25 00:41:15 +00003089 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003090 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003091 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003092 {
3093 status=MagickFalse;
3094 continue;
3095 }
cristybb503372010-05-27 20:51:26 +00003096 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003097 {
cristy79cedc72011-07-25 00:41:15 +00003098 register ssize_t
3099 i;
3100
3101 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3102 {
3103 MagickRealType
3104 alpha;
3105
3106 PixelChannel
3107 channel;
3108
3109 PixelTrait
3110 fx_traits,
3111 traits;
3112
cristye2a912b2011-12-05 20:02:07 +00003113 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003114 traits=GetPixelChannelMapTraits(image,channel);
cristy79cedc72011-07-25 00:41:15 +00003115 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
cristy010d7d12011-08-31 01:02:48 +00003116 if ((traits == UndefinedPixelTrait) ||
3117 (fx_traits == UndefinedPixelTrait))
cristy79cedc72011-07-25 00:41:15 +00003118 continue;
3119 if ((fx_traits & CopyPixelTrait) != 0)
3120 {
cristy0beccfa2011-09-25 20:47:53 +00003121 SetPixelChannel(fx_image,channel,p[i],q);
cristy79cedc72011-07-25 00:41:15 +00003122 continue;
3123 }
3124 alpha=0.0;
cristya382aca2011-12-06 18:22:48 +00003125 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3126 exception);
cristyb3a73b52011-07-26 01:34:43 +00003127 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003128 }
3129 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003130 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003131 }
3132 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3133 status=MagickFalse;
3134 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3135 {
3136 MagickBooleanType
3137 proceed;
3138
cristyb5d5f722009-11-04 03:03:49 +00003139#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy490408a2011-07-07 14:42:05 +00003140 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003141#endif
3142 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3143 if (proceed == MagickFalse)
3144 status=MagickFalse;
3145 }
3146 }
cristy3ed852e2009-09-05 21:47:34 +00003147 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003148 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003149 fx_info=DestroyFxThreadSet(fx_info);
3150 if (status == MagickFalse)
3151 fx_image=DestroyImage(fx_image);
3152 return(fx_image);
3153}
3154
3155/*
3156%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3157% %
3158% %
3159% %
3160% I m p l o d e I m a g e %
3161% %
3162% %
3163% %
3164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3165%
3166% ImplodeImage() creates a new image that is a copy of an existing
3167% one with the image pixels "implode" by the specified percentage. It
3168% allocates the memory necessary for the new Image structure and returns a
3169% pointer to the new image.
3170%
3171% The format of the ImplodeImage method is:
3172%
3173% Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003174% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003175%
3176% A description of each parameter follows:
3177%
3178% o implode_image: Method ImplodeImage returns a pointer to the image
3179% after it is implode. A null image is returned if there is a memory
3180% shortage.
3181%
3182% o image: the image.
3183%
3184% o amount: Define the extent of the implosion.
3185%
cristy76f512e2011-09-12 01:26:56 +00003186% o method: the pixel interpolation method.
3187%
cristy3ed852e2009-09-05 21:47:34 +00003188% o exception: return any errors or warnings in this structure.
3189%
3190*/
3191MagickExport 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#define ImplodeImageTag "Implode/Image"
3195
cristyfa112112010-01-04 17:48:07 +00003196 CacheView
3197 *image_view,
3198 *implode_view;
3199
cristy3ed852e2009-09-05 21:47:34 +00003200 Image
3201 *implode_image;
3202
cristy3ed852e2009-09-05 21:47:34 +00003203 MagickBooleanType
3204 status;
3205
cristybb503372010-05-27 20:51:26 +00003206 MagickOffsetType
3207 progress;
3208
cristy3ed852e2009-09-05 21:47:34 +00003209 MagickRealType
3210 radius;
3211
3212 PointInfo
3213 center,
3214 scale;
3215
cristybb503372010-05-27 20:51:26 +00003216 ssize_t
3217 y;
3218
cristy3ed852e2009-09-05 21:47:34 +00003219 /*
3220 Initialize implode image attributes.
3221 */
3222 assert(image != (Image *) NULL);
3223 assert(image->signature == MagickSignature);
3224 if (image->debug != MagickFalse)
3225 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3226 assert(exception != (ExceptionInfo *) NULL);
3227 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00003228 implode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3229 exception);
cristy3ed852e2009-09-05 21:47:34 +00003230 if (implode_image == (Image *) NULL)
3231 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003232 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003233 {
cristy3ed852e2009-09-05 21:47:34 +00003234 implode_image=DestroyImage(implode_image);
3235 return((Image *) NULL);
3236 }
cristy4c08aed2011-07-01 19:47:50 +00003237 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003238 implode_image->matte=MagickTrue;
3239 /*
3240 Compute scaling factor.
3241 */
3242 scale.x=1.0;
3243 scale.y=1.0;
3244 center.x=0.5*image->columns;
3245 center.y=0.5*image->rows;
3246 radius=center.x;
3247 if (image->columns > image->rows)
3248 scale.y=(double) image->columns/(double) image->rows;
3249 else
3250 if (image->columns < image->rows)
3251 {
3252 scale.x=(double) image->rows/(double) image->columns;
3253 radius=center.y;
3254 }
3255 /*
3256 Implode image.
3257 */
3258 status=MagickTrue;
3259 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003260 image_view=AcquireCacheView(image);
3261 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003262#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003263 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003264#endif
cristybb503372010-05-27 20:51:26 +00003265 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003266 {
cristy3ed852e2009-09-05 21:47:34 +00003267 MagickRealType
3268 distance;
3269
3270 PointInfo
3271 delta;
3272
cristy6d188022011-09-12 13:23:33 +00003273 register const Quantum
3274 *restrict p;
3275
cristybb503372010-05-27 20:51:26 +00003276 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003277 x;
3278
cristy4c08aed2011-07-01 19:47:50 +00003279 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003280 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003281
3282 if (status == MagickFalse)
3283 continue;
cristy6d188022011-09-12 13:23:33 +00003284 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003285 q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003286 exception);
cristy6d188022011-09-12 13:23:33 +00003287 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003288 {
3289 status=MagickFalse;
3290 continue;
3291 }
cristy3ed852e2009-09-05 21:47:34 +00003292 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00003293 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003294 {
cristy6d188022011-09-12 13:23:33 +00003295 register ssize_t
3296 i;
3297
cristy3ed852e2009-09-05 21:47:34 +00003298 /*
3299 Determine if the pixel is within an ellipse.
3300 */
3301 delta.x=scale.x*(double) (x-center.x);
3302 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00003303 if (distance >= (radius*radius))
cristya6d7a9b2012-01-18 20:04:48 +00003304 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy6d188022011-09-12 13:23:33 +00003305 {
cristya6d7a9b2012-01-18 20:04:48 +00003306 PixelChannel
3307 channel;
3308
cristy1707c6c2012-01-18 23:30:54 +00003309 PixelTrait
3310 implode_traits,
3311 traits;
3312
cristya6d7a9b2012-01-18 20:04:48 +00003313 channel=GetPixelChannelMapChannel(image,i);
cristy1707c6c2012-01-18 23:30:54 +00003314 traits=GetPixelChannelMapTraits(image,channel);
3315 implode_traits=GetPixelChannelMapTraits(implode_image,channel);
3316 if ((traits == UndefinedPixelTrait) ||
3317 (implode_traits == UndefinedPixelTrait))
3318 continue;
cristya6d7a9b2012-01-18 20:04:48 +00003319 SetPixelChannel(implode_image,channel,p[i],q);
cristy6d188022011-09-12 13:23:33 +00003320 }
3321 else
cristy3ed852e2009-09-05 21:47:34 +00003322 {
3323 double
3324 factor;
3325
3326 /*
3327 Implode the pixel.
3328 */
3329 factor=1.0;
3330 if (distance > 0.0)
cristy1707c6c2012-01-18 23:30:54 +00003331 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/radius/
3332 2)),-amount);
cristy76f512e2011-09-12 01:26:56 +00003333 status=InterpolatePixelChannels(image,image_view,implode_image,method,
3334 (double) (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3335 scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00003336 }
cristy6d188022011-09-12 13:23:33 +00003337 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003338 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003339 }
3340 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3341 status=MagickFalse;
3342 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3343 {
3344 MagickBooleanType
3345 proceed;
3346
cristyb5d5f722009-11-04 03:03:49 +00003347#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003348 #pragma omp critical (MagickCore_ImplodeImage)
3349#endif
3350 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3351 if (proceed == MagickFalse)
3352 status=MagickFalse;
3353 }
3354 }
3355 implode_view=DestroyCacheView(implode_view);
3356 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003357 if (status == MagickFalse)
3358 implode_image=DestroyImage(implode_image);
3359 return(implode_image);
3360}
3361
3362/*
3363%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3364% %
3365% %
3366% %
3367% M o r p h I m a g e s %
3368% %
3369% %
3370% %
3371%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3372%
3373% The MorphImages() method requires a minimum of two images. The first
3374% image is transformed into the second by a number of intervening images
3375% as specified by frames.
3376%
3377% The format of the MorphImage method is:
3378%
cristybb503372010-05-27 20:51:26 +00003379% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003380% ExceptionInfo *exception)
3381%
3382% A description of each parameter follows:
3383%
3384% o image: the image.
3385%
3386% o number_frames: Define the number of in-between image to generate.
3387% The more in-between frames, the smoother the morph.
3388%
3389% o exception: return any errors or warnings in this structure.
3390%
3391*/
3392MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003393 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003394{
3395#define MorphImageTag "Morph/Image"
3396
3397 Image
3398 *morph_image,
3399 *morph_images;
3400
cristy9d314ff2011-03-09 01:30:28 +00003401 MagickBooleanType
3402 status;
cristy3ed852e2009-09-05 21:47:34 +00003403
3404 MagickOffsetType
3405 scene;
3406
3407 MagickRealType
3408 alpha,
3409 beta;
3410
3411 register const Image
3412 *next;
3413
cristybb503372010-05-27 20:51:26 +00003414 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003415 i;
3416
cristy9d314ff2011-03-09 01:30:28 +00003417 ssize_t
3418 y;
cristy3ed852e2009-09-05 21:47:34 +00003419
3420 /*
3421 Clone first frame in sequence.
3422 */
3423 assert(image != (Image *) NULL);
3424 assert(image->signature == MagickSignature);
3425 if (image->debug != MagickFalse)
3426 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3427 assert(exception != (ExceptionInfo *) NULL);
3428 assert(exception->signature == MagickSignature);
3429 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3430 if (morph_images == (Image *) NULL)
3431 return((Image *) NULL);
3432 if (GetNextImageInList(image) == (Image *) NULL)
3433 {
3434 /*
3435 Morph single image.
3436 */
cristybb503372010-05-27 20:51:26 +00003437 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003438 {
3439 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3440 if (morph_image == (Image *) NULL)
3441 {
3442 morph_images=DestroyImageList(morph_images);
3443 return((Image *) NULL);
3444 }
3445 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003446 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003447 {
cristy8b27a6d2010-02-14 03:31:15 +00003448 MagickBooleanType
3449 proceed;
3450
cristybb503372010-05-27 20:51:26 +00003451 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3452 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003453 if (proceed == MagickFalse)
3454 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003455 }
3456 }
3457 return(GetFirstImageInList(morph_images));
3458 }
3459 /*
3460 Morph image sequence.
3461 */
3462 status=MagickTrue;
3463 scene=0;
3464 next=image;
3465 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3466 {
cristybb503372010-05-27 20:51:26 +00003467 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003468 {
3469 CacheView
3470 *image_view,
3471 *morph_view;
3472
3473 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3474 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003475 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristybb503372010-05-27 20:51:26 +00003476 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
cristy15b98cd2010-09-12 19:42:50 +00003477 next->rows+beta*GetNextImageInList(next)->rows+0.5),
3478 next->filter,next->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003479 if (morph_image == (Image *) NULL)
3480 {
3481 morph_images=DestroyImageList(morph_images);
3482 return((Image *) NULL);
3483 }
cristy1707c6c2012-01-18 23:30:54 +00003484 status=SetImageStorageClass(morph_image,DirectClass,exception);
3485 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003486 {
cristy3ed852e2009-09-05 21:47:34 +00003487 morph_image=DestroyImage(morph_image);
3488 return((Image *) NULL);
3489 }
3490 AppendImageToList(&morph_images,morph_image);
3491 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003492 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3493 morph_images->rows,GetNextImageInList(next)->filter,
3494 GetNextImageInList(next)->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003495 if (morph_image == (Image *) NULL)
3496 {
3497 morph_images=DestroyImageList(morph_images);
3498 return((Image *) NULL);
3499 }
3500 image_view=AcquireCacheView(morph_image);
3501 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003502#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003503 #pragma omp parallel for schedule(static,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003504#endif
cristybb503372010-05-27 20:51:26 +00003505 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003506 {
3507 MagickBooleanType
3508 sync;
3509
cristy4c08aed2011-07-01 19:47:50 +00003510 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003511 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003512
cristybb503372010-05-27 20:51:26 +00003513 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003514 x;
3515
cristy4c08aed2011-07-01 19:47:50 +00003516 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003517 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003518
3519 if (status == MagickFalse)
3520 continue;
3521 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3522 exception);
3523 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3524 exception);
cristy4c08aed2011-07-01 19:47:50 +00003525 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003526 {
3527 status=MagickFalse;
3528 continue;
3529 }
cristybb503372010-05-27 20:51:26 +00003530 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003531 {
cristy1707c6c2012-01-18 23:30:54 +00003532 register ssize_t
3533 i;
3534
3535 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3536 {
3537 PixelChannel
3538 channel;
3539
3540 PixelTrait
3541 morph_traits,
3542 traits;
3543
3544 channel=GetPixelChannelMapChannel(image,i);
3545 traits=GetPixelChannelMapTraits(image,channel);
3546 morph_traits=GetPixelChannelMapTraits(morph_image,channel);
3547 if ((traits == UndefinedPixelTrait) ||
3548 (morph_traits == UndefinedPixelTrait))
3549 continue;
3550 if ((morph_traits & CopyPixelTrait) != 0)
3551 {
3552 SetPixelChannel(morph_image,channel,p[i],q);
3553 continue;
3554 }
3555 SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3556 GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3557 }
cristyed231572011-07-14 02:18:59 +00003558 p+=GetPixelChannels(morph_image);
3559 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003560 }
3561 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3562 if (sync == MagickFalse)
3563 status=MagickFalse;
3564 }
3565 morph_view=DestroyCacheView(morph_view);
3566 image_view=DestroyCacheView(image_view);
3567 morph_image=DestroyImage(morph_image);
3568 }
cristybb503372010-05-27 20:51:26 +00003569 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003570 break;
3571 /*
3572 Clone last frame in sequence.
3573 */
3574 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3575 if (morph_image == (Image *) NULL)
3576 {
3577 morph_images=DestroyImageList(morph_images);
3578 return((Image *) NULL);
3579 }
3580 AppendImageToList(&morph_images,morph_image);
3581 morph_images=GetLastImageInList(morph_images);
3582 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3583 {
3584 MagickBooleanType
3585 proceed;
3586
cristyb5d5f722009-11-04 03:03:49 +00003587#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003588 #pragma omp critical (MagickCore_MorphImages)
3589#endif
3590 proceed=SetImageProgress(image,MorphImageTag,scene,
3591 GetImageListLength(image));
3592 if (proceed == MagickFalse)
3593 status=MagickFalse;
3594 }
3595 scene++;
3596 }
3597 if (GetNextImageInList(next) != (Image *) NULL)
3598 {
3599 morph_images=DestroyImageList(morph_images);
3600 return((Image *) NULL);
3601 }
3602 return(GetFirstImageInList(morph_images));
3603}
3604
3605/*
3606%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3607% %
3608% %
3609% %
3610% P l a s m a I m a g e %
3611% %
3612% %
3613% %
3614%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3615%
3616% PlasmaImage() initializes an image with plasma fractal values. The image
3617% must be initialized with a base color and the random number generator
3618% seeded before this method is called.
3619%
3620% The format of the PlasmaImage method is:
3621%
3622% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristy5cbc0162011-08-29 00:36:28 +00003623% size_t attenuate,size_t depth,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003624%
3625% A description of each parameter follows:
3626%
3627% o image: the image.
3628%
3629% o segment: Define the region to apply plasma fractals values.
3630%
glennrp7dae1ca2010-09-16 12:17:35 +00003631% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003632%
3633% o depth: Limit the plasma recursion depth.
3634%
cristy5cbc0162011-08-29 00:36:28 +00003635% o exception: return any errors or warnings in this structure.
3636%
cristy3ed852e2009-09-05 21:47:34 +00003637*/
3638
3639static inline Quantum PlasmaPixel(RandomInfo *random_info,
3640 const MagickRealType pixel,const MagickRealType noise)
3641{
3642 Quantum
3643 plasma;
3644
cristyce70c172010-01-07 17:15:30 +00003645 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003646 noise/2.0);
3647 return(plasma);
3648}
3649
cristyda1f9c12011-10-02 21:39:49 +00003650static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
3651 CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3652 const SegmentInfo *segment,size_t attenuate,size_t depth,
3653 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003654{
cristy3ed852e2009-09-05 21:47:34 +00003655 MagickRealType
3656 plasma;
3657
cristyda1f9c12011-10-02 21:39:49 +00003658 PixelChannel
3659 channel;
3660
3661 PixelTrait
3662 traits;
3663
3664 register const Quantum
3665 *restrict u,
3666 *restrict v;
3667
3668 register Quantum
3669 *restrict q;
3670
3671 register ssize_t
3672 i;
cristy3ed852e2009-09-05 21:47:34 +00003673
cristy9d314ff2011-03-09 01:30:28 +00003674 ssize_t
3675 x,
3676 x_mid,
3677 y,
3678 y_mid;
3679
cristy3ed852e2009-09-05 21:47:34 +00003680 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3681 return(MagickTrue);
3682 if (depth != 0)
3683 {
3684 SegmentInfo
3685 local_info;
3686
3687 /*
3688 Divide the area into quadrants and recurse.
3689 */
3690 depth--;
3691 attenuate++;
cristybb503372010-05-27 20:51:26 +00003692 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3693 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003694 local_info=(*segment);
3695 local_info.x2=(double) x_mid;
3696 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003697 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3698 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003699 local_info=(*segment);
3700 local_info.y1=(double) y_mid;
3701 local_info.x2=(double) x_mid;
cristyda1f9c12011-10-02 21:39:49 +00003702 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3703 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003704 local_info=(*segment);
3705 local_info.x1=(double) x_mid;
3706 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003707 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3708 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003709 local_info=(*segment);
3710 local_info.x1=(double) x_mid;
3711 local_info.y1=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003712 return(PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3713 &local_info,attenuate,depth,exception));
cristy3ed852e2009-09-05 21:47:34 +00003714 }
cristybb503372010-05-27 20:51:26 +00003715 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3716 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003717 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3718 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3719 return(MagickFalse);
3720 /*
3721 Average pixels and apply plasma.
3722 */
cristy3ed852e2009-09-05 21:47:34 +00003723 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3724 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3725 {
cristy3ed852e2009-09-05 21:47:34 +00003726 /*
3727 Left pixel.
3728 */
cristybb503372010-05-27 20:51:26 +00003729 x=(ssize_t) ceil(segment->x1-0.5);
cristy1707c6c2012-01-18 23:30:54 +00003730 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3731 exception);
3732 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3733 exception);
cristyc5c6f662010-09-22 14:23:02 +00003734 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003735 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3736 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003737 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003738 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3739 {
cristye2a912b2011-12-05 20:02:07 +00003740 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003741 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003742 if (traits == UndefinedPixelTrait)
3743 continue;
3744 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3745 }
cristyc5c6f662010-09-22 14:23:02 +00003746 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003747 if (segment->x1 != segment->x2)
3748 {
3749 /*
3750 Right pixel.
3751 */
cristybb503372010-05-27 20:51:26 +00003752 x=(ssize_t) ceil(segment->x2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003753 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3754 1,1,exception);
3755 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3756 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003757 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003758 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3759 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003760 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003761 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3762 {
cristye2a912b2011-12-05 20:02:07 +00003763 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003764 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003765 if (traits == UndefinedPixelTrait)
3766 continue;
3767 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3768 }
cristyc5c6f662010-09-22 14:23:02 +00003769 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003770 }
3771 }
3772 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3773 {
3774 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3775 {
cristy3ed852e2009-09-05 21:47:34 +00003776 /*
3777 Bottom pixel.
3778 */
cristybb503372010-05-27 20:51:26 +00003779 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003780 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3781 1,1,exception);
3782 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3783 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003784 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003785 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3786 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003787 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003788 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3789 {
cristye2a912b2011-12-05 20:02:07 +00003790 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003791 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003792 if (traits == UndefinedPixelTrait)
3793 continue;
3794 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3795 }
cristyc5c6f662010-09-22 14:23:02 +00003796 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003797 }
3798 if (segment->y1 != segment->y2)
3799 {
cristy3ed852e2009-09-05 21:47:34 +00003800 /*
3801 Top pixel.
3802 */
cristybb503372010-05-27 20:51:26 +00003803 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003804 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3805 1,1,exception);
3806 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3807 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003808 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003809 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3810 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003811 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003812 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3813 {
cristye2a912b2011-12-05 20:02:07 +00003814 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003815 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003816 if (traits == UndefinedPixelTrait)
3817 continue;
3818 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3819 }
cristyc5c6f662010-09-22 14:23:02 +00003820 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003821 }
3822 }
3823 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3824 {
cristy3ed852e2009-09-05 21:47:34 +00003825 /*
3826 Middle pixel.
3827 */
cristybb503372010-05-27 20:51:26 +00003828 x=(ssize_t) ceil(segment->x1-0.5);
3829 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003830 u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
cristybb503372010-05-27 20:51:26 +00003831 x=(ssize_t) ceil(segment->x2-0.5);
3832 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003833 v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003834 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003835 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3836 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003837 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003838 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3839 {
cristye2a912b2011-12-05 20:02:07 +00003840 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003841 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003842 if (traits == UndefinedPixelTrait)
3843 continue;
3844 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3845 }
cristyc5c6f662010-09-22 14:23:02 +00003846 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003847 }
3848 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3849 return(MagickTrue);
3850 return(MagickFalse);
3851}
cristyda1f9c12011-10-02 21:39:49 +00003852
cristy3ed852e2009-09-05 21:47:34 +00003853MagickExport MagickBooleanType PlasmaImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00003854 const SegmentInfo *segment,size_t attenuate,size_t depth,
3855 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003856{
cristyc5c6f662010-09-22 14:23:02 +00003857 CacheView
cristyda1f9c12011-10-02 21:39:49 +00003858 *image_view,
3859 *u_view,
3860 *v_view;
cristyc5c6f662010-09-22 14:23:02 +00003861
cristy3ed852e2009-09-05 21:47:34 +00003862 MagickBooleanType
3863 status;
3864
3865 RandomInfo
3866 *random_info;
3867
3868 if (image->debug != MagickFalse)
3869 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3870 assert(image != (Image *) NULL);
3871 assert(image->signature == MagickSignature);
3872 if (image->debug != MagickFalse)
3873 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy5cbc0162011-08-29 00:36:28 +00003874 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristyc5c6f662010-09-22 14:23:02 +00003875 return(MagickFalse);
3876 image_view=AcquireCacheView(image);
cristyda1f9c12011-10-02 21:39:49 +00003877 u_view=AcquireCacheView(image);
3878 v_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003879 random_info=AcquireRandomInfo();
cristyda1f9c12011-10-02 21:39:49 +00003880 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
3881 attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003882 random_info=DestroyRandomInfo(random_info);
cristyda1f9c12011-10-02 21:39:49 +00003883 v_view=DestroyCacheView(v_view);
3884 u_view=DestroyCacheView(u_view);
cristyc5c6f662010-09-22 14:23:02 +00003885 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003886 return(status);
3887}
3888
3889/*
3890%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3891% %
3892% %
3893% %
3894% P o l a r o i d I m a g e %
3895% %
3896% %
3897% %
3898%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3899%
3900% PolaroidImage() simulates a Polaroid picture.
3901%
3902% The format of the AnnotateImage method is:
3903%
3904% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003905% const char *caption,const double angle,
3906% const PixelInterpolateMethod method,ExceptionInfo exception)
cristy3ed852e2009-09-05 21:47:34 +00003907%
3908% A description of each parameter follows:
3909%
3910% o image: the image.
3911%
3912% o draw_info: the draw info.
3913%
cristye9e3d382011-12-14 01:50:13 +00003914% o caption: the Polaroid caption.
3915%
cristycee97112010-05-28 00:44:52 +00003916% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003917%
cristy5c4e2582011-09-11 19:21:03 +00003918% o method: the pixel interpolation method.
3919%
cristy3ed852e2009-09-05 21:47:34 +00003920% o exception: return any errors or warnings in this structure.
3921%
3922*/
3923MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003924 const char *caption,const double angle,const PixelInterpolateMethod method,
cristy5c4e2582011-09-11 19:21:03 +00003925 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003926{
cristy3ed852e2009-09-05 21:47:34 +00003927 Image
3928 *bend_image,
3929 *caption_image,
3930 *flop_image,
3931 *picture_image,
3932 *polaroid_image,
3933 *rotate_image,
3934 *trim_image;
3935
cristybb503372010-05-27 20:51:26 +00003936 size_t
cristy3ed852e2009-09-05 21:47:34 +00003937 height;
3938
cristy9d314ff2011-03-09 01:30:28 +00003939 ssize_t
3940 quantum;
3941
cristy3ed852e2009-09-05 21:47:34 +00003942 /*
3943 Simulate a Polaroid picture.
3944 */
3945 assert(image != (Image *) NULL);
3946 assert(image->signature == MagickSignature);
3947 if (image->debug != MagickFalse)
3948 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3949 assert(exception != (ExceptionInfo *) NULL);
3950 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003951 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003952 image->rows)/25.0,10.0);
3953 height=image->rows+2*quantum;
3954 caption_image=(Image *) NULL;
cristye9e3d382011-12-14 01:50:13 +00003955 if (caption != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003956 {
3957 char
cristye9e3d382011-12-14 01:50:13 +00003958 geometry[MaxTextExtent],
3959 *text;
cristy3ed852e2009-09-05 21:47:34 +00003960
3961 DrawInfo
3962 *annotate_info;
3963
cristy3ed852e2009-09-05 21:47:34 +00003964 MagickBooleanType
3965 status;
3966
cristy9d314ff2011-03-09 01:30:28 +00003967 ssize_t
3968 count;
3969
cristy3ed852e2009-09-05 21:47:34 +00003970 TypeMetric
3971 metrics;
3972
3973 /*
3974 Generate caption image.
3975 */
3976 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3977 if (caption_image == (Image *) NULL)
3978 return((Image *) NULL);
3979 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
cristye9e3d382011-12-14 01:50:13 +00003980 text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
3981 exception);
3982 (void) CloneString(&annotate_info->text,text);
cristy6b1d05e2010-09-22 19:17:27 +00003983 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
cristye9e3d382011-12-14 01:50:13 +00003984 &text,exception);
3985 status=SetImageExtent(caption_image,image->columns,(size_t) ((count+1)*
3986 (metrics.ascent-metrics.descent)+0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00003987 if (status == MagickFalse)
3988 caption_image=DestroyImage(caption_image);
3989 else
3990 {
3991 caption_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00003992 (void) SetImageBackgroundColor(caption_image,exception);
cristye9e3d382011-12-14 01:50:13 +00003993 (void) CloneString(&annotate_info->text,text);
cristyb51dff52011-05-19 16:55:47 +00003994 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00003995 metrics.ascent);
3996 if (annotate_info->gravity == UndefinedGravity)
3997 (void) CloneString(&annotate_info->geometry,AcquireString(
3998 geometry));
cristy5cbc0162011-08-29 00:36:28 +00003999 (void) AnnotateImage(caption_image,annotate_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004000 height+=caption_image->rows;
4001 }
4002 annotate_info=DestroyDrawInfo(annotate_info);
cristye9e3d382011-12-14 01:50:13 +00004003 text=DestroyString(text);
cristy3ed852e2009-09-05 21:47:34 +00004004 }
4005 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4006 exception);
4007 if (picture_image == (Image *) NULL)
4008 {
4009 if (caption_image != (Image *) NULL)
4010 caption_image=DestroyImage(caption_image);
4011 return((Image *) NULL);
4012 }
4013 picture_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00004014 (void) SetImageBackgroundColor(picture_image,exception);
cristye941a752011-10-15 01:52:48 +00004015 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum,
4016 exception);
cristy3ed852e2009-09-05 21:47:34 +00004017 if (caption_image != (Image *) NULL)
4018 {
cristy1707c6c2012-01-18 23:30:54 +00004019 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,quantum,
4020 (ssize_t) (image->rows+3*quantum/2),exception);
cristy3ed852e2009-09-05 21:47:34 +00004021 caption_image=DestroyImage(caption_image);
4022 }
cristy9950d572011-10-01 18:22:35 +00004023 (void) QueryColorCompliance("none",AllCompliance,
4024 &picture_image->background_color,exception);
cristy63240882011-08-05 19:05:27 +00004025 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004026 rotate_image=RotateImage(picture_image,90.0,exception);
4027 picture_image=DestroyImage(picture_image);
4028 if (rotate_image == (Image *) NULL)
4029 return((Image *) NULL);
4030 picture_image=rotate_image;
4031 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
cristy5c4e2582011-09-11 19:21:03 +00004032 picture_image->columns,method,exception);
cristy3ed852e2009-09-05 21:47:34 +00004033 picture_image=DestroyImage(picture_image);
4034 if (bend_image == (Image *) NULL)
4035 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004036 picture_image=bend_image;
4037 rotate_image=RotateImage(picture_image,-90.0,exception);
4038 picture_image=DestroyImage(picture_image);
4039 if (rotate_image == (Image *) NULL)
4040 return((Image *) NULL);
4041 picture_image=rotate_image;
4042 picture_image->background_color=image->background_color;
cristy78ec1a92011-12-09 09:25:34 +00004043 polaroid_image=ShadowImage(picture_image,80.0,2.0,0.0,quantum/3,quantum/3,
cristy3ed852e2009-09-05 21:47:34 +00004044 exception);
4045 if (polaroid_image == (Image *) NULL)
4046 {
4047 picture_image=DestroyImage(picture_image);
4048 return(picture_image);
4049 }
4050 flop_image=FlopImage(polaroid_image,exception);
4051 polaroid_image=DestroyImage(polaroid_image);
4052 if (flop_image == (Image *) NULL)
4053 {
4054 picture_image=DestroyImage(picture_image);
4055 return(picture_image);
4056 }
4057 polaroid_image=flop_image;
cristy1707c6c2012-01-18 23:30:54 +00004058 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,(ssize_t)
4059 (-0.01*picture_image->columns/2.0),0L,exception);
cristy3ed852e2009-09-05 21:47:34 +00004060 picture_image=DestroyImage(picture_image);
cristy9950d572011-10-01 18:22:35 +00004061 (void) QueryColorCompliance("none",AllCompliance,
4062 &polaroid_image->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00004063 rotate_image=RotateImage(polaroid_image,angle,exception);
4064 polaroid_image=DestroyImage(polaroid_image);
4065 if (rotate_image == (Image *) NULL)
4066 return((Image *) NULL);
4067 polaroid_image=rotate_image;
4068 trim_image=TrimImage(polaroid_image,exception);
4069 polaroid_image=DestroyImage(polaroid_image);
4070 if (trim_image == (Image *) NULL)
4071 return((Image *) NULL);
4072 polaroid_image=trim_image;
4073 return(polaroid_image);
4074}
4075
4076/*
4077%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4078% %
4079% %
4080% %
cristy3ed852e2009-09-05 21:47:34 +00004081% S e p i a T o n e I m a g e %
4082% %
4083% %
4084% %
4085%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4086%
4087% MagickSepiaToneImage() applies a special effect to the image, similar to the
4088% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4089% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4090% threshold of 80% is a good starting point for a reasonable tone.
4091%
4092% The format of the SepiaToneImage method is:
4093%
4094% Image *SepiaToneImage(const Image *image,const double threshold,
4095% ExceptionInfo *exception)
4096%
4097% A description of each parameter follows:
4098%
4099% o image: the image.
4100%
4101% o threshold: the tone threshold.
4102%
4103% o exception: return any errors or warnings in this structure.
4104%
4105*/
4106MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4107 ExceptionInfo *exception)
4108{
4109#define SepiaToneImageTag "SepiaTone/Image"
4110
cristyc4c8d132010-01-07 01:58:38 +00004111 CacheView
4112 *image_view,
4113 *sepia_view;
4114
cristy3ed852e2009-09-05 21:47:34 +00004115 Image
4116 *sepia_image;
4117
cristy3ed852e2009-09-05 21:47:34 +00004118 MagickBooleanType
4119 status;
4120
cristybb503372010-05-27 20:51:26 +00004121 MagickOffsetType
4122 progress;
4123
4124 ssize_t
4125 y;
4126
cristy3ed852e2009-09-05 21:47:34 +00004127 /*
4128 Initialize sepia-toned image attributes.
4129 */
4130 assert(image != (const Image *) NULL);
4131 assert(image->signature == MagickSignature);
4132 if (image->debug != MagickFalse)
4133 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4134 assert(exception != (ExceptionInfo *) NULL);
4135 assert(exception->signature == MagickSignature);
cristy1707c6c2012-01-18 23:30:54 +00004136 sepia_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004137 if (sepia_image == (Image *) NULL)
4138 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004139 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004140 {
cristy3ed852e2009-09-05 21:47:34 +00004141 sepia_image=DestroyImage(sepia_image);
4142 return((Image *) NULL);
4143 }
4144 /*
4145 Tone each row of the image.
4146 */
4147 status=MagickTrue;
4148 progress=0;
4149 image_view=AcquireCacheView(image);
4150 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004151#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004152 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004153#endif
cristybb503372010-05-27 20:51:26 +00004154 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004155 {
cristy4c08aed2011-07-01 19:47:50 +00004156 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004157 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004158
cristybb503372010-05-27 20:51:26 +00004159 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004160 x;
4161
cristy4c08aed2011-07-01 19:47:50 +00004162 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004163 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004164
4165 if (status == MagickFalse)
4166 continue;
4167 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00004168 q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004169 exception);
cristy4c08aed2011-07-01 19:47:50 +00004170 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004171 {
4172 status=MagickFalse;
4173 continue;
4174 }
cristybb503372010-05-27 20:51:26 +00004175 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004176 {
4177 MagickRealType
4178 intensity,
4179 tone;
4180
cristy4c08aed2011-07-01 19:47:50 +00004181 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004182 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4183 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004184 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004185 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4186 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004187 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004188 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004189 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004190 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004191 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4192 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4193 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4194 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004195 p+=GetPixelChannels(image);
4196 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004197 }
4198 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4199 status=MagickFalse;
4200 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4201 {
4202 MagickBooleanType
4203 proceed;
4204
cristyb5d5f722009-11-04 03:03:49 +00004205#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004206 #pragma omp critical (MagickCore_SepiaToneImage)
4207#endif
4208 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4209 image->rows);
4210 if (proceed == MagickFalse)
4211 status=MagickFalse;
4212 }
4213 }
4214 sepia_view=DestroyCacheView(sepia_view);
4215 image_view=DestroyCacheView(image_view);
cristye23ec9d2011-08-16 18:15:40 +00004216 (void) NormalizeImage(sepia_image,exception);
4217 (void) ContrastImage(sepia_image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004218 if (status == MagickFalse)
4219 sepia_image=DestroyImage(sepia_image);
4220 return(sepia_image);
4221}
4222
4223/*
4224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4225% %
4226% %
4227% %
4228% S h a d o w I m a g e %
4229% %
4230% %
4231% %
4232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4233%
4234% ShadowImage() simulates a shadow from the specified image and returns it.
4235%
4236% The format of the ShadowImage method is:
4237%
cristy70cddf72011-12-10 22:42:42 +00004238% Image *ShadowImage(const Image *image,const double alpha,
cristyeb6e6582011-12-09 09:14:23 +00004239% const double sigma,const double bias,const ssize_t x_offset,
4240% const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004241%
4242% A description of each parameter follows:
4243%
4244% o image: the image.
4245%
cristy70cddf72011-12-10 22:42:42 +00004246% o alpha: percentage transparency.
cristy3ed852e2009-09-05 21:47:34 +00004247%
4248% o sigma: the standard deviation of the Gaussian, in pixels.
4249%
cristyeb6e6582011-12-09 09:14:23 +00004250% o bias: the bias.
4251%
cristy3ed852e2009-09-05 21:47:34 +00004252% o x_offset: the shadow x-offset.
4253%
4254% o y_offset: the shadow y-offset.
4255%
4256% o exception: return any errors or warnings in this structure.
4257%
4258*/
cristy70cddf72011-12-10 22:42:42 +00004259MagickExport Image *ShadowImage(const Image *image,const double alpha,
cristyeb6e6582011-12-09 09:14:23 +00004260 const double sigma,const double bias,const ssize_t x_offset,
4261 const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004262{
4263#define ShadowImageTag "Shadow/Image"
4264
cristy70cddf72011-12-10 22:42:42 +00004265 CacheView
4266 *image_view;
4267
cristybd5a96c2011-08-21 00:04:26 +00004268 ChannelType
4269 channel_mask;
4270
cristy3ed852e2009-09-05 21:47:34 +00004271 Image
4272 *border_image,
4273 *clone_image,
4274 *shadow_image;
4275
cristy70cddf72011-12-10 22:42:42 +00004276 MagickBooleanType
4277 status;
4278
cristy3ed852e2009-09-05 21:47:34 +00004279 RectangleInfo
4280 border_info;
4281
cristy70cddf72011-12-10 22:42:42 +00004282 ssize_t
4283 y;
4284
cristy3ed852e2009-09-05 21:47:34 +00004285 assert(image != (Image *) NULL);
4286 assert(image->signature == MagickSignature);
4287 if (image->debug != MagickFalse)
4288 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4289 assert(exception != (ExceptionInfo *) NULL);
4290 assert(exception->signature == MagickSignature);
4291 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4292 if (clone_image == (Image *) NULL)
4293 return((Image *) NULL);
4294 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
cristybb503372010-05-27 20:51:26 +00004295 border_info.width=(size_t) floor(2.0*sigma+0.5);
4296 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004297 border_info.x=0;
4298 border_info.y=0;
cristy9950d572011-10-01 18:22:35 +00004299 (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4300 exception);
cristy70cddf72011-12-10 22:42:42 +00004301 clone_image->matte=MagickTrue;
4302 border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
cristy3ed852e2009-09-05 21:47:34 +00004303 clone_image=DestroyImage(clone_image);
4304 if (border_image == (Image *) NULL)
4305 return((Image *) NULL);
4306 if (border_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004307 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004308 /*
4309 Shadow image.
4310 */
cristy70cddf72011-12-10 22:42:42 +00004311 status=MagickTrue;
4312 image_view=AcquireCacheView(border_image);
4313 for (y=0; y < (ssize_t) border_image->rows; y++)
4314 {
4315 PixelInfo
4316 background_color;
4317
4318 register Quantum
4319 *restrict q;
4320
4321 register ssize_t
4322 x;
4323
4324 if (status == MagickFalse)
4325 continue;
4326 q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4327 exception);
4328 if (q == (Quantum *) NULL)
4329 {
4330 status=MagickFalse;
4331 continue;
4332 }
4333 background_color=border_image->background_color;
4334 background_color.matte=MagickTrue;
4335 for (x=0; x < (ssize_t) border_image->columns; x++)
4336 {
4337 if (border_image->matte != MagickFalse)
4338 background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4339 SetPixelInfoPixel(border_image,&background_color,q);
4340 q+=GetPixelChannels(border_image);
4341 }
4342 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4343 status=MagickFalse;
4344 }
4345 image_view=DestroyCacheView(image_view);
4346 if (status == MagickFalse)
4347 {
4348 border_image=DestroyImage(border_image);
4349 return((Image *) NULL);
4350 }
cristybd5a96c2011-08-21 00:04:26 +00004351 channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
cristyb27bb772011-12-11 16:12:50 +00004352 shadow_image=BlurImage(border_image,0.0,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00004353 border_image=DestroyImage(border_image);
4354 if (shadow_image == (Image *) NULL)
4355 return((Image *) NULL);
cristyae1969f2011-12-10 03:07:36 +00004356 (void) SetPixelChannelMapMask(shadow_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004357 if (shadow_image->page.width == 0)
4358 shadow_image->page.width=shadow_image->columns;
4359 if (shadow_image->page.height == 0)
4360 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004361 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4362 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4363 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4364 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004365 return(shadow_image);
4366}
4367
4368/*
4369%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4370% %
4371% %
4372% %
4373% S k e t c h I m a g e %
4374% %
4375% %
4376% %
4377%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4378%
4379% SketchImage() simulates a pencil sketch. We convolve the image with a
4380% Gaussian operator of the given radius and standard deviation (sigma). For
4381% reasonable results, radius should be larger than sigma. Use a radius of 0
4382% and SketchImage() selects a suitable radius for you. Angle gives the angle
4383% of the sketch.
4384%
4385% The format of the SketchImage method is:
4386%
4387% Image *SketchImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00004388% const double sigma,const double angle,const double bias,
4389% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004390%
4391% A description of each parameter follows:
4392%
4393% o image: the image.
4394%
cristy574cc262011-08-05 01:23:58 +00004395% o radius: the radius of the Gaussian, in pixels, not counting the
4396% center pixel.
cristy3ed852e2009-09-05 21:47:34 +00004397%
4398% o sigma: the standard deviation of the Gaussian, in pixels.
4399%
cristyf7ef0252011-09-09 14:50:06 +00004400% o angle: apply the effect along this angle.
4401%
4402% o bias: the bias.
cristy3ed852e2009-09-05 21:47:34 +00004403%
4404% o exception: return any errors or warnings in this structure.
4405%
4406*/
4407MagickExport Image *SketchImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00004408 const double sigma,const double angle,const double bias,
4409 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004410{
cristyfa112112010-01-04 17:48:07 +00004411 CacheView
4412 *random_view;
4413
cristy3ed852e2009-09-05 21:47:34 +00004414 Image
4415 *blend_image,
4416 *blur_image,
4417 *dodge_image,
4418 *random_image,
4419 *sketch_image;
4420
cristy3ed852e2009-09-05 21:47:34 +00004421 MagickBooleanType
4422 status;
4423
cristy3ed852e2009-09-05 21:47:34 +00004424 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004425 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004426
cristy9d314ff2011-03-09 01:30:28 +00004427 ssize_t
4428 y;
4429
cristy3ed852e2009-09-05 21:47:34 +00004430 /*
4431 Sketch image.
4432 */
4433 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4434 MagickTrue,exception);
4435 if (random_image == (Image *) NULL)
4436 return((Image *) NULL);
4437 status=MagickTrue;
cristy1b784432009-12-19 02:20:40 +00004438 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004439 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004440#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004441 #pragma omp parallel for schedule(static,4) shared(status)
cristy1b784432009-12-19 02:20:40 +00004442#endif
cristybb503372010-05-27 20:51:26 +00004443 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004444 {
cristy5c9e6f22010-09-17 17:31:01 +00004445 const int
4446 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004447
cristybb503372010-05-27 20:51:26 +00004448 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004449 x;
4450
cristy4c08aed2011-07-01 19:47:50 +00004451 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004452 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004453
cristy1b784432009-12-19 02:20:40 +00004454 if (status == MagickFalse)
4455 continue;
cristy3ed852e2009-09-05 21:47:34 +00004456 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4457 exception);
cristyacd2ed22011-08-30 01:44:23 +00004458 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004459 {
4460 status=MagickFalse;
4461 continue;
4462 }
cristybb503372010-05-27 20:51:26 +00004463 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004464 {
cristy76f512e2011-09-12 01:26:56 +00004465 MagickRealType
4466 value;
4467
4468 register ssize_t
4469 i;
4470
4471 value=GetPseudoRandomValue(random_info[id]);
4472 for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4473 {
cristyabace412011-12-11 15:56:53 +00004474 PixelChannel
4475 channel;
4476
cristy76f512e2011-09-12 01:26:56 +00004477 PixelTrait
4478 traits;
4479
cristyabace412011-12-11 15:56:53 +00004480 channel=GetPixelChannelMapChannel(image,i);
4481 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004482 if (traits == UndefinedPixelTrait)
4483 continue;
4484 q[i]=ClampToQuantum(QuantumRange*value);
4485 }
cristyed231572011-07-14 02:18:59 +00004486 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004487 }
4488 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4489 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004490 }
4491 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004492 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004493 if (status == MagickFalse)
4494 {
4495 random_image=DestroyImage(random_image);
4496 return(random_image);
4497 }
cristyf7ef0252011-09-09 14:50:06 +00004498 blur_image=MotionBlurImage(random_image,radius,sigma,angle,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00004499 random_image=DestroyImage(random_image);
4500 if (blur_image == (Image *) NULL)
4501 return((Image *) NULL);
cristy6bfd6902011-12-09 01:33:45 +00004502 dodge_image=EdgeImage(blur_image,radius,1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004503 blur_image=DestroyImage(blur_image);
4504 if (dodge_image == (Image *) NULL)
4505 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +00004506 (void) NormalizeImage(dodge_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00004507 (void) NegateImage(dodge_image,MagickFalse,exception);
cristye941a752011-10-15 01:52:48 +00004508 (void) TransformImage(&dodge_image,(char *) NULL,"50%",exception);
cristy3ed852e2009-09-05 21:47:34 +00004509 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4510 if (sketch_image == (Image *) NULL)
4511 {
4512 dodge_image=DestroyImage(dodge_image);
4513 return((Image *) NULL);
4514 }
cristye941a752011-10-15 01:52:48 +00004515 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0,
4516 exception);
cristy3ed852e2009-09-05 21:47:34 +00004517 dodge_image=DestroyImage(dodge_image);
4518 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4519 if (blend_image == (Image *) NULL)
4520 {
4521 sketch_image=DestroyImage(sketch_image);
4522 return((Image *) NULL);
4523 }
4524 (void) SetImageArtifact(blend_image,"compose:args","20x80");
cristye941a752011-10-15 01:52:48 +00004525 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0,
4526 exception);
cristy3ed852e2009-09-05 21:47:34 +00004527 blend_image=DestroyImage(blend_image);
4528 return(sketch_image);
4529}
4530
4531/*
4532%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4533% %
4534% %
4535% %
4536% S o l a r i z e I m a g e %
4537% %
4538% %
4539% %
4540%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4541%
4542% SolarizeImage() applies a special effect to the image, similar to the effect
4543% achieved in a photo darkroom by selectively exposing areas of photo
4544% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4545% measure of the extent of the solarization.
4546%
4547% The format of the SolarizeImage method is:
4548%
cristy5cbc0162011-08-29 00:36:28 +00004549% MagickBooleanType SolarizeImage(Image *image,const double threshold,
4550% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004551%
4552% A description of each parameter follows:
4553%
4554% o image: the image.
4555%
4556% o threshold: Define the extent of the solarization.
4557%
cristy5cbc0162011-08-29 00:36:28 +00004558% o exception: return any errors or warnings in this structure.
4559%
cristy3ed852e2009-09-05 21:47:34 +00004560*/
4561MagickExport MagickBooleanType SolarizeImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00004562 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004563{
4564#define SolarizeImageTag "Solarize/Image"
4565
cristyc4c8d132010-01-07 01:58:38 +00004566 CacheView
4567 *image_view;
4568
cristy3ed852e2009-09-05 21:47:34 +00004569 MagickBooleanType
4570 status;
4571
cristybb503372010-05-27 20:51:26 +00004572 MagickOffsetType
4573 progress;
4574
4575 ssize_t
4576 y;
4577
cristy3ed852e2009-09-05 21:47:34 +00004578 assert(image != (Image *) NULL);
4579 assert(image->signature == MagickSignature);
4580 if (image->debug != MagickFalse)
4581 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4582 if (image->storage_class == PseudoClass)
4583 {
cristybb503372010-05-27 20:51:26 +00004584 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004585 i;
4586
4587 /*
4588 Solarize colormap.
4589 */
cristybb503372010-05-27 20:51:26 +00004590 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004591 {
4592 if ((MagickRealType) image->colormap[i].red > threshold)
4593 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4594 if ((MagickRealType) image->colormap[i].green > threshold)
4595 image->colormap[i].green=(Quantum) QuantumRange-
4596 image->colormap[i].green;
4597 if ((MagickRealType) image->colormap[i].blue > threshold)
4598 image->colormap[i].blue=(Quantum) QuantumRange-
4599 image->colormap[i].blue;
4600 }
4601 }
4602 /*
4603 Solarize image.
4604 */
4605 status=MagickTrue;
4606 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00004607 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004608#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004609 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004610#endif
cristybb503372010-05-27 20:51:26 +00004611 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004612 {
cristybb503372010-05-27 20:51:26 +00004613 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004614 x;
4615
cristy4c08aed2011-07-01 19:47:50 +00004616 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004617 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004618
4619 if (status == MagickFalse)
4620 continue;
cristy5cbc0162011-08-29 00:36:28 +00004621 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00004622 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004623 {
4624 status=MagickFalse;
4625 continue;
4626 }
cristybb503372010-05-27 20:51:26 +00004627 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004628 {
cristy76f512e2011-09-12 01:26:56 +00004629 register ssize_t
4630 i;
4631
4632 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4633 {
cristyabace412011-12-11 15:56:53 +00004634 PixelChannel
4635 channel;
4636
cristy76f512e2011-09-12 01:26:56 +00004637 PixelTrait
4638 traits;
4639
cristyabace412011-12-11 15:56:53 +00004640 channel=GetPixelChannelMapChannel(image,i);
4641 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004642 if ((traits == UndefinedPixelTrait) ||
4643 ((traits & CopyPixelTrait) != 0))
4644 continue;
4645 if ((MagickRealType) q[i] > threshold)
4646 q[i]=QuantumRange-q[i];
4647 }
cristyed231572011-07-14 02:18:59 +00004648 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004649 }
4650 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4651 status=MagickFalse;
4652 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4653 {
4654 MagickBooleanType
4655 proceed;
4656
cristyb5d5f722009-11-04 03:03:49 +00004657#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004658 #pragma omp critical (MagickCore_SolarizeImage)
4659#endif
4660 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4661 if (proceed == MagickFalse)
4662 status=MagickFalse;
4663 }
4664 }
4665 image_view=DestroyCacheView(image_view);
4666 return(status);
4667}
4668
4669/*
4670%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4671% %
4672% %
4673% %
4674% S t e g a n o I m a g e %
4675% %
4676% %
4677% %
4678%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4679%
4680% SteganoImage() hides a digital watermark within the image. Recover
4681% the hidden watermark later to prove that the authenticity of an image.
4682% Offset defines the start position within the image to hide the watermark.
4683%
4684% The format of the SteganoImage method is:
4685%
4686% Image *SteganoImage(const Image *image,Image *watermark,
4687% ExceptionInfo *exception)
4688%
4689% A description of each parameter follows:
4690%
4691% o image: the image.
4692%
4693% o watermark: the watermark image.
4694%
4695% o exception: return any errors or warnings in this structure.
4696%
4697*/
4698MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4699 ExceptionInfo *exception)
4700{
cristye1bf8ad2010-09-19 17:07:03 +00004701#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004702#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004703 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004704#define SteganoImageTag "Stegano/Image"
4705
cristyb0d3bb92010-09-22 14:37:58 +00004706 CacheView
4707 *stegano_view,
4708 *watermark_view;
4709
cristy3ed852e2009-09-05 21:47:34 +00004710 Image
4711 *stegano_image;
4712
4713 int
4714 c;
4715
cristy3ed852e2009-09-05 21:47:34 +00004716 MagickBooleanType
4717 status;
4718
cristy101ab702011-10-13 13:06:32 +00004719 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004720 pixel;
4721
cristy4c08aed2011-07-01 19:47:50 +00004722 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004723 *q;
4724
cristye1bf8ad2010-09-19 17:07:03 +00004725 register ssize_t
4726 x;
4727
cristybb503372010-05-27 20:51:26 +00004728 size_t
cristyeaedf062010-05-29 22:36:02 +00004729 depth,
4730 one;
cristy3ed852e2009-09-05 21:47:34 +00004731
cristye1bf8ad2010-09-19 17:07:03 +00004732 ssize_t
4733 i,
4734 j,
4735 k,
4736 y;
4737
cristy3ed852e2009-09-05 21:47:34 +00004738 /*
4739 Initialize steganographic image attributes.
4740 */
4741 assert(image != (const Image *) NULL);
4742 assert(image->signature == MagickSignature);
4743 if (image->debug != MagickFalse)
4744 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4745 assert(watermark != (const Image *) NULL);
4746 assert(watermark->signature == MagickSignature);
4747 assert(exception != (ExceptionInfo *) NULL);
4748 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004749 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004750 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4751 if (stegano_image == (Image *) NULL)
4752 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004753 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004754 {
cristy3ed852e2009-09-05 21:47:34 +00004755 stegano_image=DestroyImage(stegano_image);
4756 return((Image *) NULL);
4757 }
4758 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4759 /*
4760 Hide watermark in low-order bits of image.
4761 */
4762 c=0;
4763 i=0;
4764 j=0;
4765 depth=stegano_image->depth;
4766 k=image->offset;
cristyda16f162011-02-19 23:52:17 +00004767 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004768 watermark_view=AcquireCacheView(watermark);
4769 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004770 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004771 {
cristybb503372010-05-27 20:51:26 +00004772 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004773 {
cristybb503372010-05-27 20:51:26 +00004774 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004775 {
cristyda1f9c12011-10-02 21:39:49 +00004776 Quantum
cristy5f95f4f2011-10-23 01:01:01 +00004777 virtual_pixel[CompositePixelChannel];
cristyda1f9c12011-10-02 21:39:49 +00004778
cristy1707c6c2012-01-18 23:30:54 +00004779 ssize_t
4780 offset;
4781
cristyda1f9c12011-10-02 21:39:49 +00004782 (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,virtual_pixel,
4783 exception);
4784 pixel.red=(double) virtual_pixel[RedPixelChannel];
4785 pixel.green=(double) virtual_pixel[GreenPixelChannel];
4786 pixel.blue=(double) virtual_pixel[BluePixelChannel];
4787 pixel.alpha=(double) virtual_pixel[AlphaPixelChannel];
cristy1707c6c2012-01-18 23:30:54 +00004788 offset=k/(ssize_t) stegano_image->columns;
4789 if (offset >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004790 break;
cristyb0d3bb92010-09-22 14:37:58 +00004791 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4792 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4793 exception);
cristyacd2ed22011-08-30 01:44:23 +00004794 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004795 break;
4796 switch (c)
4797 {
4798 case 0:
4799 {
cristy4c08aed2011-07-01 19:47:50 +00004800 SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004801 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004802 break;
4803 }
4804 case 1:
4805 {
cristy4c08aed2011-07-01 19:47:50 +00004806 SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004807 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004808 break;
4809 }
4810 case 2:
4811 {
cristy4c08aed2011-07-01 19:47:50 +00004812 SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
cristy101ab702011-10-13 13:06:32 +00004813 GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004814 break;
4815 }
4816 }
cristyb0d3bb92010-09-22 14:37:58 +00004817 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004818 break;
4819 c++;
4820 if (c == 3)
4821 c=0;
4822 k++;
cristybb503372010-05-27 20:51:26 +00004823 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004824 k=0;
4825 if (k == image->offset)
4826 j++;
4827 }
4828 }
cristy8b27a6d2010-02-14 03:31:15 +00004829 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004830 {
cristy8b27a6d2010-02-14 03:31:15 +00004831 MagickBooleanType
4832 proceed;
4833
4834 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4835 (depth-i),depth);
4836 if (proceed == MagickFalse)
4837 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004838 }
4839 }
cristyb0d3bb92010-09-22 14:37:58 +00004840 stegano_view=DestroyCacheView(stegano_view);
4841 watermark_view=DestroyCacheView(watermark_view);
cristy3ed852e2009-09-05 21:47:34 +00004842 if (stegano_image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00004843 (void) SyncImage(stegano_image,exception);
cristyda16f162011-02-19 23:52:17 +00004844 if (status == MagickFalse)
4845 {
4846 stegano_image=DestroyImage(stegano_image);
4847 return((Image *) NULL);
4848 }
cristy3ed852e2009-09-05 21:47:34 +00004849 return(stegano_image);
4850}
4851
4852/*
4853%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4854% %
4855% %
4856% %
4857% S t e r e o A n a g l y p h I m a g e %
4858% %
4859% %
4860% %
4861%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4862%
4863% StereoAnaglyphImage() combines two images and produces a single image that
4864% is the composite of a left and right image of a stereo pair. Special
4865% red-green stereo glasses are required to view this effect.
4866%
4867% The format of the StereoAnaglyphImage method is:
4868%
4869% Image *StereoImage(const Image *left_image,const Image *right_image,
4870% ExceptionInfo *exception)
4871% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004872% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004873% ExceptionInfo *exception)
4874%
4875% A description of each parameter follows:
4876%
4877% o left_image: the left image.
4878%
4879% o right_image: the right image.
4880%
4881% o exception: return any errors or warnings in this structure.
4882%
4883% o x_offset: amount, in pixels, by which the left image is offset to the
4884% right of the right image.
4885%
4886% o y_offset: amount, in pixels, by which the left image is offset to the
4887% bottom of the right image.
4888%
4889%
4890*/
4891MagickExport Image *StereoImage(const Image *left_image,
4892 const Image *right_image,ExceptionInfo *exception)
4893{
4894 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4895}
4896
4897MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004898 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004899 ExceptionInfo *exception)
4900{
4901#define StereoImageTag "Stereo/Image"
4902
4903 const Image
4904 *image;
4905
4906 Image
4907 *stereo_image;
4908
cristy3ed852e2009-09-05 21:47:34 +00004909 MagickBooleanType
4910 status;
4911
cristy9d314ff2011-03-09 01:30:28 +00004912 ssize_t
4913 y;
4914
cristy3ed852e2009-09-05 21:47:34 +00004915 assert(left_image != (const Image *) NULL);
4916 assert(left_image->signature == MagickSignature);
4917 if (left_image->debug != MagickFalse)
4918 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4919 left_image->filename);
4920 assert(right_image != (const Image *) NULL);
4921 assert(right_image->signature == MagickSignature);
4922 assert(exception != (ExceptionInfo *) NULL);
4923 assert(exception->signature == MagickSignature);
4924 assert(right_image != (const Image *) NULL);
4925 image=left_image;
4926 if ((left_image->columns != right_image->columns) ||
4927 (left_image->rows != right_image->rows))
4928 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4929 /*
4930 Initialize stereo image attributes.
4931 */
4932 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4933 MagickTrue,exception);
4934 if (stereo_image == (Image *) NULL)
4935 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004936 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004937 {
cristy3ed852e2009-09-05 21:47:34 +00004938 stereo_image=DestroyImage(stereo_image);
4939 return((Image *) NULL);
4940 }
4941 /*
4942 Copy left image to red channel and right image to blue channel.
4943 */
cristyda16f162011-02-19 23:52:17 +00004944 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004945 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004946 {
cristy4c08aed2011-07-01 19:47:50 +00004947 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004948 *restrict p,
4949 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004950
cristybb503372010-05-27 20:51:26 +00004951 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004952 x;
4953
cristy4c08aed2011-07-01 19:47:50 +00004954 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004955 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004956
4957 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4958 exception);
4959 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4960 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy76f512e2011-09-12 01:26:56 +00004961 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
4962 (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004963 break;
cristybb503372010-05-27 20:51:26 +00004964 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004965 {
cristy4c08aed2011-07-01 19:47:50 +00004966 SetPixelRed(image,GetPixelRed(left_image,p),r);
cristy76f512e2011-09-12 01:26:56 +00004967 SetPixelGreen(image,GetPixelGreen(right_image,q),r);
4968 SetPixelBlue(image,GetPixelBlue(right_image,q),r);
4969 if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
4970 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4971 GetPixelAlpha(right_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00004972 p+=GetPixelChannels(left_image);
cristy76f512e2011-09-12 01:26:56 +00004973 q+=GetPixelChannels(right_image);
4974 r+=GetPixelChannels(stereo_image);
cristy3ed852e2009-09-05 21:47:34 +00004975 }
4976 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4977 break;
cristy8b27a6d2010-02-14 03:31:15 +00004978 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004979 {
cristy8b27a6d2010-02-14 03:31:15 +00004980 MagickBooleanType
4981 proceed;
4982
cristybb503372010-05-27 20:51:26 +00004983 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4984 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004985 if (proceed == MagickFalse)
4986 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004987 }
4988 }
cristyda16f162011-02-19 23:52:17 +00004989 if (status == MagickFalse)
4990 {
4991 stereo_image=DestroyImage(stereo_image);
4992 return((Image *) NULL);
4993 }
cristy3ed852e2009-09-05 21:47:34 +00004994 return(stereo_image);
4995}
4996
4997/*
4998%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4999% %
5000% %
5001% %
5002% S w i r l I m a g e %
5003% %
5004% %
5005% %
5006%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5007%
5008% SwirlImage() swirls the pixels about the center of the image, where
5009% degrees indicates the sweep of the arc through which each pixel is moved.
5010% You get a more dramatic effect as the degrees move from 1 to 360.
5011%
5012% The format of the SwirlImage method is:
5013%
5014% Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005015% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005016%
5017% A description of each parameter follows:
5018%
5019% o image: the image.
5020%
5021% o degrees: Define the tightness of the swirling effect.
5022%
cristy76f512e2011-09-12 01:26:56 +00005023% o method: the pixel interpolation method.
5024%
cristy3ed852e2009-09-05 21:47:34 +00005025% o exception: return any errors or warnings in this structure.
5026%
5027*/
5028MagickExport Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005029 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005030{
5031#define SwirlImageTag "Swirl/Image"
5032
cristyfa112112010-01-04 17:48:07 +00005033 CacheView
5034 *image_view,
5035 *swirl_view;
5036
cristy3ed852e2009-09-05 21:47:34 +00005037 Image
5038 *swirl_image;
5039
cristy3ed852e2009-09-05 21:47:34 +00005040 MagickBooleanType
5041 status;
5042
cristybb503372010-05-27 20:51:26 +00005043 MagickOffsetType
5044 progress;
5045
cristy3ed852e2009-09-05 21:47:34 +00005046 MagickRealType
5047 radius;
5048
5049 PointInfo
5050 center,
5051 scale;
5052
cristybb503372010-05-27 20:51:26 +00005053 ssize_t
5054 y;
5055
cristy3ed852e2009-09-05 21:47:34 +00005056 /*
5057 Initialize swirl image attributes.
5058 */
5059 assert(image != (const Image *) NULL);
5060 assert(image->signature == MagickSignature);
5061 if (image->debug != MagickFalse)
5062 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5063 assert(exception != (ExceptionInfo *) NULL);
5064 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00005065 swirl_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005066 if (swirl_image == (Image *) NULL)
5067 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005068 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005069 {
cristy3ed852e2009-09-05 21:47:34 +00005070 swirl_image=DestroyImage(swirl_image);
5071 return((Image *) NULL);
5072 }
cristy4c08aed2011-07-01 19:47:50 +00005073 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005074 swirl_image->matte=MagickTrue;
5075 /*
5076 Compute scaling factor.
5077 */
5078 center.x=(double) image->columns/2.0;
5079 center.y=(double) image->rows/2.0;
5080 radius=MagickMax(center.x,center.y);
5081 scale.x=1.0;
5082 scale.y=1.0;
5083 if (image->columns > image->rows)
5084 scale.y=(double) image->columns/(double) image->rows;
5085 else
5086 if (image->columns < image->rows)
5087 scale.x=(double) image->rows/(double) image->columns;
5088 degrees=(double) DegreesToRadians(degrees);
5089 /*
5090 Swirl image.
5091 */
5092 status=MagickTrue;
5093 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00005094 image_view=AcquireCacheView(image);
5095 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005096#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00005097 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005098#endif
cristybb503372010-05-27 20:51:26 +00005099 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005100 {
cristy3ed852e2009-09-05 21:47:34 +00005101 MagickRealType
5102 distance;
5103
5104 PointInfo
5105 delta;
5106
cristy6d188022011-09-12 13:23:33 +00005107 register const Quantum
5108 *restrict p;
5109
cristybb503372010-05-27 20:51:26 +00005110 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005111 x;
5112
cristy4c08aed2011-07-01 19:47:50 +00005113 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005114 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005115
5116 if (status == MagickFalse)
5117 continue;
cristy6d188022011-09-12 13:23:33 +00005118 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00005119 q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00005120 exception);
cristy6d188022011-09-12 13:23:33 +00005121 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005122 {
5123 status=MagickFalse;
5124 continue;
5125 }
cristy3ed852e2009-09-05 21:47:34 +00005126 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00005127 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005128 {
5129 /*
5130 Determine if the pixel is within an ellipse.
5131 */
5132 delta.x=scale.x*(double) (x-center.x);
5133 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00005134 if (distance >= (radius*radius))
5135 {
cristy1707c6c2012-01-18 23:30:54 +00005136 register ssize_t
5137 i;
5138
cristy6d188022011-09-12 13:23:33 +00005139 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy1707c6c2012-01-18 23:30:54 +00005140 {
5141 PixelChannel
5142 channel;
5143
5144 PixelTrait
5145 swirl_traits,
5146 traits;
5147
5148 channel=GetPixelChannelMapChannel(image,i);
5149 traits=GetPixelChannelMapTraits(image,channel);
5150 swirl_traits=GetPixelChannelMapTraits(swirl_image,channel);
5151 if ((traits == UndefinedPixelTrait) ||
5152 (swirl_traits == UndefinedPixelTrait))
5153 continue;
5154 SetPixelChannel(swirl_image,channel,p[i],q);
5155 }
cristy6d188022011-09-12 13:23:33 +00005156 }
5157 else
cristy3ed852e2009-09-05 21:47:34 +00005158 {
5159 MagickRealType
5160 cosine,
5161 factor,
5162 sine;
5163
5164 /*
5165 Swirl the pixel.
5166 */
5167 factor=1.0-sqrt((double) distance)/radius;
5168 sine=sin((double) (degrees*factor*factor));
5169 cosine=cos((double) (degrees*factor*factor));
cristy76f512e2011-09-12 01:26:56 +00005170 status=InterpolatePixelChannels(image,image_view,swirl_image,method,
5171 ((cosine*delta.x-sine*delta.y)/scale.x+center.x),(double)
5172 ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00005173 }
cristy6d188022011-09-12 13:23:33 +00005174 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00005175 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00005176 }
5177 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5178 status=MagickFalse;
5179 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5180 {
5181 MagickBooleanType
5182 proceed;
5183
cristyb5d5f722009-11-04 03:03:49 +00005184#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005185 #pragma omp critical (MagickCore_SwirlImage)
5186#endif
5187 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5188 if (proceed == MagickFalse)
5189 status=MagickFalse;
5190 }
5191 }
5192 swirl_view=DestroyCacheView(swirl_view);
5193 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005194 if (status == MagickFalse)
5195 swirl_image=DestroyImage(swirl_image);
5196 return(swirl_image);
5197}
5198
5199/*
5200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5201% %
5202% %
5203% %
5204% T i n t I m a g e %
5205% %
5206% %
5207% %
5208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5209%
5210% TintImage() applies a color vector to each pixel in the image. The length
5211% of the vector is 0 for black and white and at its maximum for the midtones.
5212% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5213%
5214% The format of the TintImage method is:
5215%
cristyb817c3f2011-10-03 14:00:35 +00005216% Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005217% const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005218%
5219% A description of each parameter follows:
5220%
5221% o image: the image.
5222%
cristyb817c3f2011-10-03 14:00:35 +00005223% o blend: A color value used for tinting.
cristy3ed852e2009-09-05 21:47:34 +00005224%
5225% o tint: A color value used for tinting.
5226%
5227% o exception: return any errors or warnings in this structure.
5228%
5229*/
cristyb817c3f2011-10-03 14:00:35 +00005230MagickExport Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005231 const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005232{
5233#define TintImageTag "Tint/Image"
5234
cristyc4c8d132010-01-07 01:58:38 +00005235 CacheView
5236 *image_view,
5237 *tint_view;
5238
cristy3ed852e2009-09-05 21:47:34 +00005239 GeometryInfo
5240 geometry_info;
5241
5242 Image
5243 *tint_image;
5244
cristy3ed852e2009-09-05 21:47:34 +00005245 MagickBooleanType
5246 status;
5247
cristybb503372010-05-27 20:51:26 +00005248 MagickOffsetType
5249 progress;
cristy3ed852e2009-09-05 21:47:34 +00005250
cristy28474bf2011-09-11 23:32:52 +00005251 MagickRealType
5252 intensity;
5253
cristy4c08aed2011-07-01 19:47:50 +00005254 PixelInfo
cristy1707c6c2012-01-18 23:30:54 +00005255 color_vector;
cristy3ed852e2009-09-05 21:47:34 +00005256
cristybb503372010-05-27 20:51:26 +00005257 MagickStatusType
5258 flags;
5259
5260 ssize_t
5261 y;
5262
cristy3ed852e2009-09-05 21:47:34 +00005263 /*
5264 Allocate tint image.
5265 */
5266 assert(image != (const Image *) NULL);
5267 assert(image->signature == MagickSignature);
5268 if (image->debug != MagickFalse)
5269 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5270 assert(exception != (ExceptionInfo *) NULL);
5271 assert(exception->signature == MagickSignature);
5272 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5273 if (tint_image == (Image *) NULL)
5274 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005275 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005276 {
cristy3ed852e2009-09-05 21:47:34 +00005277 tint_image=DestroyImage(tint_image);
5278 return((Image *) NULL);
5279 }
cristyaed9c382011-10-03 17:54:21 +00005280 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005281 return(tint_image);
5282 /*
5283 Determine RGB values of the color.
5284 */
cristy1707c6c2012-01-18 23:30:54 +00005285 GetPixelInfo(image,&color_vector);
cristyb817c3f2011-10-03 14:00:35 +00005286 flags=ParseGeometry(blend,&geometry_info);
cristy1707c6c2012-01-18 23:30:54 +00005287 color_vector.red=geometry_info.rho;
5288 color_vector.green=geometry_info.rho;
5289 color_vector.blue=geometry_info.rho;
5290 color_vector.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005291 if ((flags & SigmaValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005292 color_vector.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00005293 if ((flags & XiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005294 color_vector.blue=geometry_info.xi;
cristyb817c3f2011-10-03 14:00:35 +00005295 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005296 color_vector.alpha=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005297 if (image->colorspace == CMYKColorspace)
5298 {
cristy1707c6c2012-01-18 23:30:54 +00005299 color_vector.black=geometry_info.rho;
cristy76f512e2011-09-12 01:26:56 +00005300 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005301 color_vector.black=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005302 if ((flags & ChiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005303 color_vector.alpha=geometry_info.chi;
cristy76f512e2011-09-12 01:26:56 +00005304 }
cristy28474bf2011-09-11 23:32:52 +00005305 intensity=(MagickRealType) GetPixelInfoIntensity(tint);
cristy1707c6c2012-01-18 23:30:54 +00005306 color_vector.red=(MagickRealType) (color_vector.red*tint->red/100.0-
5307 intensity);
5308 color_vector.green=(MagickRealType) (color_vector.green*tint->green/100.0-
5309 intensity);
5310 color_vector.blue=(MagickRealType) (color_vector.blue*tint->blue/100.0-
5311 intensity);
5312 color_vector.black=(MagickRealType) (color_vector.black*tint->black/100.0-
5313 intensity);
5314 color_vector.alpha=(MagickRealType) (color_vector.alpha*tint->alpha/100.0-
5315 intensity);
cristy3ed852e2009-09-05 21:47:34 +00005316 /*
5317 Tint image.
5318 */
5319 status=MagickTrue;
5320 progress=0;
5321 image_view=AcquireCacheView(image);
5322 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005323#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005324 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005325#endif
cristybb503372010-05-27 20:51:26 +00005326 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005327 {
cristy4c08aed2011-07-01 19:47:50 +00005328 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005329 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005330
cristy4c08aed2011-07-01 19:47:50 +00005331 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005332 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005333
cristy6b91acb2011-04-19 12:23:54 +00005334 register ssize_t
5335 x;
5336
cristy3ed852e2009-09-05 21:47:34 +00005337 if (status == MagickFalse)
5338 continue;
5339 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5340 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5341 exception);
cristy4c08aed2011-07-01 19:47:50 +00005342 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005343 {
5344 status=MagickFalse;
5345 continue;
5346 }
cristybb503372010-05-27 20:51:26 +00005347 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005348 {
cristy4c08aed2011-07-01 19:47:50 +00005349 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005350 pixel;
5351
5352 MagickRealType
5353 weight;
5354
cristy1707c6c2012-01-18 23:30:54 +00005355 register ssize_t
5356 i;
5357
5358 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
5359 {
5360 PixelChannel
5361 channel;
5362
5363 PixelTrait
5364 tint_traits,
5365 traits;
5366
5367 channel=GetPixelChannelMapChannel(image,i);
5368 traits=GetPixelChannelMapTraits(image,channel);
5369 tint_traits=GetPixelChannelMapTraits(tint_image,channel);
5370 if ((traits == UndefinedPixelTrait) ||
5371 (tint_traits == UndefinedPixelTrait))
5372 continue;
5373 if ((tint_traits & CopyPixelTrait) != 0)
5374 {
5375 SetPixelChannel(tint_image,channel,p[i],q);
5376 continue;
5377 }
5378 }
5379 GetPixelInfo(image,&pixel);
5380 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5381 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5382 (1.0-(4.0*(weight*weight)));
5383 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5384 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5385 (1.0-(4.0*(weight*weight)));
5386 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5387 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5388 (1.0-(4.0*(weight*weight)));
5389 weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5390 pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5391 (1.0-(4.0*(weight*weight)));
5392 SetPixelInfoPixel(tint_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005393 p+=GetPixelChannels(image);
5394 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005395 }
5396 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5397 status=MagickFalse;
5398 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5399 {
5400 MagickBooleanType
5401 proceed;
5402
cristyb5d5f722009-11-04 03:03:49 +00005403#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005404 #pragma omp critical (MagickCore_TintImage)
5405#endif
5406 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5407 if (proceed == MagickFalse)
5408 status=MagickFalse;
5409 }
5410 }
5411 tint_view=DestroyCacheView(tint_view);
5412 image_view=DestroyCacheView(image_view);
5413 if (status == MagickFalse)
5414 tint_image=DestroyImage(tint_image);
5415 return(tint_image);
5416}
5417
5418/*
5419%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5420% %
5421% %
5422% %
5423% V i g n e t t e I m a g e %
5424% %
5425% %
5426% %
5427%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5428%
5429% VignetteImage() softens the edges of the image in vignette style.
5430%
5431% The format of the VignetteImage method is:
5432%
5433% Image *VignetteImage(const Image *image,const double radius,
cristyeb6e6582011-12-09 09:14:23 +00005434% const double sigma,const double bias,const ssize_t x,const ssize_t y,
cristy05c0c9a2011-09-05 23:16:13 +00005435% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005436%
5437% A description of each parameter follows:
5438%
5439% o image: the image.
5440%
5441% o radius: the radius of the pixel neighborhood.
5442%
5443% o sigma: the standard deviation of the Gaussian, in pixels.
5444%
cristyeb6e6582011-12-09 09:14:23 +00005445% o bias: the bias.
5446%
cristy3ed852e2009-09-05 21:47:34 +00005447% o x, y: Define the x and y ellipse offset.
5448%
5449% o exception: return any errors or warnings in this structure.
5450%
5451*/
5452MagickExport Image *VignetteImage(const Image *image,const double radius,
cristyeb6e6582011-12-09 09:14:23 +00005453 const double sigma,const double bias,const ssize_t x,const ssize_t y,
5454 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005455{
5456 char
5457 ellipse[MaxTextExtent];
5458
5459 DrawInfo
5460 *draw_info;
5461
5462 Image
5463 *canvas_image,
5464 *blur_image,
5465 *oval_image,
5466 *vignette_image;
5467
5468 assert(image != (Image *) NULL);
5469 assert(image->signature == MagickSignature);
5470 if (image->debug != MagickFalse)
5471 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5472 assert(exception != (ExceptionInfo *) NULL);
5473 assert(exception->signature == MagickSignature);
5474 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5475 if (canvas_image == (Image *) NULL)
5476 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005477 if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005478 {
cristy3ed852e2009-09-05 21:47:34 +00005479 canvas_image=DestroyImage(canvas_image);
5480 return((Image *) NULL);
5481 }
5482 canvas_image->matte=MagickTrue;
cristy98621462011-12-31 22:31:11 +00005483 oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
5484 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005485 if (oval_image == (Image *) NULL)
5486 {
5487 canvas_image=DestroyImage(canvas_image);
5488 return((Image *) NULL);
5489 }
cristy9950d572011-10-01 18:22:35 +00005490 (void) QueryColorCompliance("#000000",AllCompliance,
5491 &oval_image->background_color,exception);
cristyea1a8aa2011-10-20 13:24:06 +00005492 (void) SetImageBackgroundColor(oval_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005493 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00005494 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5495 exception);
5496 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5497 exception);
cristy1707c6c2012-01-18 23:30:54 +00005498 (void) FormatLocaleString(ellipse,MaxTextExtent,"ellipse %g,%g,%g,%g,"
5499 "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5500 image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005501 draw_info->primitive=AcquireString(ellipse);
cristy018f07f2011-09-04 21:15:19 +00005502 (void) DrawImage(oval_image,draw_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005503 draw_info=DestroyDrawInfo(draw_info);
cristyeb6e6582011-12-09 09:14:23 +00005504 blur_image=BlurImage(oval_image,radius,sigma,bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00005505 oval_image=DestroyImage(oval_image);
5506 if (blur_image == (Image *) NULL)
5507 {
5508 canvas_image=DestroyImage(canvas_image);
5509 return((Image *) NULL);
5510 }
5511 blur_image->matte=MagickFalse;
cristy98621462011-12-31 22:31:11 +00005512 (void) CompositeImage(canvas_image,IntensityCompositeOp,blur_image,0,0,
cristye941a752011-10-15 01:52:48 +00005513 exception);
cristy3ed852e2009-09-05 21:47:34 +00005514 blur_image=DestroyImage(blur_image);
5515 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5516 canvas_image=DestroyImage(canvas_image);
5517 return(vignette_image);
5518}
5519
5520/*
5521%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5522% %
5523% %
5524% %
5525% W a v e I m a g e %
5526% %
5527% %
5528% %
5529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5530%
5531% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005532% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005533% by the given parameters.
5534%
5535% The format of the WaveImage method is:
5536%
5537% Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005538% const double wave_length,const PixelInterpolateMethod method,
5539% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005540%
5541% A description of each parameter follows:
5542%
5543% o image: the image.
5544%
5545% o amplitude, wave_length: Define the amplitude and wave length of the
5546% sine wave.
5547%
cristy5c4e2582011-09-11 19:21:03 +00005548% o interpolate: the pixel interpolation method.
5549%
cristy3ed852e2009-09-05 21:47:34 +00005550% o exception: return any errors or warnings in this structure.
5551%
5552*/
5553MagickExport Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005554 const double wave_length,const PixelInterpolateMethod method,
5555 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005556{
5557#define WaveImageTag "Wave/Image"
5558
cristyfa112112010-01-04 17:48:07 +00005559 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005560 *image_view,
cristyfa112112010-01-04 17:48:07 +00005561 *wave_view;
5562
cristy3ed852e2009-09-05 21:47:34 +00005563 Image
5564 *wave_image;
5565
cristy3ed852e2009-09-05 21:47:34 +00005566 MagickBooleanType
5567 status;
5568
cristybb503372010-05-27 20:51:26 +00005569 MagickOffsetType
5570 progress;
5571
cristy3ed852e2009-09-05 21:47:34 +00005572 MagickRealType
5573 *sine_map;
5574
cristybb503372010-05-27 20:51:26 +00005575 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005576 i;
5577
cristybb503372010-05-27 20:51:26 +00005578 ssize_t
5579 y;
5580
cristy3ed852e2009-09-05 21:47:34 +00005581 /*
5582 Initialize wave image attributes.
5583 */
5584 assert(image != (Image *) NULL);
5585 assert(image->signature == MagickSignature);
5586 if (image->debug != MagickFalse)
5587 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5588 assert(exception != (ExceptionInfo *) NULL);
5589 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005590 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005591 fabs(amplitude)),MagickTrue,exception);
5592 if (wave_image == (Image *) NULL)
5593 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005594 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005595 {
cristy3ed852e2009-09-05 21:47:34 +00005596 wave_image=DestroyImage(wave_image);
5597 return((Image *) NULL);
5598 }
cristy4c08aed2011-07-01 19:47:50 +00005599 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005600 wave_image->matte=MagickTrue;
5601 /*
5602 Allocate sine map.
5603 */
5604 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5605 sizeof(*sine_map));
5606 if (sine_map == (MagickRealType *) NULL)
5607 {
5608 wave_image=DestroyImage(wave_image);
5609 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5610 }
cristybb503372010-05-27 20:51:26 +00005611 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005612 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5613 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005614 /*
5615 Wave image.
5616 */
5617 status=MagickTrue;
5618 progress=0;
cristyd76c51e2011-03-26 00:21:26 +00005619 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00005620 wave_view=AcquireCacheView(wave_image);
cristyd76c51e2011-03-26 00:21:26 +00005621 (void) SetCacheViewVirtualPixelMethod(image_view,
5622 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005623#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005624 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005625#endif
cristybb503372010-05-27 20:51:26 +00005626 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005627 {
cristy4c08aed2011-07-01 19:47:50 +00005628 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005629 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005630
cristye97bb922011-04-03 01:36:52 +00005631 register ssize_t
5632 x;
5633
cristy3ed852e2009-09-05 21:47:34 +00005634 if (status == MagickFalse)
5635 continue;
5636 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5637 exception);
cristyacd2ed22011-08-30 01:44:23 +00005638 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005639 {
5640 status=MagickFalse;
5641 continue;
5642 }
cristybb503372010-05-27 20:51:26 +00005643 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005644 {
cristy5c4e2582011-09-11 19:21:03 +00005645 status=InterpolatePixelChannels(image,image_view,wave_image,method,
5646 (double) x,(double) (y-sine_map[x]),q,exception);
cristyed231572011-07-14 02:18:59 +00005647 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005648 }
5649 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5650 status=MagickFalse;
5651 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5652 {
5653 MagickBooleanType
5654 proceed;
5655
cristyb5d5f722009-11-04 03:03:49 +00005656#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005657 #pragma omp critical (MagickCore_WaveImage)
5658#endif
5659 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5660 if (proceed == MagickFalse)
5661 status=MagickFalse;
5662 }
5663 }
5664 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005665 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005666 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5667 if (status == MagickFalse)
5668 wave_image=DestroyImage(wave_image);
5669 return(wave_image);
5670}