blob: 6cd18616692dfdedecd53b28090142bb671e691a [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"
cristy9b7a4fc2012-04-08 22:26:56 +000051#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/composite.h"
53#include "MagickCore/decorate.h"
cristyc53413d2011-11-17 13:04:26 +000054#include "MagickCore/distort.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/draw.h"
56#include "MagickCore/effect.h"
57#include "MagickCore/enhance.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/fx.h"
61#include "MagickCore/fx-private.h"
62#include "MagickCore/gem.h"
cristy8ea81222011-09-04 10:33:32 +000063#include "MagickCore/gem-private.h"
cristy4c08aed2011-07-01 19:47:50 +000064#include "MagickCore/geometry.h"
65#include "MagickCore/layer.h"
66#include "MagickCore/list.h"
67#include "MagickCore/log.h"
68#include "MagickCore/image.h"
69#include "MagickCore/image-private.h"
70#include "MagickCore/magick.h"
71#include "MagickCore/memory_.h"
72#include "MagickCore/monitor.h"
73#include "MagickCore/monitor-private.h"
74#include "MagickCore/option.h"
75#include "MagickCore/pixel.h"
76#include "MagickCore/pixel-accessor.h"
77#include "MagickCore/property.h"
78#include "MagickCore/quantum.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/random_.h"
81#include "MagickCore/random-private.h"
82#include "MagickCore/resample.h"
83#include "MagickCore/resample-private.h"
84#include "MagickCore/resize.h"
cristy4c08aed2011-07-01 19:47:50 +000085#include "MagickCore/splay-tree.h"
86#include "MagickCore/statistic.h"
87#include "MagickCore/string_.h"
88#include "MagickCore/string-private.h"
89#include "MagickCore/thread-private.h"
90#include "MagickCore/transform.h"
91#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000092
93/*
94 Define declarations.
95*/
96#define LeftShiftOperator 0xf5
97#define RightShiftOperator 0xf6
98#define LessThanEqualOperator 0xf7
99#define GreaterThanEqualOperator 0xf8
100#define EqualOperator 0xf9
101#define NotEqualOperator 0xfa
102#define LogicalAndOperator 0xfb
103#define LogicalOrOperator 0xfc
cristy116af162010-08-13 01:25:47 +0000104#define ExponentialNotation 0xfd
cristy3ed852e2009-09-05 21:47:34 +0000105
106struct _FxInfo
107{
108 const Image
109 *images;
110
cristy3ed852e2009-09-05 21:47:34 +0000111 char
112 *expression;
113
114 FILE
115 *file;
116
117 SplayTreeInfo
118 *colors,
119 *symbols;
120
cristyd76c51e2011-03-26 00:21:26 +0000121 CacheView
122 **view;
cristy3ed852e2009-09-05 21:47:34 +0000123
124 RandomInfo
125 *random_info;
126
127 ExceptionInfo
128 *exception;
129};
130
131/*
132%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
133% %
134% %
135% %
136+ A c q u i r e F x I n f o %
137% %
138% %
139% %
140%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141%
142% AcquireFxInfo() allocates the FxInfo structure.
143%
144% The format of the AcquireFxInfo method is:
145%
cristydb070952012-04-20 14:33:00 +0000146% FxInfo *AcquireFxInfo(Image *image,const char *expression,
147% ExceptionInfo *exception)
cristy0a9b3722010-10-23 18:45:49 +0000148%
cristy3ed852e2009-09-05 21:47:34 +0000149% A description of each parameter follows:
150%
151% o image: the image.
152%
153% o expression: the expression.
154%
cristydb070952012-04-20 14:33:00 +0000155% o exception: return any errors or warnings in this structure.
156%
cristy3ed852e2009-09-05 21:47:34 +0000157*/
cristydb070952012-04-20 14:33:00 +0000158MagickPrivate FxInfo *AcquireFxInfo(const Image *image,const char *expression,
159 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000160{
161 char
162 fx_op[2];
163
cristycb180922011-03-11 14:41:24 +0000164 const Image
165 *next;
166
cristy3ed852e2009-09-05 21:47:34 +0000167 FxInfo
168 *fx_info;
169
cristybb503372010-05-27 20:51:26 +0000170 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000171 i;
172
cristy73bd4a52010-10-05 11:24:23 +0000173 fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +0000174 if (fx_info == (FxInfo *) NULL)
175 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
176 (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
177 fx_info->exception=AcquireExceptionInfo();
anthony7d86e172011-03-23 12:37:06 +0000178 fx_info->images=image;
cristy3ed852e2009-09-05 21:47:34 +0000179 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
180 RelinquishMagickMemory);
181 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
182 RelinquishMagickMemory);
cristyd76c51e2011-03-26 00:21:26 +0000183 fx_info->view=(CacheView **) AcquireQuantumMemory(GetImageListLength(
184 fx_info->images),sizeof(*fx_info->view));
185 if (fx_info->view == (CacheView **) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000186 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristya2262262011-03-11 02:50:37 +0000187 i=0;
cristy0ea377f2011-03-24 00:54:19 +0000188 next=GetFirstImageInList(fx_info->images);
189 for ( ; next != (Image *) NULL; next=next->next)
cristy3ed852e2009-09-05 21:47:34 +0000190 {
cristydb070952012-04-20 14:33:00 +0000191 fx_info->view[i]=AcquireVirtualCacheView(next,exception);
cristya2262262011-03-11 02:50:37 +0000192 i++;
cristy3ed852e2009-09-05 21:47:34 +0000193 }
194 fx_info->random_info=AcquireRandomInfo();
195 fx_info->expression=ConstantString(expression);
196 fx_info->file=stderr;
197 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
cristy37af0912011-05-23 16:09:42 +0000198 /*
199 Force right-to-left associativity for unary negation.
200 */
201 (void) SubstituteString(&fx_info->expression,"-","-1.0*");
cristy8b8a3ae2010-10-23 18:49:46 +0000202 /*
cristy3ed852e2009-09-05 21:47:34 +0000203 Convert complex to simple operators.
204 */
205 fx_op[1]='\0';
206 *fx_op=(char) LeftShiftOperator;
207 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
208 *fx_op=(char) RightShiftOperator;
209 (void) SubstituteString(&fx_info->expression,">>",fx_op);
210 *fx_op=(char) LessThanEqualOperator;
211 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
212 *fx_op=(char) GreaterThanEqualOperator;
213 (void) SubstituteString(&fx_info->expression,">=",fx_op);
214 *fx_op=(char) EqualOperator;
215 (void) SubstituteString(&fx_info->expression,"==",fx_op);
216 *fx_op=(char) NotEqualOperator;
217 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
218 *fx_op=(char) LogicalAndOperator;
219 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
220 *fx_op=(char) LogicalOrOperator;
221 (void) SubstituteString(&fx_info->expression,"||",fx_op);
cristy116af162010-08-13 01:25:47 +0000222 *fx_op=(char) ExponentialNotation;
223 (void) SubstituteString(&fx_info->expression,"**",fx_op);
cristy3ed852e2009-09-05 21:47:34 +0000224 return(fx_info);
225}
226
227/*
228%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
229% %
230% %
231% %
232% A d d N o i s e I m a g e %
233% %
234% %
235% %
236%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
237%
238% AddNoiseImage() adds random noise to the image.
239%
240% The format of the AddNoiseImage method is:
241%
242% Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
cristy9ed1f812011-10-08 02:00:08 +0000243% const double attenuate,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000244%
245% A description of each parameter follows:
246%
247% o image: the image.
248%
249% o channel: the channel type.
250%
251% o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
252% Impulse, Laplacian, or Poisson.
253%
cristy9ed1f812011-10-08 02:00:08 +0000254% o attenuate: attenuate the random distribution.
255%
cristy3ed852e2009-09-05 21:47:34 +0000256% o exception: return any errors or warnings in this structure.
257%
258*/
cristy9ed1f812011-10-08 02:00:08 +0000259MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
260 const double attenuate,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000261{
262#define AddNoiseImageTag "AddNoise/Image"
263
cristyfa112112010-01-04 17:48:07 +0000264 CacheView
265 *image_view,
266 *noise_view;
267
cristy3ed852e2009-09-05 21:47:34 +0000268 Image
269 *noise_image;
270
cristy3ed852e2009-09-05 21:47:34 +0000271 MagickBooleanType
cristye7452652012-04-14 01:34:21 +0000272 concurrent,
cristy3ed852e2009-09-05 21:47:34 +0000273 status;
274
cristybb503372010-05-27 20:51:26 +0000275 MagickOffsetType
276 progress;
277
cristy3ed852e2009-09-05 21:47:34 +0000278 RandomInfo
cristyfa112112010-01-04 17:48:07 +0000279 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +0000280
cristybb503372010-05-27 20:51:26 +0000281 ssize_t
282 y;
283
cristy3ed852e2009-09-05 21:47:34 +0000284 /*
285 Initialize noise image attributes.
286 */
287 assert(image != (const Image *) NULL);
288 assert(image->signature == MagickSignature);
289 if (image->debug != MagickFalse)
290 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
291 assert(exception != (ExceptionInfo *) NULL);
292 assert(exception->signature == MagickSignature);
cristyb2145892011-10-10 00:55:32 +0000293 noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000294 if (noise_image == (Image *) NULL)
295 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000296 if (SetImageStorageClass(noise_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000297 {
cristy3ed852e2009-09-05 21:47:34 +0000298 noise_image=DestroyImage(noise_image);
299 return((Image *) NULL);
300 }
301 /*
302 Add noise in each row.
303 */
cristy3ed852e2009-09-05 21:47:34 +0000304 status=MagickTrue;
305 progress=0;
306 random_info=AcquireRandomInfoThreadSet();
cristye7452652012-04-14 01:34:21 +0000307 concurrent=GetRandomSecretKey(random_info[0]) == ~0UL ? MagickTrue :
308 MagickFalse;
cristydb070952012-04-20 14:33:00 +0000309 image_view=AcquireVirtualCacheView(image,exception);
310 noise_view=AcquireAuthenticCacheView(noise_image,exception);
cristy319a1e72010-02-21 15:13:11 +0000311#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye7452652012-04-14 01:34:21 +0000312 #pragma omp parallel for schedule(static,4) shared(progress,status) omp_concurrent(concurrent)
cristy3ed852e2009-09-05 21:47:34 +0000313#endif
cristybb503372010-05-27 20:51:26 +0000314 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000315 {
cristy5c9e6f22010-09-17 17:31:01 +0000316 const int
317 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000318
cristy3ed852e2009-09-05 21:47:34 +0000319 MagickBooleanType
320 sync;
321
cristy4c08aed2011-07-01 19:47:50 +0000322 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000323 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000324
cristybb503372010-05-27 20:51:26 +0000325 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000326 x;
327
cristy4c08aed2011-07-01 19:47:50 +0000328 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000329 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000330
331 if (status == MagickFalse)
332 continue;
333 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +0000334 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +0000335 exception);
cristy4c08aed2011-07-01 19:47:50 +0000336 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000337 {
338 status=MagickFalse;
339 continue;
340 }
cristybb503372010-05-27 20:51:26 +0000341 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000342 {
cristy850b3072011-10-08 01:38:05 +0000343 register ssize_t
344 i;
345
cristy177e41c2012-04-15 15:08:25 +0000346 if (GetPixelMask(image,p) != 0)
347 {
348 p+=GetPixelChannels(image);
349 q+=GetPixelChannels(noise_image);
350 continue;
351 }
cristy850b3072011-10-08 01:38:05 +0000352 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
353 {
354 PixelChannel
355 channel;
356
357 PixelTrait
358 noise_traits,
359 traits;
360
cristye2a912b2011-12-05 20:02:07 +0000361 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000362 traits=GetPixelChannelMapTraits(image,channel);
cristy850b3072011-10-08 01:38:05 +0000363 noise_traits=GetPixelChannelMapTraits(noise_image,channel);
364 if ((traits == UndefinedPixelTrait) ||
365 (noise_traits == UndefinedPixelTrait))
366 continue;
cristy177e41c2012-04-15 15:08:25 +0000367 if ((noise_traits & CopyPixelTrait) != 0)
cristyb2145892011-10-10 00:55:32 +0000368 {
369 SetPixelChannel(noise_image,channel,p[i],q);
370 continue;
371 }
cristy850b3072011-10-08 01:38:05 +0000372 SetPixelChannel(noise_image,channel,ClampToQuantum(
373 GenerateDifferentialNoise(random_info[id],p[i],noise_type,attenuate)),
374 q);
375 }
cristyed231572011-07-14 02:18:59 +0000376 p+=GetPixelChannels(image);
377 q+=GetPixelChannels(noise_image);
cristy3ed852e2009-09-05 21:47:34 +0000378 }
379 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
380 if (sync == MagickFalse)
381 status=MagickFalse;
382 if (image->progress_monitor != (MagickProgressMonitor) NULL)
383 {
384 MagickBooleanType
385 proceed;
386
cristyb5d5f722009-11-04 03:03:49 +0000387#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy319a1e72010-02-21 15:13:11 +0000388 #pragma omp critical (MagickCore_AddNoiseImage)
cristy3ed852e2009-09-05 21:47:34 +0000389#endif
390 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
391 image->rows);
392 if (proceed == MagickFalse)
393 status=MagickFalse;
394 }
395 }
396 noise_view=DestroyCacheView(noise_view);
397 image_view=DestroyCacheView(image_view);
398 random_info=DestroyRandomInfoThreadSet(random_info);
399 if (status == MagickFalse)
400 noise_image=DestroyImage(noise_image);
401 return(noise_image);
402}
403
404/*
405%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406% %
407% %
408% %
409% B l u e S h i f t I m a g e %
410% %
411% %
412% %
413%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
414%
415% BlueShiftImage() mutes the colors of the image to simulate a scene at
416% nighttime in the moonlight.
417%
418% The format of the BlueShiftImage method is:
419%
420% Image *BlueShiftImage(const Image *image,const double factor,
421% ExceptionInfo *exception)
422%
423% A description of each parameter follows:
424%
425% o image: the image.
426%
427% o factor: the shift factor.
428%
429% o exception: return any errors or warnings in this structure.
430%
431*/
432MagickExport Image *BlueShiftImage(const Image *image,const double factor,
433 ExceptionInfo *exception)
434{
435#define BlueShiftImageTag "BlueShift/Image"
436
cristyc4c8d132010-01-07 01:58:38 +0000437 CacheView
438 *image_view,
439 *shift_view;
440
cristy3ed852e2009-09-05 21:47:34 +0000441 Image
442 *shift_image;
443
cristy3ed852e2009-09-05 21:47:34 +0000444 MagickBooleanType
445 status;
446
cristybb503372010-05-27 20:51:26 +0000447 MagickOffsetType
448 progress;
449
450 ssize_t
451 y;
452
cristy3ed852e2009-09-05 21:47:34 +0000453 /*
454 Allocate blue shift image.
455 */
456 assert(image != (const Image *) NULL);
457 assert(image->signature == MagickSignature);
458 if (image->debug != MagickFalse)
459 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
460 assert(exception != (ExceptionInfo *) NULL);
461 assert(exception->signature == MagickSignature);
cristya6d7a9b2012-01-18 20:04:48 +0000462 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000463 if (shift_image == (Image *) NULL)
464 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000465 if (SetImageStorageClass(shift_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000466 {
cristy3ed852e2009-09-05 21:47:34 +0000467 shift_image=DestroyImage(shift_image);
468 return((Image *) NULL);
469 }
470 /*
471 Blue-shift DirectClass image.
472 */
473 status=MagickTrue;
474 progress=0;
cristydb070952012-04-20 14:33:00 +0000475 image_view=AcquireVirtualCacheView(image,exception);
476 shift_view=AcquireAuthenticCacheView(shift_image,exception);
cristy319a1e72010-02-21 15:13:11 +0000477#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000478 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000479#endif
cristybb503372010-05-27 20:51:26 +0000480 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000481 {
482 MagickBooleanType
483 sync;
484
cristy4c08aed2011-07-01 19:47:50 +0000485 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000486 pixel;
487
488 Quantum
489 quantum;
490
cristy4c08aed2011-07-01 19:47:50 +0000491 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000492 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000493
cristybb503372010-05-27 20:51:26 +0000494 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000495 x;
496
cristy4c08aed2011-07-01 19:47:50 +0000497 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000498 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000499
500 if (status == MagickFalse)
501 continue;
502 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
503 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
504 exception);
cristy4c08aed2011-07-01 19:47:50 +0000505 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000506 {
507 status=MagickFalse;
508 continue;
509 }
cristybb503372010-05-27 20:51:26 +0000510 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000511 {
cristy4c08aed2011-07-01 19:47:50 +0000512 quantum=GetPixelRed(image,p);
513 if (GetPixelGreen(image,p) < quantum)
514 quantum=GetPixelGreen(image,p);
515 if (GetPixelBlue(image,p) < quantum)
516 quantum=GetPixelBlue(image,p);
517 pixel.red=0.5*(GetPixelRed(image,p)+factor*quantum);
518 pixel.green=0.5*(GetPixelGreen(image,p)+factor*quantum);
519 pixel.blue=0.5*(GetPixelBlue(image,p)+factor*quantum);
520 quantum=GetPixelRed(image,p);
521 if (GetPixelGreen(image,p) > quantum)
522 quantum=GetPixelGreen(image,p);
523 if (GetPixelBlue(image,p) > quantum)
524 quantum=GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000525 pixel.red=0.5*(pixel.red+factor*quantum);
526 pixel.green=0.5*(pixel.green+factor*quantum);
527 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristy4c08aed2011-07-01 19:47:50 +0000528 SetPixelRed(shift_image,ClampToQuantum(pixel.red),q);
529 SetPixelGreen(shift_image,ClampToQuantum(pixel.green),q);
530 SetPixelBlue(shift_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000531 p+=GetPixelChannels(image);
532 q+=GetPixelChannels(shift_image);
cristy3ed852e2009-09-05 21:47:34 +0000533 }
534 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
535 if (sync == MagickFalse)
536 status=MagickFalse;
537 if (image->progress_monitor != (MagickProgressMonitor) NULL)
538 {
539 MagickBooleanType
540 proceed;
541
cristy319a1e72010-02-21 15:13:11 +0000542#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000543 #pragma omp critical (MagickCore_BlueShiftImage)
cristy3ed852e2009-09-05 21:47:34 +0000544#endif
545 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
546 image->rows);
547 if (proceed == MagickFalse)
548 status=MagickFalse;
549 }
550 }
551 image_view=DestroyCacheView(image_view);
552 shift_view=DestroyCacheView(shift_view);
553 if (status == MagickFalse)
554 shift_image=DestroyImage(shift_image);
555 return(shift_image);
556}
557
558/*
559%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
560% %
561% %
562% %
563% C h a r c o a l I m a g e %
564% %
565% %
566% %
567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
568%
569% CharcoalImage() creates a new image that is a copy of an existing one with
570% the edge highlighted. It allocates the memory necessary for the new Image
571% structure and returns a pointer to the new image.
572%
573% The format of the CharcoalImage method is:
574%
575% Image *CharcoalImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +0000576% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000577%
578% A description of each parameter follows:
579%
580% o image: the image.
581%
582% o radius: the radius of the pixel neighborhood.
583%
584% o sigma: the standard deviation of the Gaussian, in pixels.
585%
586% o exception: return any errors or warnings in this structure.
587%
588*/
589MagickExport Image *CharcoalImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +0000590 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000591{
592 Image
593 *charcoal_image,
594 *clone_image,
595 *edge_image;
596
597 assert(image != (Image *) NULL);
598 assert(image->signature == MagickSignature);
599 if (image->debug != MagickFalse)
600 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
601 assert(exception != (ExceptionInfo *) NULL);
602 assert(exception->signature == MagickSignature);
603 clone_image=CloneImage(image,0,0,MagickTrue,exception);
604 if (clone_image == (Image *) NULL)
605 return((Image *) NULL);
cristy018f07f2011-09-04 21:15:19 +0000606 (void) SetImageType(clone_image,GrayscaleType,exception);
cristy8ae632d2011-09-05 17:29:53 +0000607 edge_image=EdgeImage(clone_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000608 clone_image=DestroyImage(clone_image);
609 if (edge_image == (Image *) NULL)
610 return((Image *) NULL);
cristyaa2c16c2012-03-25 22:21:35 +0000611 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000612 edge_image=DestroyImage(edge_image);
613 if (charcoal_image == (Image *) NULL)
614 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +0000615 (void) NormalizeImage(charcoal_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +0000616 (void) NegateImage(charcoal_image,MagickFalse,exception);
cristy018f07f2011-09-04 21:15:19 +0000617 (void) SetImageType(charcoal_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000618 return(charcoal_image);
619}
620
621/*
622%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
623% %
624% %
625% %
626% C o l o r i z e I m a g e %
627% %
628% %
629% %
630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
631%
632% ColorizeImage() blends the fill color with each pixel in the image.
633% A percentage blend is specified with opacity. Control the application
634% of different color components by specifying a different percentage for
635% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
636%
637% The format of the ColorizeImage method is:
638%
cristyc7e6ff62011-10-03 13:46:11 +0000639% Image *ColorizeImage(const Image *image,const char *blend,
640% const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000641%
642% A description of each parameter follows:
643%
644% o image: the image.
645%
cristyc7e6ff62011-10-03 13:46:11 +0000646% o blend: A character string indicating the level of blending as a
cristy3ed852e2009-09-05 21:47:34 +0000647% percentage.
648%
649% o colorize: A color value.
650%
651% o exception: return any errors or warnings in this structure.
652%
653*/
cristyc7e6ff62011-10-03 13:46:11 +0000654MagickExport Image *ColorizeImage(const Image *image,const char *blend,
655 const PixelInfo *colorize,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000656{
657#define ColorizeImageTag "Colorize/Image"
cristy20c3aed2012-04-10 01:06:21 +0000658#define Colorize(pixel,blend_percentage,colorize) \
659 (pixel)=((pixel)*(100.0-(blend_percentage))+(colorize)*(blend_percentage))/100.0;
cristy3ed852e2009-09-05 21:47:34 +0000660
cristyc4c8d132010-01-07 01:58:38 +0000661 CacheView
662 *colorize_view,
663 *image_view;
664
cristy3ed852e2009-09-05 21:47:34 +0000665 GeometryInfo
666 geometry_info;
667
668 Image
669 *colorize_image;
670
cristy3ed852e2009-09-05 21:47:34 +0000671 MagickBooleanType
672 status;
673
cristybb503372010-05-27 20:51:26 +0000674 MagickOffsetType
675 progress;
676
cristy3ed852e2009-09-05 21:47:34 +0000677 MagickStatusType
678 flags;
679
cristyc7e6ff62011-10-03 13:46:11 +0000680 PixelInfo
cristy20c3aed2012-04-10 01:06:21 +0000681 blend_percentage;
cristyc7e6ff62011-10-03 13:46:11 +0000682
cristybb503372010-05-27 20:51:26 +0000683 ssize_t
684 y;
685
cristy3ed852e2009-09-05 21:47:34 +0000686 /*
687 Allocate colorized image.
688 */
689 assert(image != (const Image *) NULL);
690 assert(image->signature == MagickSignature);
691 if (image->debug != MagickFalse)
692 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
693 assert(exception != (ExceptionInfo *) NULL);
694 assert(exception->signature == MagickSignature);
cristy768165d2012-04-09 15:01:35 +0000695 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
696 exception);
697 if (colorize_image == (Image *) NULL)
698 return((Image *) NULL);
699 if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
700 {
701 colorize_image=DestroyImage(colorize_image);
702 return((Image *) NULL);
703 }
cristy9b7a4fc2012-04-08 22:26:56 +0000704 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
cristy20c3aed2012-04-10 01:06:21 +0000705 (IsPixelInfoGray(colorize) != MagickFalse))
cristy52f2e1e2012-04-10 00:51:19 +0000706 (void) SetImageColorspace(colorize_image,sRGBColorspace,exception);
cristy20c3aed2012-04-10 01:06:21 +0000707 if ((colorize_image->matte == MagickFalse) &&
708 (colorize->matte != MagickFalse))
cristy768165d2012-04-09 15:01:35 +0000709 (void) SetImageAlpha(colorize_image,OpaqueAlpha,exception);
710 if (blend == (const char *) NULL)
711 return(colorize_image);
cristy20c3aed2012-04-10 01:06:21 +0000712 GetPixelInfo(image,&blend_percentage);
713 flags=ParseGeometry(blend,&geometry_info);
714 blend_percentage.red=geometry_info.rho;
715 blend_percentage.green=geometry_info.rho;
716 blend_percentage.blue=geometry_info.rho;
717 blend_percentage.black=geometry_info.rho;
cristy3ee7de52012-04-14 23:40:47 +0000718 blend_percentage.alpha=geometry_info.rho;
cristy20c3aed2012-04-10 01:06:21 +0000719 if ((flags & SigmaValue) != 0)
720 blend_percentage.green=geometry_info.sigma;
721 if ((flags & XiValue) != 0)
722 blend_percentage.blue=geometry_info.xi;
723 if ((flags & PsiValue) != 0)
724 blend_percentage.alpha=geometry_info.psi;
725 if (blend_percentage.colorspace == CMYKColorspace)
726 {
727 if ((flags & PsiValue) != 0)
728 blend_percentage.black=geometry_info.psi;
729 if ((flags & ChiValue) != 0)
730 blend_percentage.alpha=geometry_info.chi;
731 }
cristy3ed852e2009-09-05 21:47:34 +0000732 /*
733 Colorize DirectClass image.
734 */
735 status=MagickTrue;
736 progress=0;
cristydb070952012-04-20 14:33:00 +0000737 image_view=AcquireVirtualCacheView(image,exception);
738 colorize_view=AcquireAuthenticCacheView(colorize_image,exception);
cristy319a1e72010-02-21 15:13:11 +0000739#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000740 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000741#endif
cristybb503372010-05-27 20:51:26 +0000742 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000743 {
744 MagickBooleanType
745 sync;
746
cristyf9bf43e2012-04-07 18:13:07 +0000747 PixelInfo
748 pixel;
749
cristy4c08aed2011-07-01 19:47:50 +0000750 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000751 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000752
cristybb503372010-05-27 20:51:26 +0000753 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000754 x;
755
cristy4c08aed2011-07-01 19:47:50 +0000756 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000757 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000758
759 if (status == MagickFalse)
760 continue;
761 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
762 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
763 exception);
cristy4c08aed2011-07-01 19:47:50 +0000764 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000765 {
766 status=MagickFalse;
767 continue;
768 }
cristy66d3e1f2012-04-14 23:21:39 +0000769 GetPixelInfo(colorize_image,&pixel);
cristybb503372010-05-27 20:51:26 +0000770 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000771 {
cristyd6803382012-04-10 01:41:25 +0000772 if (GetPixelMask(colorize_image,q) != 0)
773 {
774 p+=GetPixelChannels(image);
775 q+=GetPixelChannels(colorize_image);
776 continue;
777 }
cristyf9bf43e2012-04-07 18:13:07 +0000778 GetPixelInfoPixel(image,p,&pixel);
cristy20c3aed2012-04-10 01:06:21 +0000779 Colorize(pixel.red,blend_percentage.red,colorize->red);
780 Colorize(pixel.green,blend_percentage.green,colorize->green);
781 Colorize(pixel.blue,blend_percentage.blue,colorize->blue);
782 Colorize(pixel.black,blend_percentage.black,colorize->black);
783 Colorize(pixel.alpha,blend_percentage.alpha,colorize->alpha);
cristyf9bf43e2012-04-07 18:13:07 +0000784 SetPixelInfoPixel(colorize_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000785 p+=GetPixelChannels(image);
786 q+=GetPixelChannels(colorize_image);
cristy3ed852e2009-09-05 21:47:34 +0000787 }
788 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
789 if (sync == MagickFalse)
790 status=MagickFalse;
791 if (image->progress_monitor != (MagickProgressMonitor) NULL)
792 {
793 MagickBooleanType
794 proceed;
795
cristy319a1e72010-02-21 15:13:11 +0000796#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +0000797 #pragma omp critical (MagickCore_ColorizeImage)
cristy3ed852e2009-09-05 21:47:34 +0000798#endif
799 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
800 if (proceed == MagickFalse)
801 status=MagickFalse;
802 }
803 }
804 image_view=DestroyCacheView(image_view);
805 colorize_view=DestroyCacheView(colorize_view);
806 if (status == MagickFalse)
807 colorize_image=DestroyImage(colorize_image);
808 return(colorize_image);
809}
810
811/*
812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813% %
814% %
815% %
cristye6365592010-04-02 17:31:23 +0000816% C o l o r M a t r i x I m a g e %
817% %
818% %
819% %
820%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821%
822% ColorMatrixImage() applies color transformation to an image. This method
823% permits saturation changes, hue rotation, luminance to alpha, and various
824% other effects. Although variable-sized transformation matrices can be used,
825% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
826% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
827% except offsets are in column 6 rather than 5 (in support of CMYKA images)
828% and offsets are normalized (divide Flash offset by 255).
829%
830% The format of the ColorMatrixImage method is:
831%
832% Image *ColorMatrixImage(const Image *image,
833% const KernelInfo *color_matrix,ExceptionInfo *exception)
834%
835% A description of each parameter follows:
836%
837% o image: the image.
838%
839% o color_matrix: the color matrix.
840%
841% o exception: return any errors or warnings in this structure.
842%
843*/
anthonyfd706f92012-01-19 04:22:02 +0000844/* FUTURE: modify to make use of a MagickMatrix Mutliply function
845 That should be provided in "matrix.c"
846 (ASIDE: actually distorts should do this too but currently doesn't)
847*/
848
cristye6365592010-04-02 17:31:23 +0000849MagickExport Image *ColorMatrixImage(const Image *image,
850 const KernelInfo *color_matrix,ExceptionInfo *exception)
851{
852#define ColorMatrixImageTag "ColorMatrix/Image"
853
854 CacheView
855 *color_view,
856 *image_view;
857
858 double
859 ColorMatrix[6][6] =
860 {
861 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
862 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
863 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
864 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
865 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
866 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
867 };
868
869 Image
870 *color_image;
871
cristye6365592010-04-02 17:31:23 +0000872 MagickBooleanType
873 status;
874
cristybb503372010-05-27 20:51:26 +0000875 MagickOffsetType
876 progress;
877
878 register ssize_t
cristye6365592010-04-02 17:31:23 +0000879 i;
880
cristybb503372010-05-27 20:51:26 +0000881 ssize_t
882 u,
883 v,
884 y;
885
cristye6365592010-04-02 17:31:23 +0000886 /*
anthonyfd706f92012-01-19 04:22:02 +0000887 Map given color_matrix, into a 6x6 matrix RGBKA and a constant
cristye6365592010-04-02 17:31:23 +0000888 */
889 assert(image != (Image *) NULL);
890 assert(image->signature == MagickSignature);
891 if (image->debug != MagickFalse)
892 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
893 assert(exception != (ExceptionInfo *) NULL);
894 assert(exception->signature == MagickSignature);
895 i=0;
cristybb503372010-05-27 20:51:26 +0000896 for (v=0; v < (ssize_t) color_matrix->height; v++)
897 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000898 {
899 if ((v < 6) && (u < 6))
900 ColorMatrix[v][u]=color_matrix->values[i];
901 i++;
902 }
903 /*
904 Initialize color image.
905 */
cristy12550e62010-06-07 12:46:40 +0000906 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000907 if (color_image == (Image *) NULL)
908 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000909 if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
cristye6365592010-04-02 17:31:23 +0000910 {
cristye6365592010-04-02 17:31:23 +0000911 color_image=DestroyImage(color_image);
912 return((Image *) NULL);
913 }
914 if (image->debug != MagickFalse)
915 {
916 char
917 format[MaxTextExtent],
918 *message;
919
920 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
921 " ColorMatrix image with color matrix:");
922 message=AcquireString("");
923 for (v=0; v < 6; v++)
924 {
925 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000926 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000927 (void) ConcatenateString(&message,format);
928 for (u=0; u < 6; u++)
929 {
cristyb51dff52011-05-19 16:55:47 +0000930 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
cristye6365592010-04-02 17:31:23 +0000931 ColorMatrix[v][u]);
932 (void) ConcatenateString(&message,format);
933 }
934 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
935 }
936 message=DestroyString(message);
937 }
938 /*
anthonyfd706f92012-01-19 04:22:02 +0000939 Apply the ColorMatrix to image.
cristye6365592010-04-02 17:31:23 +0000940 */
941 status=MagickTrue;
942 progress=0;
cristydb070952012-04-20 14:33:00 +0000943 image_view=AcquireVirtualCacheView(image,exception);
944 color_view=AcquireAuthenticCacheView(color_image,exception);
cristye6365592010-04-02 17:31:23 +0000945#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000946 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye6365592010-04-02 17:31:23 +0000947#endif
cristybb503372010-05-27 20:51:26 +0000948 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000949 {
cristyfcc25d92012-02-19 23:06:48 +0000950 PixelInfo
cristye6365592010-04-02 17:31:23 +0000951 pixel;
952
cristy4c08aed2011-07-01 19:47:50 +0000953 register const Quantum
cristye6365592010-04-02 17:31:23 +0000954 *restrict p;
955
cristy4c08aed2011-07-01 19:47:50 +0000956 register Quantum
957 *restrict q;
958
cristybb503372010-05-27 20:51:26 +0000959 register ssize_t
cristye6365592010-04-02 17:31:23 +0000960 x;
961
cristye6365592010-04-02 17:31:23 +0000962 if (status == MagickFalse)
963 continue;
964 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
965 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
966 exception);
cristy4c08aed2011-07-01 19:47:50 +0000967 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristye6365592010-04-02 17:31:23 +0000968 {
969 status=MagickFalse;
970 continue;
971 }
cristyfcc25d92012-02-19 23:06:48 +0000972 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000973 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +0000974 {
cristybb503372010-05-27 20:51:26 +0000975 register ssize_t
cristye6365592010-04-02 17:31:23 +0000976 v;
977
cristybb503372010-05-27 20:51:26 +0000978 size_t
cristye6365592010-04-02 17:31:23 +0000979 height;
980
cristyfcc25d92012-02-19 23:06:48 +0000981 GetPixelInfoPixel(image,p,&pixel);
cristye6365592010-04-02 17:31:23 +0000982 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +0000983 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +0000984 {
cristyfcc25d92012-02-19 23:06:48 +0000985 MagickRealType
986 sum;
987
988 sum=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
cristy4c08aed2011-07-01 19:47:50 +0000989 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
cristye6365592010-04-02 17:31:23 +0000990 if (image->colorspace == CMYKColorspace)
cristyfcc25d92012-02-19 23:06:48 +0000991 sum+=ColorMatrix[v][3]*GetPixelBlack(image,p);
cristy4c08aed2011-07-01 19:47:50 +0000992 if (image->matte != MagickFalse)
cristyfcc25d92012-02-19 23:06:48 +0000993 sum+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
994 sum+=QuantumRange*ColorMatrix[v][5];
cristye6365592010-04-02 17:31:23 +0000995 switch (v)
996 {
cristyfcc25d92012-02-19 23:06:48 +0000997 case 0: pixel.red=sum; break;
998 case 1: pixel.green=sum; break;
999 case 2: pixel.blue=sum; break;
1000 case 3: pixel.black=sum; break;
1001 case 4: pixel.alpha=sum; break;
1002 default: break;
cristye6365592010-04-02 17:31:23 +00001003 }
1004 }
cristyfcc25d92012-02-19 23:06:48 +00001005 SetPixelInfoPixel(color_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001006 p+=GetPixelChannels(image);
1007 q+=GetPixelChannels(color_image);
cristye6365592010-04-02 17:31:23 +00001008 }
1009 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1010 status=MagickFalse;
1011 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1012 {
1013 MagickBooleanType
1014 proceed;
1015
1016#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00001017 #pragma omp critical (MagickCore_ColorMatrixImage)
cristye6365592010-04-02 17:31:23 +00001018#endif
1019 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1020 image->rows);
1021 if (proceed == MagickFalse)
1022 status=MagickFalse;
1023 }
1024 }
1025 color_view=DestroyCacheView(color_view);
1026 image_view=DestroyCacheView(image_view);
1027 if (status == MagickFalse)
1028 color_image=DestroyImage(color_image);
1029 return(color_image);
1030}
1031
1032/*
1033%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1034% %
1035% %
1036% %
cristy3ed852e2009-09-05 21:47:34 +00001037+ D e s t r o y F x I n f o %
1038% %
1039% %
1040% %
1041%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1042%
1043% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1044%
1045% The format of the DestroyFxInfo method is:
1046%
1047% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1048%
1049% A description of each parameter follows:
1050%
1051% o fx_info: the fx info.
1052%
1053*/
cristy7832dc22011-09-05 01:21:53 +00001054MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
cristy3ed852e2009-09-05 21:47:34 +00001055{
cristybb503372010-05-27 20:51:26 +00001056 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001057 i;
1058
1059 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1060 fx_info->expression=DestroyString(fx_info->expression);
1061 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1062 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001063 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristyd76c51e2011-03-26 00:21:26 +00001064 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1065 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
cristy3ed852e2009-09-05 21:47:34 +00001066 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1067 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1068 return(fx_info);
1069}
1070
1071/*
1072%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1073% %
1074% %
1075% %
cristy3ed852e2009-09-05 21:47:34 +00001076+ 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 %
1077% %
1078% %
1079% %
1080%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1081%
1082% FxEvaluateChannelExpression() evaluates an expression and returns the
1083% results.
1084%
1085% The format of the FxEvaluateExpression method is:
1086%
1087% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00001088% const PixelChannel channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001089% MagickRealType *alpha,Exceptioninfo *exception)
1090% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1091% MagickRealType *alpha,Exceptioninfo *exception)
1092%
1093% A description of each parameter follows:
1094%
1095% o fx_info: the fx info.
1096%
1097% o channel: the channel.
1098%
1099% o x,y: the pixel position.
1100%
1101% o alpha: the result.
1102%
1103% o exception: return any errors or warnings in this structure.
1104%
1105*/
1106
cristy351842f2010-03-07 15:27:38 +00001107static inline double MagickMax(const double x,const double y)
1108{
1109 if (x > y)
1110 return(x);
1111 return(y);
1112}
1113
1114static inline double MagickMin(const double x,const double y)
1115{
1116 if (x < y)
1117 return(x);
1118 return(y);
1119}
1120
cristy3ed852e2009-09-05 21:47:34 +00001121static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
cristy0568ffc2011-07-25 16:54:14 +00001122 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001123{
1124 char
1125 key[MaxTextExtent],
1126 statistic[MaxTextExtent];
1127
1128 const char
1129 *value;
1130
1131 register const char
1132 *p;
1133
1134 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1135 if (*p == '.')
1136 switch (*++p) /* e.g. depth.r */
1137 {
cristy541ae572011-08-05 19:08:59 +00001138 case 'r': channel=RedPixelChannel; break;
1139 case 'g': channel=GreenPixelChannel; break;
1140 case 'b': channel=BluePixelChannel; break;
1141 case 'c': channel=CyanPixelChannel; break;
1142 case 'm': channel=MagentaPixelChannel; break;
1143 case 'y': channel=YellowPixelChannel; break;
1144 case 'k': channel=BlackPixelChannel; break;
cristy3ed852e2009-09-05 21:47:34 +00001145 default: break;
1146 }
cristyb51dff52011-05-19 16:55:47 +00001147 (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
cristye8c25f92010-06-03 00:53:06 +00001148 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001149 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1150 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001151 return(QuantumScale*StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001152 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1153 if (LocaleNCompare(symbol,"depth",5) == 0)
1154 {
cristybb503372010-05-27 20:51:26 +00001155 size_t
cristy3ed852e2009-09-05 21:47:34 +00001156 depth;
1157
cristyfefab1b2011-07-05 00:33:22 +00001158 depth=GetImageDepth(image,exception);
1159 (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
cristy3ed852e2009-09-05 21:47:34 +00001160 }
1161 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1162 {
1163 double
1164 kurtosis,
1165 skewness;
1166
cristyd42d9952011-07-08 14:21:50 +00001167 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001168 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001169 }
1170 if (LocaleNCompare(symbol,"maxima",6) == 0)
1171 {
1172 double
1173 maxima,
1174 minima;
1175
cristyd42d9952011-07-08 14:21:50 +00001176 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001177 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001178 }
1179 if (LocaleNCompare(symbol,"mean",4) == 0)
1180 {
1181 double
1182 mean,
1183 standard_deviation;
1184
cristyd42d9952011-07-08 14:21:50 +00001185 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001186 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001187 }
1188 if (LocaleNCompare(symbol,"minima",6) == 0)
1189 {
1190 double
1191 maxima,
1192 minima;
1193
cristyd42d9952011-07-08 14:21:50 +00001194 (void) GetImageRange(image,&minima,&maxima,exception);
cristyb51dff52011-05-19 16:55:47 +00001195 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001196 }
1197 if (LocaleNCompare(symbol,"skewness",8) == 0)
1198 {
1199 double
1200 kurtosis,
1201 skewness;
1202
cristyd42d9952011-07-08 14:21:50 +00001203 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
cristyb51dff52011-05-19 16:55:47 +00001204 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001205 }
1206 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1207 {
1208 double
1209 mean,
1210 standard_deviation;
1211
cristyd42d9952011-07-08 14:21:50 +00001212 (void) GetImageMean(image,&mean,&standard_deviation,exception);
cristyb51dff52011-05-19 16:55:47 +00001213 (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001214 standard_deviation);
1215 }
1216 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1217 ConstantString(statistic));
cristydbdd0e32011-11-04 23:29:40 +00001218 return(QuantumScale*StringToDouble(statistic,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001219}
1220
1221static MagickRealType
cristy0568ffc2011-07-25 16:54:14 +00001222 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
cristye85007d2010-06-06 22:51:36 +00001223 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001224
cristyb0aad4c2011-11-02 19:30:35 +00001225static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
1226{
1227 if (beta != 0)
1228 return(FxGCD(beta,alpha % beta));
1229 return(alpha);
1230}
1231
cristy3ed852e2009-09-05 21:47:34 +00001232static inline const char *FxSubexpression(const char *expression,
1233 ExceptionInfo *exception)
1234{
1235 const char
1236 *subexpression;
1237
cristybb503372010-05-27 20:51:26 +00001238 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001239 level;
1240
1241 level=0;
1242 subexpression=expression;
1243 while ((*subexpression != '\0') &&
1244 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1245 {
1246 if (strchr("(",(int) *subexpression) != (char *) NULL)
1247 level++;
1248 else
1249 if (strchr(")",(int) *subexpression) != (char *) NULL)
1250 level--;
1251 subexpression++;
1252 }
1253 if (*subexpression == '\0')
1254 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001255 "UnbalancedParenthesis","'%s'",expression);
cristy3ed852e2009-09-05 21:47:34 +00001256 return(subexpression);
1257}
1258
cristy0568ffc2011-07-25 16:54:14 +00001259static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
cristye85007d2010-06-06 22:51:36 +00001260 const ssize_t x,const ssize_t y,const char *expression,
1261 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001262{
1263 char
1264 *q,
1265 subexpression[MaxTextExtent],
1266 symbol[MaxTextExtent];
1267
1268 const char
1269 *p,
1270 *value;
1271
1272 Image
1273 *image;
1274
cristy4c08aed2011-07-01 19:47:50 +00001275 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001276 pixel;
1277
1278 MagickRealType
1279 alpha,
1280 beta;
1281
1282 PointInfo
1283 point;
1284
cristybb503372010-05-27 20:51:26 +00001285 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001286 i;
1287
1288 size_t
cristy1707c6c2012-01-18 23:30:54 +00001289 length,
cristy3ed852e2009-09-05 21:47:34 +00001290 level;
1291
1292 p=expression;
1293 i=GetImageIndexInList(fx_info->images);
1294 level=0;
1295 point.x=(double) x;
1296 point.y=(double) y;
1297 if (isalpha((int) *(p+1)) == 0)
1298 {
1299 if (strchr("suv",(int) *p) != (char *) NULL)
1300 {
1301 switch (*p)
1302 {
1303 case 's':
1304 default:
1305 {
1306 i=GetImageIndexInList(fx_info->images);
1307 break;
1308 }
1309 case 'u': i=0; break;
1310 case 'v': i=1; break;
1311 }
1312 p++;
1313 if (*p == '[')
1314 {
1315 level++;
1316 q=subexpression;
1317 for (p++; *p != '\0'; )
1318 {
1319 if (*p == '[')
1320 level++;
1321 else
1322 if (*p == ']')
1323 {
1324 level--;
1325 if (level == 0)
1326 break;
1327 }
1328 *q++=(*p++);
1329 }
1330 *q='\0';
1331 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1332 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001333 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001334 p++;
1335 }
1336 if (*p == '.')
1337 p++;
1338 }
1339 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1340 {
1341 p++;
1342 if (*p == '{')
1343 {
1344 level++;
1345 q=subexpression;
1346 for (p++; *p != '\0'; )
1347 {
1348 if (*p == '{')
1349 level++;
1350 else
1351 if (*p == '}')
1352 {
1353 level--;
1354 if (level == 0)
1355 break;
1356 }
1357 *q++=(*p++);
1358 }
1359 *q='\0';
1360 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1361 &beta,exception);
1362 point.x=alpha;
1363 point.y=beta;
1364 p++;
1365 }
1366 else
1367 if (*p == '[')
1368 {
1369 level++;
1370 q=subexpression;
1371 for (p++; *p != '\0'; )
1372 {
1373 if (*p == '[')
1374 level++;
1375 else
1376 if (*p == ']')
1377 {
1378 level--;
1379 if (level == 0)
1380 break;
1381 }
1382 *q++=(*p++);
1383 }
1384 *q='\0';
1385 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1386 &beta,exception);
1387 point.x+=alpha;
1388 point.y+=beta;
1389 p++;
1390 }
1391 if (*p == '.')
1392 p++;
1393 }
1394 }
1395 length=GetImageListLength(fx_info->images);
1396 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001397 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001398 i%=length;
1399 image=GetImageFromList(fx_info->images,i);
1400 if (image == (Image *) NULL)
1401 {
1402 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001403 "NoSuchImage","'%s'",expression);
cristy3ed852e2009-09-05 21:47:34 +00001404 return(0.0);
1405 }
cristy4c08aed2011-07-01 19:47:50 +00001406 GetPixelInfo(image,&pixel);
1407 (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
cristy4f820712011-04-01 12:35:43 +00001408 point.x,point.y,&pixel,exception);
cristy1707c6c2012-01-18 23:30:54 +00001409 if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1410 (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001411 (LocaleCompare(p,"saturation") != 0) &&
1412 (LocaleCompare(p,"lightness") != 0))
1413 {
1414 char
1415 name[MaxTextExtent];
1416
1417 (void) CopyMagickString(name,p,MaxTextExtent);
1418 for (q=name+(strlen(name)-1); q > name; q--)
1419 {
1420 if (*q == ')')
1421 break;
1422 if (*q == '.')
1423 {
1424 *q='\0';
1425 break;
1426 }
1427 }
1428 if ((strlen(name) > 2) &&
1429 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1430 {
cristy4c08aed2011-07-01 19:47:50 +00001431 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001432 *color;
1433
cristy4c08aed2011-07-01 19:47:50 +00001434 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1435 if (color != (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001436 {
1437 pixel=(*color);
1438 p+=strlen(name);
1439 }
1440 else
cristy1707c6c2012-01-18 23:30:54 +00001441 {
1442 MagickBooleanType
1443 status;
1444
1445 status=QueryColorCompliance(name,AllCompliance,&pixel,
1446 fx_info->exception);
1447 if (status != MagickFalse)
1448 {
1449 (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1450 name),ClonePixelInfo(&pixel));
1451 p+=strlen(name);
1452 }
1453 }
cristy3ed852e2009-09-05 21:47:34 +00001454 }
1455 }
1456 (void) CopyMagickString(symbol,p,MaxTextExtent);
1457 StripString(symbol);
1458 if (*symbol == '\0')
1459 {
1460 switch (channel)
1461 {
cristy0568ffc2011-07-25 16:54:14 +00001462 case RedPixelChannel: return(QuantumScale*pixel.red);
1463 case GreenPixelChannel: return(QuantumScale*pixel.green);
1464 case BluePixelChannel: return(QuantumScale*pixel.blue);
1465 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001466 {
1467 if (image->colorspace != CMYKColorspace)
1468 {
1469 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001470 ImageError,"ColorSeparatedImageRequired","'%s'",
cristy3ed852e2009-09-05 21:47:34 +00001471 image->filename);
1472 return(0.0);
1473 }
cristy4c08aed2011-07-01 19:47:50 +00001474 return(QuantumScale*pixel.black);
1475 }
cristy0568ffc2011-07-25 16:54:14 +00001476 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001477 {
1478 MagickRealType
1479 alpha;
1480
1481 if (pixel.matte == MagickFalse)
1482 return(1.0);
1483 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1484 return(alpha);
cristy3ed852e2009-09-05 21:47:34 +00001485 }
cristya382aca2011-12-06 18:22:48 +00001486 case IndexPixelChannel:
1487 return(0.0);
cristyb3a73b52011-07-26 01:34:43 +00001488 case IntensityPixelChannel:
cristyf364ed42010-12-15 01:54:43 +00001489 {
cristy4c08aed2011-07-01 19:47:50 +00001490 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristyf364ed42010-12-15 01:54:43 +00001491 }
cristy3ed852e2009-09-05 21:47:34 +00001492 default:
1493 break;
1494 }
1495 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001496 "UnableToParseExpression","'%s'",p);
cristy3ed852e2009-09-05 21:47:34 +00001497 return(0.0);
1498 }
1499 switch (*symbol)
1500 {
1501 case 'A':
1502 case 'a':
1503 {
1504 if (LocaleCompare(symbol,"a") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001505 return((MagickRealType) (QuantumScale*pixel.alpha));
cristy3ed852e2009-09-05 21:47:34 +00001506 break;
1507 }
1508 case 'B':
1509 case 'b':
1510 {
1511 if (LocaleCompare(symbol,"b") == 0)
1512 return(QuantumScale*pixel.blue);
1513 break;
1514 }
1515 case 'C':
1516 case 'c':
1517 {
1518 if (LocaleNCompare(symbol,"channel",7) == 0)
1519 {
1520 GeometryInfo
1521 channel_info;
1522
1523 MagickStatusType
1524 flags;
1525
1526 flags=ParseGeometry(symbol+7,&channel_info);
1527 if (image->colorspace == CMYKColorspace)
1528 switch (channel)
1529 {
cristy0568ffc2011-07-25 16:54:14 +00001530 case CyanPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001531 {
1532 if ((flags & RhoValue) == 0)
1533 return(0.0);
1534 return(channel_info.rho);
1535 }
cristy0568ffc2011-07-25 16:54:14 +00001536 case MagentaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001537 {
1538 if ((flags & SigmaValue) == 0)
1539 return(0.0);
1540 return(channel_info.sigma);
1541 }
cristy0568ffc2011-07-25 16:54:14 +00001542 case YellowPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001543 {
1544 if ((flags & XiValue) == 0)
1545 return(0.0);
1546 return(channel_info.xi);
1547 }
cristy0568ffc2011-07-25 16:54:14 +00001548 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001549 {
1550 if ((flags & PsiValue) == 0)
1551 return(0.0);
1552 return(channel_info.psi);
1553 }
cristy0568ffc2011-07-25 16:54:14 +00001554 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001555 {
1556 if ((flags & ChiValue) == 0)
1557 return(0.0);
1558 return(channel_info.chi);
1559 }
1560 default:
1561 return(0.0);
1562 }
1563 switch (channel)
1564 {
cristy0568ffc2011-07-25 16:54:14 +00001565 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001566 {
1567 if ((flags & RhoValue) == 0)
1568 return(0.0);
1569 return(channel_info.rho);
1570 }
cristy0568ffc2011-07-25 16:54:14 +00001571 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001572 {
1573 if ((flags & SigmaValue) == 0)
1574 return(0.0);
1575 return(channel_info.sigma);
1576 }
cristy0568ffc2011-07-25 16:54:14 +00001577 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001578 {
1579 if ((flags & XiValue) == 0)
1580 return(0.0);
1581 return(channel_info.xi);
1582 }
cristy0568ffc2011-07-25 16:54:14 +00001583 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001584 {
1585 if ((flags & ChiValue) == 0)
1586 return(0.0);
1587 return(channel_info.chi);
1588 }
cristy0568ffc2011-07-25 16:54:14 +00001589 case AlphaPixelChannel:
cristy4c08aed2011-07-01 19:47:50 +00001590 {
1591 if ((flags & PsiValue) == 0)
1592 return(0.0);
1593 return(channel_info.psi);
1594 }
cristy3ed852e2009-09-05 21:47:34 +00001595 default:
1596 return(0.0);
1597 }
1598 return(0.0);
1599 }
1600 if (LocaleCompare(symbol,"c") == 0)
1601 return(QuantumScale*pixel.red);
1602 break;
1603 }
1604 case 'D':
1605 case 'd':
1606 {
1607 if (LocaleNCompare(symbol,"depth",5) == 0)
1608 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1609 break;
1610 }
1611 case 'G':
1612 case 'g':
1613 {
1614 if (LocaleCompare(symbol,"g") == 0)
1615 return(QuantumScale*pixel.green);
1616 break;
1617 }
1618 case 'K':
1619 case 'k':
1620 {
1621 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1622 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1623 if (LocaleCompare(symbol,"k") == 0)
1624 {
1625 if (image->colorspace != CMYKColorspace)
1626 {
1627 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001628 OptionError,"ColorSeparatedImageRequired","'%s'",
cristy3ed852e2009-09-05 21:47:34 +00001629 image->filename);
1630 return(0.0);
1631 }
cristy4c08aed2011-07-01 19:47:50 +00001632 return(QuantumScale*pixel.black);
cristy3ed852e2009-09-05 21:47:34 +00001633 }
1634 break;
1635 }
1636 case 'H':
1637 case 'h':
1638 {
1639 if (LocaleCompare(symbol,"h") == 0)
1640 return((MagickRealType) image->rows);
1641 if (LocaleCompare(symbol,"hue") == 0)
1642 {
1643 double
1644 hue,
1645 lightness,
1646 saturation;
1647
cristyda1f9c12011-10-02 21:39:49 +00001648 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1649 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001650 return(hue);
1651 }
1652 break;
1653 }
1654 case 'I':
1655 case 'i':
1656 {
1657 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1658 (LocaleCompare(symbol,"image.minima") == 0) ||
1659 (LocaleCompare(symbol,"image.maxima") == 0) ||
1660 (LocaleCompare(symbol,"image.mean") == 0) ||
1661 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1662 (LocaleCompare(symbol,"image.skewness") == 0) ||
1663 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1664 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1665 if (LocaleCompare(symbol,"image.resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001666 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001667 if (LocaleCompare(symbol,"image.resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001668 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001669 if (LocaleCompare(symbol,"intensity") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001670 return(QuantumScale*GetPixelInfoIntensity(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00001671 if (LocaleCompare(symbol,"i") == 0)
1672 return((MagickRealType) x);
1673 break;
1674 }
1675 case 'J':
1676 case 'j':
1677 {
1678 if (LocaleCompare(symbol,"j") == 0)
1679 return((MagickRealType) y);
1680 break;
1681 }
1682 case 'L':
1683 case 'l':
1684 {
1685 if (LocaleCompare(symbol,"lightness") == 0)
1686 {
1687 double
1688 hue,
1689 lightness,
1690 saturation;
1691
cristyda1f9c12011-10-02 21:39:49 +00001692 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1693 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001694 return(lightness);
1695 }
1696 if (LocaleCompare(symbol,"luminance") == 0)
1697 {
1698 double
1699 luminence;
1700
1701 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1702 return(QuantumScale*luminence);
1703 }
1704 break;
1705 }
1706 case 'M':
1707 case 'm':
1708 {
1709 if (LocaleNCompare(symbol,"maxima",6) == 0)
1710 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1711 if (LocaleNCompare(symbol,"mean",4) == 0)
1712 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1713 if (LocaleNCompare(symbol,"minima",6) == 0)
1714 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1715 if (LocaleCompare(symbol,"m") == 0)
1716 return(QuantumScale*pixel.blue);
1717 break;
1718 }
1719 case 'N':
1720 case 'n':
1721 {
1722 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001723 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001724 break;
1725 }
1726 case 'O':
1727 case 'o':
1728 {
1729 if (LocaleCompare(symbol,"o") == 0)
cristy4c08aed2011-07-01 19:47:50 +00001730 return(QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00001731 break;
1732 }
1733 case 'P':
1734 case 'p':
1735 {
1736 if (LocaleCompare(symbol,"page.height") == 0)
1737 return((MagickRealType) image->page.height);
1738 if (LocaleCompare(symbol,"page.width") == 0)
1739 return((MagickRealType) image->page.width);
1740 if (LocaleCompare(symbol,"page.x") == 0)
1741 return((MagickRealType) image->page.x);
1742 if (LocaleCompare(symbol,"page.y") == 0)
1743 return((MagickRealType) image->page.y);
1744 break;
1745 }
1746 case 'R':
1747 case 'r':
1748 {
1749 if (LocaleCompare(symbol,"resolution.x") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001750 return(image->resolution.x);
cristy3ed852e2009-09-05 21:47:34 +00001751 if (LocaleCompare(symbol,"resolution.y") == 0)
cristy2a11bef2011-10-28 18:33:11 +00001752 return(image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001753 if (LocaleCompare(symbol,"r") == 0)
1754 return(QuantumScale*pixel.red);
1755 break;
1756 }
1757 case 'S':
1758 case 's':
1759 {
1760 if (LocaleCompare(symbol,"saturation") == 0)
1761 {
1762 double
1763 hue,
1764 lightness,
1765 saturation;
1766
cristyda1f9c12011-10-02 21:39:49 +00001767 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1768 &lightness);
cristy3ed852e2009-09-05 21:47:34 +00001769 return(saturation);
1770 }
1771 if (LocaleNCompare(symbol,"skewness",8) == 0)
1772 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1773 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1774 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1775 break;
1776 }
1777 case 'T':
1778 case 't':
1779 {
1780 if (LocaleCompare(symbol,"t") == 0)
cristy5a15b932011-03-26 12:50:33 +00001781 return((MagickRealType) GetImageIndexInList(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001782 break;
1783 }
1784 case 'W':
1785 case 'w':
1786 {
1787 if (LocaleCompare(symbol,"w") == 0)
1788 return((MagickRealType) image->columns);
1789 break;
1790 }
1791 case 'Y':
1792 case 'y':
1793 {
1794 if (LocaleCompare(symbol,"y") == 0)
1795 return(QuantumScale*pixel.green);
1796 break;
1797 }
1798 case 'Z':
1799 case 'z':
1800 {
1801 if (LocaleCompare(symbol,"z") == 0)
1802 {
1803 MagickRealType
1804 depth;
1805
cristyfefab1b2011-07-05 00:33:22 +00001806 depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001807 return(depth);
1808 }
1809 break;
1810 }
1811 default:
1812 break;
1813 }
1814 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1815 if (value != (const char *) NULL)
cristydbdd0e32011-11-04 23:29:40 +00001816 return((MagickRealType) StringToDouble(value,(char **) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001817 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001818 "UnableToParseExpression","'%s'",symbol);
cristy3ed852e2009-09-05 21:47:34 +00001819 return(0.0);
1820}
1821
1822static const char *FxOperatorPrecedence(const char *expression,
1823 ExceptionInfo *exception)
1824{
1825 typedef enum
1826 {
1827 UndefinedPrecedence,
1828 NullPrecedence,
1829 BitwiseComplementPrecedence,
1830 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001831 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001832 MultiplyPrecedence,
1833 AdditionPrecedence,
1834 ShiftPrecedence,
1835 RelationalPrecedence,
1836 EquivalencyPrecedence,
1837 BitwiseAndPrecedence,
1838 BitwiseOrPrecedence,
1839 LogicalAndPrecedence,
1840 LogicalOrPrecedence,
1841 TernaryPrecedence,
1842 AssignmentPrecedence,
1843 CommaPrecedence,
1844 SeparatorPrecedence
1845 } FxPrecedence;
1846
1847 FxPrecedence
1848 precedence,
1849 target;
1850
1851 register const char
1852 *subexpression;
1853
1854 register int
1855 c;
1856
cristybb503372010-05-27 20:51:26 +00001857 size_t
cristy3ed852e2009-09-05 21:47:34 +00001858 level;
1859
1860 c=0;
1861 level=0;
1862 subexpression=(const char *) NULL;
1863 target=NullPrecedence;
1864 while (*expression != '\0')
1865 {
1866 precedence=UndefinedPrecedence;
1867 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1868 {
1869 expression++;
1870 continue;
1871 }
cristy488fa882010-03-01 22:34:24 +00001872 switch (*expression)
1873 {
1874 case 'A':
1875 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001876 {
cristyb33454f2011-08-03 02:10:45 +00001877#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00001878 if (LocaleNCompare(expression,"acosh",5) == 0)
1879 {
1880 expression+=5;
1881 break;
1882 }
cristyb33454f2011-08-03 02:10:45 +00001883#endif
1884#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00001885 if (LocaleNCompare(expression,"asinh",5) == 0)
1886 {
1887 expression+=5;
1888 break;
1889 }
cristyb33454f2011-08-03 02:10:45 +00001890#endif
1891#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00001892 if (LocaleNCompare(expression,"atanh",5) == 0)
cristy488fa882010-03-01 22:34:24 +00001893 {
1894 expression+=5;
1895 break;
1896 }
cristyb33454f2011-08-03 02:10:45 +00001897#endif
cristy488fa882010-03-01 22:34:24 +00001898 break;
cristy3ed852e2009-09-05 21:47:34 +00001899 }
cristy62d455f2011-11-03 11:42:28 +00001900 case 'E':
1901 case 'e':
1902 {
1903 if ((LocaleNCompare(expression,"E+",2) == 0) ||
1904 (LocaleNCompare(expression,"E-",2) == 0))
1905 {
1906 expression+=2; /* scientific notation */
1907 break;
1908 }
1909 }
cristy488fa882010-03-01 22:34:24 +00001910 case 'J':
1911 case 'j':
1912 {
1913 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1914 (LocaleNCompare(expression,"j1",2) == 0))
1915 {
1916 expression+=2;
1917 break;
1918 }
1919 break;
1920 }
cristy2def9322010-06-18 23:59:37 +00001921 case '#':
1922 {
1923 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1924 expression++;
1925 break;
1926 }
cristy488fa882010-03-01 22:34:24 +00001927 default:
1928 break;
1929 }
cristy3ed852e2009-09-05 21:47:34 +00001930 if ((c == (int) '{') || (c == (int) '['))
1931 level++;
1932 else
1933 if ((c == (int) '}') || (c == (int) ']'))
1934 level--;
1935 if (level == 0)
1936 switch ((unsigned char) *expression)
1937 {
1938 case '~':
1939 case '!':
1940 {
1941 precedence=BitwiseComplementPrecedence;
1942 break;
1943 }
1944 case '^':
cristy6621e252010-08-13 00:42:57 +00001945 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001946 {
1947 precedence=ExponentPrecedence;
1948 break;
1949 }
1950 default:
1951 {
1952 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1953 (strchr(")",c) != (char *) NULL))) &&
1954 (((islower((int) ((char) *expression)) != 0) ||
1955 (strchr("(",(int) *expression) != (char *) NULL)) ||
1956 ((isdigit((int) ((char) c)) == 0) &&
1957 (isdigit((int) ((char) *expression)) != 0))) &&
1958 (strchr("xy",(int) *expression) == (char *) NULL))
1959 precedence=MultiplyPrecedence;
1960 break;
1961 }
1962 case '*':
1963 case '/':
1964 case '%':
1965 {
1966 precedence=MultiplyPrecedence;
1967 break;
1968 }
1969 case '+':
1970 case '-':
1971 {
1972 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1973 (isalpha(c) != 0))
1974 precedence=AdditionPrecedence;
1975 break;
1976 }
1977 case LeftShiftOperator:
1978 case RightShiftOperator:
1979 {
1980 precedence=ShiftPrecedence;
1981 break;
1982 }
1983 case '<':
1984 case LessThanEqualOperator:
1985 case GreaterThanEqualOperator:
1986 case '>':
1987 {
1988 precedence=RelationalPrecedence;
1989 break;
1990 }
1991 case EqualOperator:
1992 case NotEqualOperator:
1993 {
1994 precedence=EquivalencyPrecedence;
1995 break;
1996 }
1997 case '&':
1998 {
1999 precedence=BitwiseAndPrecedence;
2000 break;
2001 }
2002 case '|':
2003 {
2004 precedence=BitwiseOrPrecedence;
2005 break;
2006 }
2007 case LogicalAndOperator:
2008 {
2009 precedence=LogicalAndPrecedence;
2010 break;
2011 }
2012 case LogicalOrOperator:
2013 {
2014 precedence=LogicalOrPrecedence;
2015 break;
2016 }
cristy116af162010-08-13 01:25:47 +00002017 case ExponentialNotation:
2018 {
2019 precedence=ExponentialNotationPrecedence;
2020 break;
2021 }
cristy3ed852e2009-09-05 21:47:34 +00002022 case ':':
2023 case '?':
2024 {
2025 precedence=TernaryPrecedence;
2026 break;
2027 }
2028 case '=':
2029 {
2030 precedence=AssignmentPrecedence;
2031 break;
2032 }
2033 case ',':
2034 {
2035 precedence=CommaPrecedence;
2036 break;
2037 }
2038 case ';':
2039 {
2040 precedence=SeparatorPrecedence;
2041 break;
2042 }
2043 }
2044 if ((precedence == BitwiseComplementPrecedence) ||
2045 (precedence == TernaryPrecedence) ||
2046 (precedence == AssignmentPrecedence))
2047 {
2048 if (precedence > target)
2049 {
2050 /*
2051 Right-to-left associativity.
2052 */
2053 target=precedence;
2054 subexpression=expression;
2055 }
2056 }
2057 else
2058 if (precedence >= target)
2059 {
2060 /*
2061 Left-to-right associativity.
2062 */
2063 target=precedence;
2064 subexpression=expression;
2065 }
2066 if (strchr("(",(int) *expression) != (char *) NULL)
2067 expression=FxSubexpression(expression,exception);
2068 c=(int) (*expression++);
2069 }
2070 return(subexpression);
2071}
2072
2073static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002074 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002075 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002076{
2077 char
2078 *q,
2079 subexpression[MaxTextExtent];
2080
2081 MagickRealType
2082 alpha,
2083 gamma;
2084
2085 register const char
2086 *p;
2087
2088 *beta=0.0;
2089 if (exception->severity != UndefinedException)
2090 return(0.0);
2091 while (isspace((int) *expression) != 0)
2092 expression++;
2093 if (*expression == '\0')
2094 {
2095 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00002096 "MissingExpression","'%s'",expression);
cristy3ed852e2009-09-05 21:47:34 +00002097 return(0.0);
2098 }
cristy66322f02010-05-17 11:40:48 +00002099 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002100 p=FxOperatorPrecedence(expression,exception);
2101 if (p != (const char *) NULL)
2102 {
2103 (void) CopyMagickString(subexpression,expression,(size_t)
2104 (p-expression+1));
2105 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2106 exception);
2107 switch ((unsigned char) *p)
2108 {
2109 case '~':
2110 {
2111 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002112 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002113 return(*beta);
2114 }
2115 case '!':
2116 {
2117 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2118 return(*beta == 0.0 ? 1.0 : 0.0);
2119 }
2120 case '^':
2121 {
2122 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2123 channel,x,y,++p,beta,exception));
2124 return(*beta);
2125 }
2126 case '*':
cristy116af162010-08-13 01:25:47 +00002127 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002128 {
2129 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2130 return(alpha*(*beta));
2131 }
2132 case '/':
2133 {
2134 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2135 if (*beta == 0.0)
2136 {
2137 if (exception->severity == UndefinedException)
2138 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00002139 OptionError,"DivideByZero","'%s'",expression);
cristy3ed852e2009-09-05 21:47:34 +00002140 return(0.0);
2141 }
2142 return(alpha/(*beta));
2143 }
2144 case '%':
2145 {
2146 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2147 *beta=fabs(floor(((double) *beta)+0.5));
2148 if (*beta == 0.0)
2149 {
2150 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00002151 OptionError,"DivideByZero","'%s'",expression);
cristy3ed852e2009-09-05 21:47:34 +00002152 return(0.0);
2153 }
2154 return(fmod((double) alpha,(double) *beta));
2155 }
2156 case '+':
2157 {
2158 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2159 return(alpha+(*beta));
2160 }
2161 case '-':
2162 {
2163 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2164 return(alpha-(*beta));
2165 }
2166 case LeftShiftOperator:
2167 {
2168 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002169 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002170 return(*beta);
2171 }
2172 case RightShiftOperator:
2173 {
2174 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002175 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002176 return(*beta);
2177 }
2178 case '<':
2179 {
2180 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2181 return(alpha < *beta ? 1.0 : 0.0);
2182 }
2183 case LessThanEqualOperator:
2184 {
2185 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2186 return(alpha <= *beta ? 1.0 : 0.0);
2187 }
2188 case '>':
2189 {
2190 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2191 return(alpha > *beta ? 1.0 : 0.0);
2192 }
2193 case GreaterThanEqualOperator:
2194 {
2195 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2196 return(alpha >= *beta ? 1.0 : 0.0);
2197 }
2198 case EqualOperator:
2199 {
2200 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2201 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2202 }
2203 case NotEqualOperator:
2204 {
2205 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2206 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2207 }
2208 case '&':
2209 {
2210 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002211 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002212 return(*beta);
2213 }
2214 case '|':
2215 {
2216 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy1707c6c2012-01-18 23:30:54 +00002217 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002218 return(*beta);
2219 }
2220 case LogicalAndOperator:
2221 {
2222 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2223 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2224 return(*beta);
2225 }
2226 case LogicalOrOperator:
2227 {
2228 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2229 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2230 return(*beta);
2231 }
2232 case '?':
2233 {
2234 MagickRealType
2235 gamma;
2236
2237 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2238 q=subexpression;
2239 p=StringToken(":",&q);
2240 if (q == (char *) NULL)
2241 {
2242 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00002243 OptionError,"UnableToParseExpression","'%s'",subexpression);
cristy3ed852e2009-09-05 21:47:34 +00002244 return(0.0);
2245 }
2246 if (fabs((double) alpha) > MagickEpsilon)
2247 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2248 else
2249 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2250 return(gamma);
2251 }
2252 case '=':
2253 {
2254 char
2255 numeric[MaxTextExtent];
2256
2257 q=subexpression;
2258 while (isalpha((int) ((unsigned char) *q)) != 0)
2259 q++;
2260 if (*q != '\0')
2261 {
2262 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00002263 OptionError,"UnableToParseExpression","'%s'",subexpression);
cristy3ed852e2009-09-05 21:47:34 +00002264 return(0.0);
2265 }
2266 ClearMagickException(exception);
2267 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristyb51dff52011-05-19 16:55:47 +00002268 (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002269 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002270 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2271 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2272 subexpression),ConstantString(numeric));
2273 return(*beta);
2274 }
2275 case ',':
2276 {
2277 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2278 return(alpha);
2279 }
2280 case ';':
2281 {
2282 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2283 return(*beta);
2284 }
2285 default:
2286 {
2287 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2288 exception);
2289 return(gamma);
2290 }
2291 }
2292 }
2293 if (strchr("(",(int) *expression) != (char *) NULL)
2294 {
2295 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2296 subexpression[strlen(subexpression)-1]='\0';
2297 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2298 exception);
2299 return(gamma);
2300 }
cristy8b8a3ae2010-10-23 18:49:46 +00002301 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002302 {
2303 case '+':
2304 {
2305 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2306 exception);
2307 return(1.0*gamma);
2308 }
2309 case '-':
2310 {
2311 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2312 exception);
2313 return(-1.0*gamma);
2314 }
2315 case '~':
2316 {
2317 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2318 exception);
cristybb503372010-05-27 20:51:26 +00002319 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002320 }
2321 case 'A':
2322 case 'a':
2323 {
2324 if (LocaleNCompare(expression,"abs",3) == 0)
2325 {
2326 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2327 exception);
2328 return((MagickRealType) fabs((double) alpha));
2329 }
cristyb33454f2011-08-03 02:10:45 +00002330#if defined(MAGICKCORE_HAVE_ACOSH)
cristy363772a2011-07-28 23:25:33 +00002331 if (LocaleNCompare(expression,"acosh",5) == 0)
2332 {
2333 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2334 exception);
2335 return((MagickRealType) acosh((double) alpha));
2336 }
cristyb33454f2011-08-03 02:10:45 +00002337#endif
cristy3ed852e2009-09-05 21:47:34 +00002338 if (LocaleNCompare(expression,"acos",4) == 0)
2339 {
2340 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2341 exception);
2342 return((MagickRealType) acos((double) alpha));
2343 }
cristy43c22f42010-03-30 12:34:07 +00002344#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002345 if (LocaleNCompare(expression,"airy",4) == 0)
2346 {
2347 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2348 exception);
2349 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002350 return(1.0);
2351 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002352 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002353 }
cristy43c22f42010-03-30 12:34:07 +00002354#endif
cristyb33454f2011-08-03 02:10:45 +00002355#if defined(MAGICKCORE_HAVE_ASINH)
cristy363772a2011-07-28 23:25:33 +00002356 if (LocaleNCompare(expression,"asinh",5) == 0)
2357 {
2358 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2359 exception);
2360 return((MagickRealType) asinh((double) alpha));
2361 }
cristyb33454f2011-08-03 02:10:45 +00002362#endif
cristy3ed852e2009-09-05 21:47:34 +00002363 if (LocaleNCompare(expression,"asin",4) == 0)
2364 {
2365 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2366 exception);
2367 return((MagickRealType) asin((double) alpha));
2368 }
2369 if (LocaleNCompare(expression,"alt",3) == 0)
2370 {
2371 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2372 exception);
cristybb503372010-05-27 20:51:26 +00002373 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002374 }
2375 if (LocaleNCompare(expression,"atan2",5) == 0)
2376 {
2377 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2378 exception);
2379 return((MagickRealType) atan2((double) alpha,(double) *beta));
2380 }
cristyb33454f2011-08-03 02:10:45 +00002381#if defined(MAGICKCORE_HAVE_ATANH)
cristy363772a2011-07-28 23:25:33 +00002382 if (LocaleNCompare(expression,"atanh",5) == 0)
2383 {
2384 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2385 exception);
2386 return((MagickRealType) atanh((double) alpha));
2387 }
cristyb33454f2011-08-03 02:10:45 +00002388#endif
cristy3ed852e2009-09-05 21:47:34 +00002389 if (LocaleNCompare(expression,"atan",4) == 0)
2390 {
2391 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2392 exception);
2393 return((MagickRealType) atan((double) alpha));
2394 }
2395 if (LocaleCompare(expression,"a") == 0)
2396 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2397 break;
2398 }
2399 case 'B':
2400 case 'b':
2401 {
2402 if (LocaleCompare(expression,"b") == 0)
2403 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2404 break;
2405 }
2406 case 'C':
2407 case 'c':
2408 {
2409 if (LocaleNCompare(expression,"ceil",4) == 0)
2410 {
2411 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2412 exception);
2413 return((MagickRealType) ceil((double) alpha));
2414 }
2415 if (LocaleNCompare(expression,"cosh",4) == 0)
2416 {
2417 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2418 exception);
2419 return((MagickRealType) cosh((double) alpha));
2420 }
2421 if (LocaleNCompare(expression,"cos",3) == 0)
2422 {
2423 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2424 exception);
2425 return((MagickRealType) cos((double) alpha));
2426 }
2427 if (LocaleCompare(expression,"c") == 0)
2428 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2429 break;
2430 }
2431 case 'D':
2432 case 'd':
2433 {
2434 if (LocaleNCompare(expression,"debug",5) == 0)
2435 {
2436 const char
2437 *type;
2438
2439 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2440 exception);
2441 if (fx_info->images->colorspace == CMYKColorspace)
2442 switch (channel)
2443 {
cristy0568ffc2011-07-25 16:54:14 +00002444 case CyanPixelChannel: type="cyan"; break;
2445 case MagentaPixelChannel: type="magenta"; break;
2446 case YellowPixelChannel: type="yellow"; break;
2447 case AlphaPixelChannel: type="opacity"; break;
2448 case BlackPixelChannel: type="black"; break;
cristy3ed852e2009-09-05 21:47:34 +00002449 default: type="unknown"; break;
2450 }
2451 else
2452 switch (channel)
2453 {
cristy0568ffc2011-07-25 16:54:14 +00002454 case RedPixelChannel: type="red"; break;
2455 case GreenPixelChannel: type="green"; break;
2456 case BluePixelChannel: type="blue"; break;
2457 case AlphaPixelChannel: type="opacity"; break;
cristy3ed852e2009-09-05 21:47:34 +00002458 default: type="unknown"; break;
2459 }
2460 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2461 if (strlen(subexpression) > 1)
2462 subexpression[strlen(subexpression)-1]='\0';
2463 if (fx_info->file != (FILE *) NULL)
cristy1707c6c2012-01-18 23:30:54 +00002464 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2465 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2466 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002467 return(0.0);
2468 }
cristy5597a8d2011-11-04 00:25:32 +00002469 if (LocaleNCompare(expression,"drc",3) == 0)
2470 {
2471 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2472 exception);
2473 return((MagickRealType) (alpha/(*beta*(alpha-1.0)+1.0)));
2474 }
cristy3ed852e2009-09-05 21:47:34 +00002475 break;
2476 }
2477 case 'E':
2478 case 'e':
2479 {
2480 if (LocaleCompare(expression,"epsilon") == 0)
2481 return((MagickRealType) MagickEpsilon);
2482 if (LocaleNCompare(expression,"exp",3) == 0)
2483 {
2484 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2485 exception);
2486 return((MagickRealType) exp((double) alpha));
2487 }
2488 if (LocaleCompare(expression,"e") == 0)
2489 return((MagickRealType) 2.7182818284590452354);
2490 break;
2491 }
2492 case 'F':
2493 case 'f':
2494 {
2495 if (LocaleNCompare(expression,"floor",5) == 0)
2496 {
2497 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2498 exception);
2499 return((MagickRealType) floor((double) alpha));
2500 }
2501 break;
2502 }
2503 case 'G':
2504 case 'g':
2505 {
cristy9eeedea2011-11-02 19:04:05 +00002506 if (LocaleNCompare(expression,"gauss",5) == 0)
2507 {
2508 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2509 exception);
2510 gamma=exp((double) (-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2511 return((MagickRealType) gamma);
2512 }
cristyb0aad4c2011-11-02 19:30:35 +00002513 if (LocaleNCompare(expression,"gcd",3) == 0)
2514 {
2515 MagickOffsetType
2516 gcd;
2517
2518 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2519 exception);
cristy1707c6c2012-01-18 23:30:54 +00002520 gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2521 0.5));
cristyb0aad4c2011-11-02 19:30:35 +00002522 return((MagickRealType) gcd);
2523 }
cristy3ed852e2009-09-05 21:47:34 +00002524 if (LocaleCompare(expression,"g") == 0)
2525 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2526 break;
2527 }
2528 case 'H':
2529 case 'h':
2530 {
2531 if (LocaleCompare(expression,"h") == 0)
2532 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2533 if (LocaleCompare(expression,"hue") == 0)
2534 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2535 if (LocaleNCompare(expression,"hypot",5) == 0)
2536 {
2537 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2538 exception);
2539 return((MagickRealType) hypot((double) alpha,(double) *beta));
2540 }
2541 break;
2542 }
2543 case 'K':
2544 case 'k':
2545 {
2546 if (LocaleCompare(expression,"k") == 0)
2547 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2548 break;
2549 }
2550 case 'I':
2551 case 'i':
2552 {
2553 if (LocaleCompare(expression,"intensity") == 0)
2554 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2555 if (LocaleNCompare(expression,"int",3) == 0)
2556 {
2557 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2558 exception);
cristy16788e42010-08-13 13:44:26 +00002559 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002560 }
cristy82b20722011-11-05 21:52:36 +00002561#if defined(MAGICKCORE_HAVE_ISNAN)
cristy639399c2011-11-02 19:16:15 +00002562 if (LocaleNCompare(expression,"isnan",5) == 0)
2563 {
2564 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2565 exception);
cristy17a10202011-11-02 19:17:04 +00002566 return((MagickRealType) !!isnan((double) alpha));
cristy639399c2011-11-02 19:16:15 +00002567 }
cristy82b20722011-11-05 21:52:36 +00002568#endif
cristy3ed852e2009-09-05 21:47:34 +00002569 if (LocaleCompare(expression,"i") == 0)
2570 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2571 break;
2572 }
2573 case 'J':
2574 case 'j':
2575 {
2576 if (LocaleCompare(expression,"j") == 0)
2577 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002578#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002579 if (LocaleNCompare(expression,"j0",2) == 0)
2580 {
2581 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2582 exception);
2583 return((MagickRealType) j0((double) alpha));
2584 }
cristy161b9262010-03-20 19:34:32 +00002585#endif
2586#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002587 if (LocaleNCompare(expression,"j1",2) == 0)
2588 {
2589 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2590 exception);
2591 return((MagickRealType) j1((double) alpha));
2592 }
cristy161b9262010-03-20 19:34:32 +00002593#endif
cristyaa018fa2010-04-08 23:03:54 +00002594#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002595 if (LocaleNCompare(expression,"jinc",4) == 0)
2596 {
2597 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2598 exception);
cristy0946a822010-03-12 17:14:58 +00002599 if (alpha == 0.0)
2600 return(1.0);
cristy1707c6c2012-01-18 23:30:54 +00002601 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/(MagickPI*
2602 alpha));
cristyfce2f7b2010-03-12 00:29:49 +00002603 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002604 }
cristyaa018fa2010-04-08 23:03:54 +00002605#endif
cristy3ed852e2009-09-05 21:47:34 +00002606 break;
2607 }
2608 case 'L':
2609 case 'l':
2610 {
2611 if (LocaleNCompare(expression,"ln",2) == 0)
2612 {
2613 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2614 exception);
2615 return((MagickRealType) log((double) alpha));
2616 }
cristyc8ed5322010-08-31 12:07:59 +00002617 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002618 {
cristyc8ed5322010-08-31 12:07:59 +00002619 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002620 exception);
2621 return((MagickRealType) log10((double) alpha))/log10(2.0);
2622 }
2623 if (LocaleNCompare(expression,"log",3) == 0)
2624 {
2625 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2626 exception);
2627 return((MagickRealType) log10((double) alpha));
2628 }
2629 if (LocaleCompare(expression,"lightness") == 0)
2630 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2631 break;
2632 }
2633 case 'M':
2634 case 'm':
2635 {
2636 if (LocaleCompare(expression,"MaxRGB") == 0)
2637 return((MagickRealType) QuantumRange);
2638 if (LocaleNCompare(expression,"maxima",6) == 0)
2639 break;
2640 if (LocaleNCompare(expression,"max",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002641 {
2642 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2643 exception);
2644 return(alpha > *beta ? alpha : *beta);
2645 }
cristy3ed852e2009-09-05 21:47:34 +00002646 if (LocaleNCompare(expression,"minima",6) == 0)
2647 break;
2648 if (LocaleNCompare(expression,"min",3) == 0)
cristy984049c2011-11-03 18:34:58 +00002649 {
2650 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2651 exception);
2652 return(alpha < *beta ? alpha : *beta);
2653 }
cristy3ed852e2009-09-05 21:47:34 +00002654 if (LocaleNCompare(expression,"mod",3) == 0)
2655 {
2656 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2657 exception);
cristy984049c2011-11-03 18:34:58 +00002658 gamma=alpha-floor((double) (alpha/(*beta)))*(*beta);
2659 return(gamma);
cristy3ed852e2009-09-05 21:47:34 +00002660 }
2661 if (LocaleCompare(expression,"m") == 0)
2662 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2663 break;
2664 }
2665 case 'N':
2666 case 'n':
2667 {
cristyad3502e2011-11-02 19:10:45 +00002668 if (LocaleNCompare(expression,"not",3) == 0)
2669 {
2670 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2671 exception);
2672 return((MagickRealType) (alpha < MagickEpsilon));
2673 }
cristy3ed852e2009-09-05 21:47:34 +00002674 if (LocaleCompare(expression,"n") == 0)
2675 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2676 break;
2677 }
2678 case 'O':
2679 case 'o':
2680 {
2681 if (LocaleCompare(expression,"Opaque") == 0)
2682 return(1.0);
2683 if (LocaleCompare(expression,"o") == 0)
2684 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2685 break;
2686 }
2687 case 'P':
2688 case 'p':
2689 {
cristy670aa3c2011-11-03 00:54:00 +00002690 if (LocaleCompare(expression,"phi") == 0)
2691 return((MagickRealType) MagickPHI);
cristy3ed852e2009-09-05 21:47:34 +00002692 if (LocaleCompare(expression,"pi") == 0)
2693 return((MagickRealType) MagickPI);
2694 if (LocaleNCompare(expression,"pow",3) == 0)
2695 {
2696 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2697 exception);
2698 return((MagickRealType) pow((double) alpha,(double) *beta));
2699 }
2700 if (LocaleCompare(expression,"p") == 0)
2701 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2702 break;
2703 }
2704 case 'Q':
2705 case 'q':
2706 {
2707 if (LocaleCompare(expression,"QuantumRange") == 0)
2708 return((MagickRealType) QuantumRange);
2709 if (LocaleCompare(expression,"QuantumScale") == 0)
2710 return((MagickRealType) QuantumScale);
2711 break;
2712 }
2713 case 'R':
2714 case 'r':
2715 {
2716 if (LocaleNCompare(expression,"rand",4) == 0)
2717 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2718 if (LocaleNCompare(expression,"round",5) == 0)
2719 {
2720 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2721 exception);
cristy16788e42010-08-13 13:44:26 +00002722 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002723 }
2724 if (LocaleCompare(expression,"r") == 0)
2725 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2726 break;
2727 }
2728 case 'S':
2729 case 's':
2730 {
2731 if (LocaleCompare(expression,"saturation") == 0)
2732 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2733 if (LocaleNCompare(expression,"sign",4) == 0)
2734 {
2735 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2736 exception);
2737 return(alpha < 0.0 ? -1.0 : 1.0);
2738 }
cristya6a09e72010-03-02 14:51:02 +00002739 if (LocaleNCompare(expression,"sinc",4) == 0)
2740 {
2741 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2742 exception);
2743 if (alpha == 0)
2744 return(1.0);
2745 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2746 (MagickPI*alpha));
2747 return(gamma);
2748 }
cristy3ed852e2009-09-05 21:47:34 +00002749 if (LocaleNCompare(expression,"sinh",4) == 0)
2750 {
2751 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2752 exception);
2753 return((MagickRealType) sinh((double) alpha));
2754 }
2755 if (LocaleNCompare(expression,"sin",3) == 0)
2756 {
2757 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2758 exception);
2759 return((MagickRealType) sin((double) alpha));
2760 }
2761 if (LocaleNCompare(expression,"sqrt",4) == 0)
2762 {
2763 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2764 exception);
2765 return((MagickRealType) sqrt((double) alpha));
2766 }
cristy9eeedea2011-11-02 19:04:05 +00002767 if (LocaleNCompare(expression,"squish",6) == 0)
2768 {
2769 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2770 exception);
cristyf7b28ea2012-04-27 11:22:49 +00002771 return((MagickRealType) (1.0/(1.0+exp((double) (-alpha)))));
cristy9eeedea2011-11-02 19:04:05 +00002772 }
cristy3ed852e2009-09-05 21:47:34 +00002773 if (LocaleCompare(expression,"s") == 0)
2774 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2775 break;
2776 }
2777 case 'T':
2778 case 't':
2779 {
2780 if (LocaleNCompare(expression,"tanh",4) == 0)
2781 {
2782 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2783 exception);
2784 return((MagickRealType) tanh((double) alpha));
2785 }
2786 if (LocaleNCompare(expression,"tan",3) == 0)
2787 {
2788 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2789 exception);
2790 return((MagickRealType) tan((double) alpha));
2791 }
2792 if (LocaleCompare(expression,"Transparent") == 0)
2793 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002794 if (LocaleNCompare(expression,"trunc",5) == 0)
2795 {
2796 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2797 exception);
2798 if (alpha >= 0.0)
2799 return((MagickRealType) floor((double) alpha));
2800 return((MagickRealType) ceil((double) alpha));
2801 }
cristy3ed852e2009-09-05 21:47:34 +00002802 if (LocaleCompare(expression,"t") == 0)
2803 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2804 break;
2805 }
2806 case 'U':
2807 case 'u':
2808 {
2809 if (LocaleCompare(expression,"u") == 0)
2810 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2811 break;
2812 }
2813 case 'V':
2814 case 'v':
2815 {
2816 if (LocaleCompare(expression,"v") == 0)
2817 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2818 break;
2819 }
2820 case 'W':
2821 case 'w':
2822 {
cristy9eeedea2011-11-02 19:04:05 +00002823 if (LocaleNCompare(expression,"while",5) == 0)
2824 {
2825 do
2826 {
2827 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2828 exception);
2829 } while (fabs((double) alpha) >= MagickEpsilon);
2830 return((MagickRealType) *beta);
2831 }
cristy3ed852e2009-09-05 21:47:34 +00002832 if (LocaleCompare(expression,"w") == 0)
2833 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2834 break;
2835 }
2836 case 'Y':
2837 case 'y':
2838 {
2839 if (LocaleCompare(expression,"y") == 0)
2840 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2841 break;
2842 }
2843 case 'Z':
2844 case 'z':
2845 {
2846 if (LocaleCompare(expression,"z") == 0)
2847 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2848 break;
2849 }
2850 default:
2851 break;
2852 }
2853 q=(char *) expression;
cristydbdd0e32011-11-04 23:29:40 +00002854 alpha=InterpretSiPrefixValue(expression,&q);
cristy3ed852e2009-09-05 21:47:34 +00002855 if (q == expression)
2856 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2857 return(alpha);
2858}
2859
cristy7832dc22011-09-05 01:21:53 +00002860MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
cristy3ed852e2009-09-05 21:47:34 +00002861 MagickRealType *alpha,ExceptionInfo *exception)
2862{
2863 MagickBooleanType
2864 status;
2865
cristy541ae572011-08-05 19:08:59 +00002866 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2867 exception);
cristy3ed852e2009-09-05 21:47:34 +00002868 return(status);
2869}
2870
2871MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2872 MagickRealType *alpha,ExceptionInfo *exception)
2873{
2874 FILE
2875 *file;
2876
2877 MagickBooleanType
2878 status;
2879
2880 file=fx_info->file;
2881 fx_info->file=(FILE *) NULL;
cristy541ae572011-08-05 19:08:59 +00002882 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2883 exception);
cristy3ed852e2009-09-05 21:47:34 +00002884 fx_info->file=file;
2885 return(status);
2886}
2887
cristy7832dc22011-09-05 01:21:53 +00002888MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristy0568ffc2011-07-25 16:54:14 +00002889 const PixelChannel channel,const ssize_t x,const ssize_t y,
cristye85007d2010-06-06 22:51:36 +00002890 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002891{
2892 MagickRealType
2893 beta;
2894
2895 beta=0.0;
2896 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2897 exception);
2898 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2899}
2900
2901/*
2902%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2903% %
2904% %
2905% %
2906% F x I m a g e %
2907% %
2908% %
2909% %
2910%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2911%
2912% FxImage() applies a mathematical expression to the specified image.
2913%
2914% The format of the FxImage method is:
2915%
2916% Image *FxImage(const Image *image,const char *expression,
2917% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002918%
2919% A description of each parameter follows:
2920%
2921% o image: the image.
2922%
cristy3ed852e2009-09-05 21:47:34 +00002923% o expression: A mathematical expression.
2924%
2925% o exception: return any errors or warnings in this structure.
2926%
2927*/
2928
2929static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2930{
cristybb503372010-05-27 20:51:26 +00002931 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002932 i;
2933
2934 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002935 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002936 if (fx_info[i] != (FxInfo *) NULL)
2937 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002938 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002939 return(fx_info);
2940}
2941
2942static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2943 ExceptionInfo *exception)
2944{
2945 char
2946 *fx_expression;
2947
2948 FxInfo
2949 **fx_info;
2950
2951 MagickRealType
2952 alpha;
2953
cristybb503372010-05-27 20:51:26 +00002954 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002955 i;
2956
cristybb503372010-05-27 20:51:26 +00002957 size_t
cristy3ed852e2009-09-05 21:47:34 +00002958 number_threads;
2959
2960 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002961 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002962 if (fx_info == (FxInfo **) NULL)
2963 return((FxInfo **) NULL);
2964 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2965 if (*expression != '@')
2966 fx_expression=ConstantString(expression);
2967 else
2968 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00002969 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002970 {
cristydb070952012-04-20 14:33:00 +00002971 fx_info[i]=AcquireFxInfo(image,fx_expression,exception);
cristy3ed852e2009-09-05 21:47:34 +00002972 if (fx_info[i] == (FxInfo *) NULL)
2973 return(DestroyFxThreadSet(fx_info));
2974 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2975 }
2976 fx_expression=DestroyString(fx_expression);
2977 return(fx_info);
2978}
2979
2980MagickExport Image *FxImage(const Image *image,const char *expression,
2981 ExceptionInfo *exception)
2982{
cristy3ed852e2009-09-05 21:47:34 +00002983#define FxImageTag "Fx/Image"
2984
cristyfa112112010-01-04 17:48:07 +00002985 CacheView
cristy79cedc72011-07-25 00:41:15 +00002986 *fx_view,
2987 *image_view;
cristyfa112112010-01-04 17:48:07 +00002988
cristy3ed852e2009-09-05 21:47:34 +00002989 FxInfo
cristyfa112112010-01-04 17:48:07 +00002990 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00002991
2992 Image
2993 *fx_image;
2994
cristy3ed852e2009-09-05 21:47:34 +00002995 MagickBooleanType
2996 status;
2997
cristybb503372010-05-27 20:51:26 +00002998 MagickOffsetType
2999 progress;
3000
cristy3ed852e2009-09-05 21:47:34 +00003001 MagickRealType
3002 alpha;
3003
cristybb503372010-05-27 20:51:26 +00003004 ssize_t
3005 y;
3006
cristy3ed852e2009-09-05 21:47:34 +00003007 assert(image != (Image *) NULL);
3008 assert(image->signature == MagickSignature);
3009 if (image->debug != MagickFalse)
3010 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy79cedc72011-07-25 00:41:15 +00003011 fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003012 if (fx_image == (Image *) NULL)
3013 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003014 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003015 {
cristy3ed852e2009-09-05 21:47:34 +00003016 fx_image=DestroyImage(fx_image);
3017 return((Image *) NULL);
3018 }
3019 fx_info=AcquireFxThreadSet(image,expression,exception);
3020 if (fx_info == (FxInfo **) NULL)
3021 {
3022 fx_image=DestroyImage(fx_image);
3023 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3024 }
3025 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3026 if (status == MagickFalse)
3027 {
3028 fx_image=DestroyImage(fx_image);
3029 fx_info=DestroyFxThreadSet(fx_info);
3030 return((Image *) NULL);
3031 }
3032 /*
3033 Fx image.
3034 */
3035 status=MagickTrue;
3036 progress=0;
cristydb070952012-04-20 14:33:00 +00003037 image_view=AcquireVirtualCacheView(image,exception);
3038 fx_view=AcquireAuthenticCacheView(fx_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00003039#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003040 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003041#endif
cristybb503372010-05-27 20:51:26 +00003042 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003043 {
cristy5c9e6f22010-09-17 17:31:01 +00003044 const int
3045 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003046
cristy79cedc72011-07-25 00:41:15 +00003047 register const Quantum
3048 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003049
cristy4c08aed2011-07-01 19:47:50 +00003050 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003051 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003052
cristy79cedc72011-07-25 00:41:15 +00003053 register ssize_t
3054 x;
3055
cristy3ed852e2009-09-05 21:47:34 +00003056 if (status == MagickFalse)
3057 continue;
cristy79cedc72011-07-25 00:41:15 +00003058 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003059 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
cristy79cedc72011-07-25 00:41:15 +00003060 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003061 {
3062 status=MagickFalse;
3063 continue;
3064 }
cristybb503372010-05-27 20:51:26 +00003065 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003066 {
cristy79cedc72011-07-25 00:41:15 +00003067 register ssize_t
3068 i;
3069
cristy177e41c2012-04-15 15:08:25 +00003070 if (GetPixelMask(image,p) != 0)
3071 {
3072 p+=GetPixelChannels(image);
3073 q+=GetPixelChannels(fx_image);
3074 continue;
3075 }
cristy79cedc72011-07-25 00:41:15 +00003076 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3077 {
3078 MagickRealType
3079 alpha;
3080
3081 PixelChannel
3082 channel;
3083
3084 PixelTrait
3085 fx_traits,
3086 traits;
3087
cristye2a912b2011-12-05 20:02:07 +00003088 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003089 traits=GetPixelChannelMapTraits(image,channel);
cristy79cedc72011-07-25 00:41:15 +00003090 fx_traits=GetPixelChannelMapTraits(fx_image,channel);
cristy010d7d12011-08-31 01:02:48 +00003091 if ((traits == UndefinedPixelTrait) ||
3092 (fx_traits == UndefinedPixelTrait))
cristy79cedc72011-07-25 00:41:15 +00003093 continue;
cristy177e41c2012-04-15 15:08:25 +00003094 if ((fx_traits & CopyPixelTrait) != 0)
cristy79cedc72011-07-25 00:41:15 +00003095 {
cristy0beccfa2011-09-25 20:47:53 +00003096 SetPixelChannel(fx_image,channel,p[i],q);
cristy79cedc72011-07-25 00:41:15 +00003097 continue;
3098 }
3099 alpha=0.0;
cristya382aca2011-12-06 18:22:48 +00003100 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3101 exception);
cristyb3a73b52011-07-26 01:34:43 +00003102 q[i]=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy79cedc72011-07-25 00:41:15 +00003103 }
3104 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003105 q+=GetPixelChannels(fx_image);
cristy3ed852e2009-09-05 21:47:34 +00003106 }
3107 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3108 status=MagickFalse;
3109 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3110 {
3111 MagickBooleanType
3112 proceed;
3113
cristyb5d5f722009-11-04 03:03:49 +00003114#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003115 #pragma omp critical (MagickCore_FxImage)
cristy3ed852e2009-09-05 21:47:34 +00003116#endif
3117 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3118 if (proceed == MagickFalse)
3119 status=MagickFalse;
3120 }
3121 }
cristy3ed852e2009-09-05 21:47:34 +00003122 fx_view=DestroyCacheView(fx_view);
cristy79cedc72011-07-25 00:41:15 +00003123 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003124 fx_info=DestroyFxThreadSet(fx_info);
3125 if (status == MagickFalse)
3126 fx_image=DestroyImage(fx_image);
3127 return(fx_image);
3128}
3129
3130/*
3131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3132% %
3133% %
3134% %
3135% I m p l o d e I m a g e %
3136% %
3137% %
3138% %
3139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3140%
3141% ImplodeImage() creates a new image that is a copy of an existing
3142% one with the image pixels "implode" by the specified percentage. It
3143% allocates the memory necessary for the new Image structure and returns a
3144% pointer to the new image.
3145%
3146% The format of the ImplodeImage method is:
3147%
3148% Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003149% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003150%
3151% A description of each parameter follows:
3152%
3153% o implode_image: Method ImplodeImage returns a pointer to the image
3154% after it is implode. A null image is returned if there is a memory
3155% shortage.
3156%
3157% o image: the image.
3158%
3159% o amount: Define the extent of the implosion.
3160%
cristy76f512e2011-09-12 01:26:56 +00003161% o method: the pixel interpolation method.
3162%
cristy3ed852e2009-09-05 21:47:34 +00003163% o exception: return any errors or warnings in this structure.
3164%
3165*/
3166MagickExport Image *ImplodeImage(const Image *image,const double amount,
cristy76f512e2011-09-12 01:26:56 +00003167 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003168{
3169#define ImplodeImageTag "Implode/Image"
3170
cristyfa112112010-01-04 17:48:07 +00003171 CacheView
3172 *image_view,
3173 *implode_view;
3174
cristy3ed852e2009-09-05 21:47:34 +00003175 Image
3176 *implode_image;
3177
cristy3ed852e2009-09-05 21:47:34 +00003178 MagickBooleanType
3179 status;
3180
cristybb503372010-05-27 20:51:26 +00003181 MagickOffsetType
3182 progress;
3183
cristy3ed852e2009-09-05 21:47:34 +00003184 MagickRealType
3185 radius;
3186
3187 PointInfo
3188 center,
3189 scale;
3190
cristybb503372010-05-27 20:51:26 +00003191 ssize_t
3192 y;
3193
cristy3ed852e2009-09-05 21:47:34 +00003194 /*
3195 Initialize implode image attributes.
3196 */
3197 assert(image != (Image *) NULL);
3198 assert(image->signature == MagickSignature);
3199 if (image->debug != MagickFalse)
3200 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3201 assert(exception != (ExceptionInfo *) NULL);
3202 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00003203 implode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3204 exception);
cristy3ed852e2009-09-05 21:47:34 +00003205 if (implode_image == (Image *) NULL)
3206 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003207 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003208 {
cristy3ed852e2009-09-05 21:47:34 +00003209 implode_image=DestroyImage(implode_image);
3210 return((Image *) NULL);
3211 }
cristy4c08aed2011-07-01 19:47:50 +00003212 if (implode_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00003213 implode_image->matte=MagickTrue;
3214 /*
3215 Compute scaling factor.
3216 */
3217 scale.x=1.0;
3218 scale.y=1.0;
3219 center.x=0.5*image->columns;
3220 center.y=0.5*image->rows;
3221 radius=center.x;
3222 if (image->columns > image->rows)
3223 scale.y=(double) image->columns/(double) image->rows;
3224 else
3225 if (image->columns < image->rows)
3226 {
3227 scale.x=(double) image->rows/(double) image->columns;
3228 radius=center.y;
3229 }
3230 /*
3231 Implode image.
3232 */
3233 status=MagickTrue;
3234 progress=0;
cristydb070952012-04-20 14:33:00 +00003235 image_view=AcquireVirtualCacheView(image,exception);
3236 implode_view=AcquireAuthenticCacheView(implode_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00003237#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003238 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003239#endif
cristybb503372010-05-27 20:51:26 +00003240 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003241 {
cristy3ed852e2009-09-05 21:47:34 +00003242 MagickRealType
3243 distance;
3244
3245 PointInfo
3246 delta;
3247
cristy6d188022011-09-12 13:23:33 +00003248 register const Quantum
3249 *restrict p;
3250
cristybb503372010-05-27 20:51:26 +00003251 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003252 x;
3253
cristy4c08aed2011-07-01 19:47:50 +00003254 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003255 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003256
3257 if (status == MagickFalse)
3258 continue;
cristy6d188022011-09-12 13:23:33 +00003259 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristya6d7a9b2012-01-18 20:04:48 +00003260 q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003261 exception);
cristy6d188022011-09-12 13:23:33 +00003262 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003263 {
3264 status=MagickFalse;
3265 continue;
3266 }
cristy3ed852e2009-09-05 21:47:34 +00003267 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00003268 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003269 {
cristy6d188022011-09-12 13:23:33 +00003270 register ssize_t
3271 i;
3272
cristy3ed852e2009-09-05 21:47:34 +00003273 /*
3274 Determine if the pixel is within an ellipse.
3275 */
cristy10a6c612012-01-29 21:41:05 +00003276 if (GetPixelMask(image,p) != 0)
3277 {
3278 p+=GetPixelChannels(image);
3279 q+=GetPixelChannels(implode_image);
3280 continue;
3281 }
cristy3ed852e2009-09-05 21:47:34 +00003282 delta.x=scale.x*(double) (x-center.x);
3283 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00003284 if (distance >= (radius*radius))
cristya6d7a9b2012-01-18 20:04:48 +00003285 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy6d188022011-09-12 13:23:33 +00003286 {
cristya6d7a9b2012-01-18 20:04:48 +00003287 PixelChannel
3288 channel;
3289
cristy1707c6c2012-01-18 23:30:54 +00003290 PixelTrait
3291 implode_traits,
3292 traits;
3293
cristya6d7a9b2012-01-18 20:04:48 +00003294 channel=GetPixelChannelMapChannel(image,i);
cristy1707c6c2012-01-18 23:30:54 +00003295 traits=GetPixelChannelMapTraits(image,channel);
3296 implode_traits=GetPixelChannelMapTraits(implode_image,channel);
3297 if ((traits == UndefinedPixelTrait) ||
3298 (implode_traits == UndefinedPixelTrait))
3299 continue;
cristya6d7a9b2012-01-18 20:04:48 +00003300 SetPixelChannel(implode_image,channel,p[i],q);
cristy6d188022011-09-12 13:23:33 +00003301 }
3302 else
cristy3ed852e2009-09-05 21:47:34 +00003303 {
3304 double
3305 factor;
3306
3307 /*
3308 Implode the pixel.
3309 */
3310 factor=1.0;
3311 if (distance > 0.0)
cristy1707c6c2012-01-18 23:30:54 +00003312 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/radius/
3313 2)),-amount);
cristy76f512e2011-09-12 01:26:56 +00003314 status=InterpolatePixelChannels(image,image_view,implode_image,method,
3315 (double) (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3316 scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00003317 }
cristy6d188022011-09-12 13:23:33 +00003318 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003319 q+=GetPixelChannels(implode_image);
cristy3ed852e2009-09-05 21:47:34 +00003320 }
3321 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3322 status=MagickFalse;
3323 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3324 {
3325 MagickBooleanType
3326 proceed;
3327
cristyb5d5f722009-11-04 03:03:49 +00003328#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003329 #pragma omp critical (MagickCore_ImplodeImage)
cristy3ed852e2009-09-05 21:47:34 +00003330#endif
3331 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3332 if (proceed == MagickFalse)
3333 status=MagickFalse;
3334 }
3335 }
3336 implode_view=DestroyCacheView(implode_view);
3337 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003338 if (status == MagickFalse)
3339 implode_image=DestroyImage(implode_image);
3340 return(implode_image);
3341}
3342
3343/*
3344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3345% %
3346% %
3347% %
3348% M o r p h I m a g e s %
3349% %
3350% %
3351% %
3352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3353%
3354% The MorphImages() method requires a minimum of two images. The first
3355% image is transformed into the second by a number of intervening images
3356% as specified by frames.
3357%
3358% The format of the MorphImage method is:
3359%
cristybb503372010-05-27 20:51:26 +00003360% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003361% ExceptionInfo *exception)
3362%
3363% A description of each parameter follows:
3364%
3365% o image: the image.
3366%
3367% o number_frames: Define the number of in-between image to generate.
3368% The more in-between frames, the smoother the morph.
3369%
3370% o exception: return any errors or warnings in this structure.
3371%
3372*/
3373MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003374 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003375{
3376#define MorphImageTag "Morph/Image"
3377
3378 Image
3379 *morph_image,
3380 *morph_images;
3381
cristy9d314ff2011-03-09 01:30:28 +00003382 MagickBooleanType
3383 status;
cristy3ed852e2009-09-05 21:47:34 +00003384
3385 MagickOffsetType
3386 scene;
3387
3388 MagickRealType
3389 alpha,
3390 beta;
3391
3392 register const Image
3393 *next;
3394
cristybb503372010-05-27 20:51:26 +00003395 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003396 i;
3397
cristy9d314ff2011-03-09 01:30:28 +00003398 ssize_t
3399 y;
cristy3ed852e2009-09-05 21:47:34 +00003400
3401 /*
3402 Clone first frame in sequence.
3403 */
3404 assert(image != (Image *) NULL);
3405 assert(image->signature == MagickSignature);
3406 if (image->debug != MagickFalse)
3407 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3408 assert(exception != (ExceptionInfo *) NULL);
3409 assert(exception->signature == MagickSignature);
3410 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3411 if (morph_images == (Image *) NULL)
3412 return((Image *) NULL);
3413 if (GetNextImageInList(image) == (Image *) NULL)
3414 {
3415 /*
3416 Morph single image.
3417 */
cristybb503372010-05-27 20:51:26 +00003418 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003419 {
3420 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3421 if (morph_image == (Image *) NULL)
3422 {
3423 morph_images=DestroyImageList(morph_images);
3424 return((Image *) NULL);
3425 }
3426 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003427 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003428 {
cristy8b27a6d2010-02-14 03:31:15 +00003429 MagickBooleanType
3430 proceed;
3431
cristybb503372010-05-27 20:51:26 +00003432 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3433 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003434 if (proceed == MagickFalse)
3435 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003436 }
3437 }
3438 return(GetFirstImageInList(morph_images));
3439 }
3440 /*
3441 Morph image sequence.
3442 */
3443 status=MagickTrue;
3444 scene=0;
3445 next=image;
3446 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3447 {
cristybb503372010-05-27 20:51:26 +00003448 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003449 {
3450 CacheView
3451 *image_view,
3452 *morph_view;
3453
3454 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3455 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003456 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristyaa2c16c2012-03-25 22:21:35 +00003457 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*next->rows+beta*
3458 GetNextImageInList(next)->rows+0.5),next->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00003459 if (morph_image == (Image *) NULL)
3460 {
3461 morph_images=DestroyImageList(morph_images);
3462 return((Image *) NULL);
3463 }
cristy1707c6c2012-01-18 23:30:54 +00003464 status=SetImageStorageClass(morph_image,DirectClass,exception);
3465 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003466 {
cristy3ed852e2009-09-05 21:47:34 +00003467 morph_image=DestroyImage(morph_image);
3468 return((Image *) NULL);
3469 }
3470 AppendImageToList(&morph_images,morph_image);
3471 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003472 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
cristyaa2c16c2012-03-25 22:21:35 +00003473 morph_images->rows,GetNextImageInList(next)->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00003474 if (morph_image == (Image *) NULL)
3475 {
3476 morph_images=DestroyImageList(morph_images);
3477 return((Image *) NULL);
3478 }
cristydb070952012-04-20 14:33:00 +00003479 image_view=AcquireVirtualCacheView(morph_image,exception);
3480 morph_view=AcquireAuthenticCacheView(morph_images,exception);
cristyb5d5f722009-11-04 03:03:49 +00003481#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003482 #pragma omp parallel for schedule(static,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003483#endif
cristybb503372010-05-27 20:51:26 +00003484 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003485 {
3486 MagickBooleanType
3487 sync;
3488
cristy4c08aed2011-07-01 19:47:50 +00003489 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003490 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003491
cristybb503372010-05-27 20:51:26 +00003492 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003493 x;
3494
cristy4c08aed2011-07-01 19:47:50 +00003495 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003496 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003497
3498 if (status == MagickFalse)
3499 continue;
3500 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3501 exception);
3502 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3503 exception);
cristy4c08aed2011-07-01 19:47:50 +00003504 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003505 {
3506 status=MagickFalse;
3507 continue;
3508 }
cristybb503372010-05-27 20:51:26 +00003509 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003510 {
cristy1707c6c2012-01-18 23:30:54 +00003511 register ssize_t
3512 i;
3513
cristy177e41c2012-04-15 15:08:25 +00003514 if (GetPixelMask(image,p) != 0)
3515 {
3516 p+=GetPixelChannels(image);
3517 q+=GetPixelChannels(morph_image);
3518 continue;
3519 }
cristy10a6c612012-01-29 21:41:05 +00003520 for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
cristy1707c6c2012-01-18 23:30:54 +00003521 {
3522 PixelChannel
3523 channel;
3524
3525 PixelTrait
3526 morph_traits,
3527 traits;
3528
3529 channel=GetPixelChannelMapChannel(image,i);
3530 traits=GetPixelChannelMapTraits(image,channel);
3531 morph_traits=GetPixelChannelMapTraits(morph_image,channel);
3532 if ((traits == UndefinedPixelTrait) ||
3533 (morph_traits == UndefinedPixelTrait))
3534 continue;
cristy177e41c2012-04-15 15:08:25 +00003535 if ((morph_traits & CopyPixelTrait) != 0)
cristy1707c6c2012-01-18 23:30:54 +00003536 {
3537 SetPixelChannel(morph_image,channel,p[i],q);
3538 continue;
3539 }
3540 SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3541 GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3542 }
cristyed231572011-07-14 02:18:59 +00003543 p+=GetPixelChannels(morph_image);
3544 q+=GetPixelChannels(morph_images);
cristy3ed852e2009-09-05 21:47:34 +00003545 }
3546 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3547 if (sync == MagickFalse)
3548 status=MagickFalse;
3549 }
3550 morph_view=DestroyCacheView(morph_view);
3551 image_view=DestroyCacheView(image_view);
3552 morph_image=DestroyImage(morph_image);
3553 }
cristybb503372010-05-27 20:51:26 +00003554 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003555 break;
3556 /*
3557 Clone last frame in sequence.
3558 */
3559 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3560 if (morph_image == (Image *) NULL)
3561 {
3562 morph_images=DestroyImageList(morph_images);
3563 return((Image *) NULL);
3564 }
3565 AppendImageToList(&morph_images,morph_image);
3566 morph_images=GetLastImageInList(morph_images);
3567 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3568 {
3569 MagickBooleanType
3570 proceed;
3571
cristyb5d5f722009-11-04 03:03:49 +00003572#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00003573 #pragma omp critical (MagickCore_MorphImages)
cristy3ed852e2009-09-05 21:47:34 +00003574#endif
3575 proceed=SetImageProgress(image,MorphImageTag,scene,
3576 GetImageListLength(image));
3577 if (proceed == MagickFalse)
3578 status=MagickFalse;
3579 }
3580 scene++;
3581 }
3582 if (GetNextImageInList(next) != (Image *) NULL)
3583 {
3584 morph_images=DestroyImageList(morph_images);
3585 return((Image *) NULL);
3586 }
3587 return(GetFirstImageInList(morph_images));
3588}
3589
3590/*
3591%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3592% %
3593% %
3594% %
3595% P l a s m a I m a g e %
3596% %
3597% %
3598% %
3599%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3600%
3601% PlasmaImage() initializes an image with plasma fractal values. The image
3602% must be initialized with a base color and the random number generator
3603% seeded before this method is called.
3604%
3605% The format of the PlasmaImage method is:
3606%
3607% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristy5cbc0162011-08-29 00:36:28 +00003608% size_t attenuate,size_t depth,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003609%
3610% A description of each parameter follows:
3611%
3612% o image: the image.
3613%
3614% o segment: Define the region to apply plasma fractals values.
3615%
glennrp7dae1ca2010-09-16 12:17:35 +00003616% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003617%
3618% o depth: Limit the plasma recursion depth.
3619%
cristy5cbc0162011-08-29 00:36:28 +00003620% o exception: return any errors or warnings in this structure.
3621%
cristy3ed852e2009-09-05 21:47:34 +00003622*/
3623
3624static inline Quantum PlasmaPixel(RandomInfo *random_info,
3625 const MagickRealType pixel,const MagickRealType noise)
3626{
3627 Quantum
3628 plasma;
3629
cristyce70c172010-01-07 17:15:30 +00003630 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003631 noise/2.0);
3632 return(plasma);
3633}
3634
cristyda1f9c12011-10-02 21:39:49 +00003635static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
3636 CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3637 const SegmentInfo *segment,size_t attenuate,size_t depth,
3638 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003639{
cristy3ed852e2009-09-05 21:47:34 +00003640 MagickRealType
3641 plasma;
3642
cristyda1f9c12011-10-02 21:39:49 +00003643 PixelChannel
3644 channel;
3645
3646 PixelTrait
3647 traits;
3648
3649 register const Quantum
3650 *restrict u,
3651 *restrict v;
3652
3653 register Quantum
3654 *restrict q;
3655
3656 register ssize_t
3657 i;
cristy3ed852e2009-09-05 21:47:34 +00003658
cristy9d314ff2011-03-09 01:30:28 +00003659 ssize_t
3660 x,
3661 x_mid,
3662 y,
3663 y_mid;
3664
cristy3ed852e2009-09-05 21:47:34 +00003665 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3666 return(MagickTrue);
3667 if (depth != 0)
3668 {
3669 SegmentInfo
3670 local_info;
3671
3672 /*
3673 Divide the area into quadrants and recurse.
3674 */
3675 depth--;
3676 attenuate++;
cristybb503372010-05-27 20:51:26 +00003677 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3678 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003679 local_info=(*segment);
3680 local_info.x2=(double) x_mid;
3681 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003682 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3683 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003684 local_info=(*segment);
3685 local_info.y1=(double) y_mid;
3686 local_info.x2=(double) x_mid;
cristyda1f9c12011-10-02 21:39:49 +00003687 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3688 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003689 local_info=(*segment);
3690 local_info.x1=(double) x_mid;
3691 local_info.y2=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003692 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3693 &local_info,attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003694 local_info=(*segment);
3695 local_info.x1=(double) x_mid;
3696 local_info.y1=(double) y_mid;
cristyda1f9c12011-10-02 21:39:49 +00003697 return(PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3698 &local_info,attenuate,depth,exception));
cristy3ed852e2009-09-05 21:47:34 +00003699 }
cristybb503372010-05-27 20:51:26 +00003700 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3701 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003702 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3703 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3704 return(MagickFalse);
3705 /*
3706 Average pixels and apply plasma.
3707 */
cristy3ed852e2009-09-05 21:47:34 +00003708 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3709 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3710 {
cristy3ed852e2009-09-05 21:47:34 +00003711 /*
3712 Left pixel.
3713 */
cristybb503372010-05-27 20:51:26 +00003714 x=(ssize_t) ceil(segment->x1-0.5);
cristy1707c6c2012-01-18 23:30:54 +00003715 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3716 exception);
3717 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3718 exception);
cristyc5c6f662010-09-22 14:23:02 +00003719 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003720 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3721 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003722 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003723 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3724 {
cristye2a912b2011-12-05 20:02:07 +00003725 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003726 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003727 if (traits == UndefinedPixelTrait)
3728 continue;
3729 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3730 }
cristyc5c6f662010-09-22 14:23:02 +00003731 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003732 if (segment->x1 != segment->x2)
3733 {
3734 /*
3735 Right pixel.
3736 */
cristybb503372010-05-27 20:51:26 +00003737 x=(ssize_t) ceil(segment->x2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003738 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3739 1,1,exception);
3740 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3741 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003742 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003743 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3744 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003745 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003746 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3747 {
cristye2a912b2011-12-05 20:02:07 +00003748 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003749 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003750 if (traits == UndefinedPixelTrait)
3751 continue;
3752 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3753 }
cristyc5c6f662010-09-22 14:23:02 +00003754 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003755 }
3756 }
3757 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3758 {
3759 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3760 {
cristy3ed852e2009-09-05 21:47:34 +00003761 /*
3762 Bottom pixel.
3763 */
cristybb503372010-05-27 20:51:26 +00003764 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003765 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3766 1,1,exception);
3767 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3768 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003769 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003770 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3771 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003772 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003773 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3774 {
cristye2a912b2011-12-05 20:02:07 +00003775 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003776 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003777 if (traits == UndefinedPixelTrait)
3778 continue;
3779 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3780 }
cristyc5c6f662010-09-22 14:23:02 +00003781 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003782 }
3783 if (segment->y1 != segment->y2)
3784 {
cristy3ed852e2009-09-05 21:47:34 +00003785 /*
3786 Top pixel.
3787 */
cristybb503372010-05-27 20:51:26 +00003788 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003789 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3790 1,1,exception);
3791 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3792 1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003793 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003794 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3795 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003796 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003797 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3798 {
cristye2a912b2011-12-05 20:02:07 +00003799 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003800 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003801 if (traits == UndefinedPixelTrait)
3802 continue;
3803 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3804 }
cristyc5c6f662010-09-22 14:23:02 +00003805 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003806 }
3807 }
3808 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3809 {
cristy3ed852e2009-09-05 21:47:34 +00003810 /*
3811 Middle pixel.
3812 */
cristybb503372010-05-27 20:51:26 +00003813 x=(ssize_t) ceil(segment->x1-0.5);
3814 y=(ssize_t) ceil(segment->y1-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003815 u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
cristybb503372010-05-27 20:51:26 +00003816 x=(ssize_t) ceil(segment->x2-0.5);
3817 y=(ssize_t) ceil(segment->y2-0.5);
cristyda1f9c12011-10-02 21:39:49 +00003818 v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
cristyc5c6f662010-09-22 14:23:02 +00003819 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristyda1f9c12011-10-02 21:39:49 +00003820 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3821 (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003822 return(MagickTrue);
cristyda1f9c12011-10-02 21:39:49 +00003823 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3824 {
cristye2a912b2011-12-05 20:02:07 +00003825 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003826 traits=GetPixelChannelMapTraits(image,channel);
cristyda1f9c12011-10-02 21:39:49 +00003827 if (traits == UndefinedPixelTrait)
3828 continue;
3829 q[i]=PlasmaPixel(random_info,(u[channel]+v[channel])/2.0,plasma);
3830 }
cristyc5c6f662010-09-22 14:23:02 +00003831 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003832 }
3833 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3834 return(MagickTrue);
3835 return(MagickFalse);
3836}
cristyda1f9c12011-10-02 21:39:49 +00003837
cristy3ed852e2009-09-05 21:47:34 +00003838MagickExport MagickBooleanType PlasmaImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00003839 const SegmentInfo *segment,size_t attenuate,size_t depth,
3840 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003841{
cristyc5c6f662010-09-22 14:23:02 +00003842 CacheView
cristyda1f9c12011-10-02 21:39:49 +00003843 *image_view,
3844 *u_view,
3845 *v_view;
cristyc5c6f662010-09-22 14:23:02 +00003846
cristy3ed852e2009-09-05 21:47:34 +00003847 MagickBooleanType
3848 status;
3849
3850 RandomInfo
3851 *random_info;
3852
3853 if (image->debug != MagickFalse)
3854 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3855 assert(image != (Image *) NULL);
3856 assert(image->signature == MagickSignature);
3857 if (image->debug != MagickFalse)
3858 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy5cbc0162011-08-29 00:36:28 +00003859 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristyc5c6f662010-09-22 14:23:02 +00003860 return(MagickFalse);
cristydb070952012-04-20 14:33:00 +00003861 image_view=AcquireAuthenticCacheView(image,exception);
3862 u_view=AcquireVirtualCacheView(image,exception);
3863 v_view=AcquireVirtualCacheView(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003864 random_info=AcquireRandomInfo();
cristyda1f9c12011-10-02 21:39:49 +00003865 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
3866 attenuate,depth,exception);
cristy3ed852e2009-09-05 21:47:34 +00003867 random_info=DestroyRandomInfo(random_info);
cristyda1f9c12011-10-02 21:39:49 +00003868 v_view=DestroyCacheView(v_view);
3869 u_view=DestroyCacheView(u_view);
cristyc5c6f662010-09-22 14:23:02 +00003870 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003871 return(status);
3872}
3873
3874/*
3875%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3876% %
3877% %
3878% %
3879% P o l a r o i d I m a g e %
3880% %
3881% %
3882% %
3883%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3884%
3885% PolaroidImage() simulates a Polaroid picture.
3886%
3887% The format of the AnnotateImage method is:
3888%
3889% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003890% const char *caption,const double angle,
3891% const PixelInterpolateMethod method,ExceptionInfo exception)
cristy3ed852e2009-09-05 21:47:34 +00003892%
3893% A description of each parameter follows:
3894%
3895% o image: the image.
3896%
3897% o draw_info: the draw info.
3898%
cristye9e3d382011-12-14 01:50:13 +00003899% o caption: the Polaroid caption.
3900%
cristycee97112010-05-28 00:44:52 +00003901% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003902%
cristy5c4e2582011-09-11 19:21:03 +00003903% o method: the pixel interpolation method.
3904%
cristy3ed852e2009-09-05 21:47:34 +00003905% o exception: return any errors or warnings in this structure.
3906%
3907*/
3908MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
cristye9e3d382011-12-14 01:50:13 +00003909 const char *caption,const double angle,const PixelInterpolateMethod method,
cristy5c4e2582011-09-11 19:21:03 +00003910 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003911{
cristy3ed852e2009-09-05 21:47:34 +00003912 Image
3913 *bend_image,
3914 *caption_image,
3915 *flop_image,
3916 *picture_image,
3917 *polaroid_image,
3918 *rotate_image,
3919 *trim_image;
3920
cristybb503372010-05-27 20:51:26 +00003921 size_t
cristy3ed852e2009-09-05 21:47:34 +00003922 height;
3923
cristy9d314ff2011-03-09 01:30:28 +00003924 ssize_t
3925 quantum;
3926
cristy3ed852e2009-09-05 21:47:34 +00003927 /*
3928 Simulate a Polaroid picture.
3929 */
3930 assert(image != (Image *) NULL);
3931 assert(image->signature == MagickSignature);
3932 if (image->debug != MagickFalse)
3933 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3934 assert(exception != (ExceptionInfo *) NULL);
3935 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003936 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003937 image->rows)/25.0,10.0);
3938 height=image->rows+2*quantum;
3939 caption_image=(Image *) NULL;
cristye9e3d382011-12-14 01:50:13 +00003940 if (caption != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003941 {
3942 char
cristye9e3d382011-12-14 01:50:13 +00003943 geometry[MaxTextExtent],
3944 *text;
cristy3ed852e2009-09-05 21:47:34 +00003945
3946 DrawInfo
3947 *annotate_info;
3948
cristy3ed852e2009-09-05 21:47:34 +00003949 MagickBooleanType
3950 status;
3951
cristy9d314ff2011-03-09 01:30:28 +00003952 ssize_t
3953 count;
3954
cristy3ed852e2009-09-05 21:47:34 +00003955 TypeMetric
3956 metrics;
3957
3958 /*
3959 Generate caption image.
3960 */
3961 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3962 if (caption_image == (Image *) NULL)
3963 return((Image *) NULL);
3964 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
cristye9e3d382011-12-14 01:50:13 +00003965 text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
3966 exception);
3967 (void) CloneString(&annotate_info->text,text);
cristy6b1d05e2010-09-22 19:17:27 +00003968 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
cristye9e3d382011-12-14 01:50:13 +00003969 &text,exception);
3970 status=SetImageExtent(caption_image,image->columns,(size_t) ((count+1)*
3971 (metrics.ascent-metrics.descent)+0.5),exception);
cristy3ed852e2009-09-05 21:47:34 +00003972 if (status == MagickFalse)
3973 caption_image=DestroyImage(caption_image);
3974 else
3975 {
3976 caption_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00003977 (void) SetImageBackgroundColor(caption_image,exception);
cristye9e3d382011-12-14 01:50:13 +00003978 (void) CloneString(&annotate_info->text,text);
cristyb51dff52011-05-19 16:55:47 +00003979 (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00003980 metrics.ascent);
3981 if (annotate_info->gravity == UndefinedGravity)
3982 (void) CloneString(&annotate_info->geometry,AcquireString(
3983 geometry));
cristy5cbc0162011-08-29 00:36:28 +00003984 (void) AnnotateImage(caption_image,annotate_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003985 height+=caption_image->rows;
3986 }
3987 annotate_info=DestroyDrawInfo(annotate_info);
cristye9e3d382011-12-14 01:50:13 +00003988 text=DestroyString(text);
cristy3ed852e2009-09-05 21:47:34 +00003989 }
3990 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3991 exception);
3992 if (picture_image == (Image *) NULL)
3993 {
3994 if (caption_image != (Image *) NULL)
3995 caption_image=DestroyImage(caption_image);
3996 return((Image *) NULL);
3997 }
3998 picture_image->background_color=image->border_color;
cristyea1a8aa2011-10-20 13:24:06 +00003999 (void) SetImageBackgroundColor(picture_image,exception);
cristy39172402012-03-30 13:04:39 +00004000 (void) CompositeImage(picture_image,image,OverCompositeOp,MagickTrue,quantum,
cristyfeb3e962012-03-29 17:25:55 +00004001 quantum,exception);
cristy3ed852e2009-09-05 21:47:34 +00004002 if (caption_image != (Image *) NULL)
4003 {
cristyfeb3e962012-03-29 17:25:55 +00004004 (void) CompositeImage(picture_image,caption_image,OverCompositeOp,
cristy39172402012-03-30 13:04:39 +00004005 MagickTrue,quantum,(ssize_t) (image->rows+3*quantum/2),exception);
cristy3ed852e2009-09-05 21:47:34 +00004006 caption_image=DestroyImage(caption_image);
4007 }
cristy9950d572011-10-01 18:22:35 +00004008 (void) QueryColorCompliance("none",AllCompliance,
4009 &picture_image->background_color,exception);
cristy63240882011-08-05 19:05:27 +00004010 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004011 rotate_image=RotateImage(picture_image,90.0,exception);
4012 picture_image=DestroyImage(picture_image);
4013 if (rotate_image == (Image *) NULL)
4014 return((Image *) NULL);
4015 picture_image=rotate_image;
4016 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
cristy5c4e2582011-09-11 19:21:03 +00004017 picture_image->columns,method,exception);
cristy3ed852e2009-09-05 21:47:34 +00004018 picture_image=DestroyImage(picture_image);
4019 if (bend_image == (Image *) NULL)
4020 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004021 picture_image=bend_image;
4022 rotate_image=RotateImage(picture_image,-90.0,exception);
4023 picture_image=DestroyImage(picture_image);
4024 if (rotate_image == (Image *) NULL)
4025 return((Image *) NULL);
4026 picture_image=rotate_image;
4027 picture_image->background_color=image->background_color;
anthonyf46d4262012-03-26 03:30:34 +00004028 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
cristy3ed852e2009-09-05 21:47:34 +00004029 exception);
4030 if (polaroid_image == (Image *) NULL)
4031 {
4032 picture_image=DestroyImage(picture_image);
4033 return(picture_image);
4034 }
4035 flop_image=FlopImage(polaroid_image,exception);
4036 polaroid_image=DestroyImage(polaroid_image);
4037 if (flop_image == (Image *) NULL)
4038 {
4039 picture_image=DestroyImage(picture_image);
4040 return(picture_image);
4041 }
4042 polaroid_image=flop_image;
cristyfeb3e962012-03-29 17:25:55 +00004043 (void) CompositeImage(polaroid_image,picture_image,OverCompositeOp,
cristy39172402012-03-30 13:04:39 +00004044 MagickTrue,(ssize_t) (-0.01*picture_image->columns/2.0),0L,exception);
cristy3ed852e2009-09-05 21:47:34 +00004045 picture_image=DestroyImage(picture_image);
cristy9950d572011-10-01 18:22:35 +00004046 (void) QueryColorCompliance("none",AllCompliance,
4047 &polaroid_image->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00004048 rotate_image=RotateImage(polaroid_image,angle,exception);
4049 polaroid_image=DestroyImage(polaroid_image);
4050 if (rotate_image == (Image *) NULL)
4051 return((Image *) NULL);
4052 polaroid_image=rotate_image;
4053 trim_image=TrimImage(polaroid_image,exception);
4054 polaroid_image=DestroyImage(polaroid_image);
4055 if (trim_image == (Image *) NULL)
4056 return((Image *) NULL);
4057 polaroid_image=trim_image;
4058 return(polaroid_image);
4059}
4060
4061/*
4062%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4063% %
4064% %
4065% %
cristy3ed852e2009-09-05 21:47:34 +00004066% S e p i a T o n e I m a g e %
4067% %
4068% %
4069% %
4070%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4071%
4072% MagickSepiaToneImage() applies a special effect to the image, similar to the
4073% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4074% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4075% threshold of 80% is a good starting point for a reasonable tone.
4076%
4077% The format of the SepiaToneImage method is:
4078%
4079% Image *SepiaToneImage(const Image *image,const double threshold,
4080% ExceptionInfo *exception)
4081%
4082% A description of each parameter follows:
4083%
4084% o image: the image.
4085%
4086% o threshold: the tone threshold.
4087%
4088% o exception: return any errors or warnings in this structure.
4089%
4090*/
4091MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4092 ExceptionInfo *exception)
4093{
4094#define SepiaToneImageTag "SepiaTone/Image"
4095
cristyc4c8d132010-01-07 01:58:38 +00004096 CacheView
4097 *image_view,
4098 *sepia_view;
4099
cristy3ed852e2009-09-05 21:47:34 +00004100 Image
4101 *sepia_image;
4102
cristy3ed852e2009-09-05 21:47:34 +00004103 MagickBooleanType
4104 status;
4105
cristybb503372010-05-27 20:51:26 +00004106 MagickOffsetType
4107 progress;
4108
4109 ssize_t
4110 y;
4111
cristy3ed852e2009-09-05 21:47:34 +00004112 /*
4113 Initialize sepia-toned image attributes.
4114 */
4115 assert(image != (const Image *) NULL);
4116 assert(image->signature == MagickSignature);
4117 if (image->debug != MagickFalse)
4118 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4119 assert(exception != (ExceptionInfo *) NULL);
4120 assert(exception->signature == MagickSignature);
cristy1707c6c2012-01-18 23:30:54 +00004121 sepia_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004122 if (sepia_image == (Image *) NULL)
4123 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004124 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004125 {
cristy3ed852e2009-09-05 21:47:34 +00004126 sepia_image=DestroyImage(sepia_image);
4127 return((Image *) NULL);
4128 }
4129 /*
4130 Tone each row of the image.
4131 */
4132 status=MagickTrue;
4133 progress=0;
cristydb070952012-04-20 14:33:00 +00004134 image_view=AcquireVirtualCacheView(image,exception);
4135 sepia_view=AcquireAuthenticCacheView(sepia_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00004136#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004137 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004138#endif
cristybb503372010-05-27 20:51:26 +00004139 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004140 {
cristy4c08aed2011-07-01 19:47:50 +00004141 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004142 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004143
cristybb503372010-05-27 20:51:26 +00004144 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004145 x;
4146
cristy4c08aed2011-07-01 19:47:50 +00004147 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004148 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004149
4150 if (status == MagickFalse)
4151 continue;
4152 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00004153 q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004154 exception);
cristy4c08aed2011-07-01 19:47:50 +00004155 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004156 {
4157 status=MagickFalse;
4158 continue;
4159 }
cristybb503372010-05-27 20:51:26 +00004160 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004161 {
4162 MagickRealType
4163 intensity,
4164 tone;
4165
cristy4c08aed2011-07-01 19:47:50 +00004166 intensity=(MagickRealType) GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004167 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4168 (MagickRealType) QuantumRange-threshold;
cristy4c08aed2011-07-01 19:47:50 +00004169 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004170 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4171 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004172 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004173 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristy4c08aed2011-07-01 19:47:50 +00004174 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristy3ed852e2009-09-05 21:47:34 +00004175 tone=threshold/7.0;
cristy4c08aed2011-07-01 19:47:50 +00004176 if ((MagickRealType) GetPixelGreen(image,q) < tone)
4177 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4178 if ((MagickRealType) GetPixelBlue(image,q) < tone)
4179 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
cristyed231572011-07-14 02:18:59 +00004180 p+=GetPixelChannels(image);
4181 q+=GetPixelChannels(sepia_image);
cristy3ed852e2009-09-05 21:47:34 +00004182 }
4183 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4184 status=MagickFalse;
4185 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4186 {
4187 MagickBooleanType
4188 proceed;
4189
cristyb5d5f722009-11-04 03:03:49 +00004190#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004191 #pragma omp critical (MagickCore_SepiaToneImage)
cristy3ed852e2009-09-05 21:47:34 +00004192#endif
4193 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4194 image->rows);
4195 if (proceed == MagickFalse)
4196 status=MagickFalse;
4197 }
4198 }
4199 sepia_view=DestroyCacheView(sepia_view);
4200 image_view=DestroyCacheView(image_view);
cristye23ec9d2011-08-16 18:15:40 +00004201 (void) NormalizeImage(sepia_image,exception);
4202 (void) ContrastImage(sepia_image,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004203 if (status == MagickFalse)
4204 sepia_image=DestroyImage(sepia_image);
4205 return(sepia_image);
4206}
4207
4208/*
4209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4210% %
4211% %
4212% %
4213% S h a d o w I m a g e %
4214% %
4215% %
4216% %
4217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4218%
4219% ShadowImage() simulates a shadow from the specified image and returns it.
4220%
4221% The format of the ShadowImage method is:
4222%
cristy70cddf72011-12-10 22:42:42 +00004223% Image *ShadowImage(const Image *image,const double alpha,
cristyaa2c16c2012-03-25 22:21:35 +00004224% const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4225% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004226%
4227% A description of each parameter follows:
4228%
4229% o image: the image.
4230%
cristy70cddf72011-12-10 22:42:42 +00004231% o alpha: percentage transparency.
cristy3ed852e2009-09-05 21:47:34 +00004232%
4233% o sigma: the standard deviation of the Gaussian, in pixels.
4234%
4235% o x_offset: the shadow x-offset.
4236%
4237% o y_offset: the shadow y-offset.
4238%
4239% o exception: return any errors or warnings in this structure.
4240%
4241*/
cristy70cddf72011-12-10 22:42:42 +00004242MagickExport Image *ShadowImage(const Image *image,const double alpha,
cristyaa2c16c2012-03-25 22:21:35 +00004243 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4244 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004245{
4246#define ShadowImageTag "Shadow/Image"
4247
cristy70cddf72011-12-10 22:42:42 +00004248 CacheView
4249 *image_view;
4250
cristybd5a96c2011-08-21 00:04:26 +00004251 ChannelType
4252 channel_mask;
4253
cristy3ed852e2009-09-05 21:47:34 +00004254 Image
4255 *border_image,
4256 *clone_image,
4257 *shadow_image;
4258
cristy70cddf72011-12-10 22:42:42 +00004259 MagickBooleanType
4260 status;
4261
cristy3ed852e2009-09-05 21:47:34 +00004262 RectangleInfo
4263 border_info;
4264
cristy70cddf72011-12-10 22:42:42 +00004265 ssize_t
4266 y;
4267
cristy3ed852e2009-09-05 21:47:34 +00004268 assert(image != (Image *) NULL);
4269 assert(image->signature == MagickSignature);
4270 if (image->debug != MagickFalse)
4271 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4272 assert(exception != (ExceptionInfo *) NULL);
4273 assert(exception->signature == MagickSignature);
4274 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4275 if (clone_image == (Image *) NULL)
4276 return((Image *) NULL);
cristy0898eba2012-04-09 16:38:29 +00004277 if (IsGrayColorspace(image->colorspace) != MagickFalse)
4278 (void) TransformImageColorspace(clone_image,sRGBColorspace,exception);
cristy387430f2012-02-07 13:09:46 +00004279 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod,
4280 exception);
cristybb503372010-05-27 20:51:26 +00004281 border_info.width=(size_t) floor(2.0*sigma+0.5);
4282 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004283 border_info.x=0;
4284 border_info.y=0;
cristy9950d572011-10-01 18:22:35 +00004285 (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4286 exception);
cristy70cddf72011-12-10 22:42:42 +00004287 clone_image->matte=MagickTrue;
4288 border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
cristy3ed852e2009-09-05 21:47:34 +00004289 clone_image=DestroyImage(clone_image);
4290 if (border_image == (Image *) NULL)
4291 return((Image *) NULL);
4292 if (border_image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +00004293 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +00004294 /*
4295 Shadow image.
4296 */
cristy70cddf72011-12-10 22:42:42 +00004297 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +00004298 image_view=AcquireAuthenticCacheView(border_image,exception);
cristy70cddf72011-12-10 22:42:42 +00004299 for (y=0; y < (ssize_t) border_image->rows; y++)
4300 {
4301 PixelInfo
4302 background_color;
4303
4304 register Quantum
4305 *restrict q;
4306
4307 register ssize_t
4308 x;
4309
4310 if (status == MagickFalse)
4311 continue;
4312 q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4313 exception);
4314 if (q == (Quantum *) NULL)
4315 {
4316 status=MagickFalse;
4317 continue;
4318 }
4319 background_color=border_image->background_color;
4320 background_color.matte=MagickTrue;
4321 for (x=0; x < (ssize_t) border_image->columns; x++)
4322 {
4323 if (border_image->matte != MagickFalse)
4324 background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4325 SetPixelInfoPixel(border_image,&background_color,q);
4326 q+=GetPixelChannels(border_image);
4327 }
4328 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4329 status=MagickFalse;
4330 }
4331 image_view=DestroyCacheView(image_view);
4332 if (status == MagickFalse)
4333 {
4334 border_image=DestroyImage(border_image);
4335 return((Image *) NULL);
4336 }
cristybd5a96c2011-08-21 00:04:26 +00004337 channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
cristyaa2c16c2012-03-25 22:21:35 +00004338 shadow_image=BlurImage(border_image,0.0,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00004339 border_image=DestroyImage(border_image);
4340 if (shadow_image == (Image *) NULL)
4341 return((Image *) NULL);
cristyae1969f2011-12-10 03:07:36 +00004342 (void) SetPixelChannelMapMask(shadow_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +00004343 if (shadow_image->page.width == 0)
4344 shadow_image->page.width=shadow_image->columns;
4345 if (shadow_image->page.height == 0)
4346 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004347 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4348 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4349 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4350 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004351 return(shadow_image);
4352}
4353
4354/*
4355%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4356% %
4357% %
4358% %
4359% S k e t c h I m a g e %
4360% %
4361% %
4362% %
4363%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4364%
4365% SketchImage() simulates a pencil sketch. We convolve the image with a
4366% Gaussian operator of the given radius and standard deviation (sigma). For
4367% reasonable results, radius should be larger than sigma. Use a radius of 0
4368% and SketchImage() selects a suitable radius for you. Angle gives the angle
4369% of the sketch.
4370%
4371% The format of the SketchImage method is:
4372%
4373% Image *SketchImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00004374% const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004375%
4376% A description of each parameter follows:
4377%
4378% o image: the image.
4379%
cristy574cc262011-08-05 01:23:58 +00004380% o radius: the radius of the Gaussian, in pixels, not counting the
4381% center pixel.
cristy3ed852e2009-09-05 21:47:34 +00004382%
4383% o sigma: the standard deviation of the Gaussian, in pixels.
4384%
cristyf7ef0252011-09-09 14:50:06 +00004385% o angle: apply the effect along this angle.
4386%
cristy3ed852e2009-09-05 21:47:34 +00004387% o exception: return any errors or warnings in this structure.
4388%
4389*/
4390MagickExport Image *SketchImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00004391 const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004392{
cristyfa112112010-01-04 17:48:07 +00004393 CacheView
4394 *random_view;
4395
cristy3ed852e2009-09-05 21:47:34 +00004396 Image
4397 *blend_image,
4398 *blur_image,
4399 *dodge_image,
4400 *random_image,
4401 *sketch_image;
4402
cristy3ed852e2009-09-05 21:47:34 +00004403 MagickBooleanType
cristye7452652012-04-14 01:34:21 +00004404 concurrent,
cristy3ed852e2009-09-05 21:47:34 +00004405 status;
4406
cristy3ed852e2009-09-05 21:47:34 +00004407 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004408 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004409
cristy9d314ff2011-03-09 01:30:28 +00004410 ssize_t
4411 y;
4412
cristy3ed852e2009-09-05 21:47:34 +00004413 /*
4414 Sketch image.
4415 */
4416 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4417 MagickTrue,exception);
4418 if (random_image == (Image *) NULL)
4419 return((Image *) NULL);
4420 status=MagickTrue;
cristy1b784432009-12-19 02:20:40 +00004421 random_info=AcquireRandomInfoThreadSet();
cristye7452652012-04-14 01:34:21 +00004422 concurrent=GetRandomSecretKey(random_info[0]) == ~0UL ? MagickTrue :
4423 MagickFalse;
cristydb070952012-04-20 14:33:00 +00004424 random_view=AcquireAuthenticCacheView(random_image,exception);
cristy1b784432009-12-19 02:20:40 +00004425#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye7452652012-04-14 01:34:21 +00004426 #pragma omp parallel for schedule(static,4) shared(status) omp_concurrent(concurrent)
cristy1b784432009-12-19 02:20:40 +00004427#endif
cristybb503372010-05-27 20:51:26 +00004428 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004429 {
cristy5c9e6f22010-09-17 17:31:01 +00004430 const int
4431 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004432
cristybb503372010-05-27 20:51:26 +00004433 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004434 x;
4435
cristy4c08aed2011-07-01 19:47:50 +00004436 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004437 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004438
cristy1b784432009-12-19 02:20:40 +00004439 if (status == MagickFalse)
4440 continue;
cristy3ed852e2009-09-05 21:47:34 +00004441 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4442 exception);
cristyacd2ed22011-08-30 01:44:23 +00004443 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004444 {
4445 status=MagickFalse;
4446 continue;
4447 }
cristybb503372010-05-27 20:51:26 +00004448 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004449 {
cristy76f512e2011-09-12 01:26:56 +00004450 MagickRealType
4451 value;
4452
4453 register ssize_t
4454 i;
4455
cristy10a6c612012-01-29 21:41:05 +00004456 if (GetPixelMask(random_image,q) != 0)
4457 {
4458 q+=GetPixelChannels(random_image);
4459 continue;
4460 }
cristy76f512e2011-09-12 01:26:56 +00004461 value=GetPseudoRandomValue(random_info[id]);
4462 for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4463 {
cristyabace412011-12-11 15:56:53 +00004464 PixelChannel
4465 channel;
4466
cristy76f512e2011-09-12 01:26:56 +00004467 PixelTrait
4468 traits;
4469
cristyabace412011-12-11 15:56:53 +00004470 channel=GetPixelChannelMapChannel(image,i);
4471 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004472 if (traits == UndefinedPixelTrait)
4473 continue;
4474 q[i]=ClampToQuantum(QuantumRange*value);
4475 }
cristyed231572011-07-14 02:18:59 +00004476 q+=GetPixelChannels(random_image);
cristy3ed852e2009-09-05 21:47:34 +00004477 }
4478 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4479 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004480 }
4481 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004482 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004483 if (status == MagickFalse)
4484 {
4485 random_image=DestroyImage(random_image);
4486 return(random_image);
4487 }
cristyaa2c16c2012-03-25 22:21:35 +00004488 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
cristy3ed852e2009-09-05 21:47:34 +00004489 random_image=DestroyImage(random_image);
4490 if (blur_image == (Image *) NULL)
4491 return((Image *) NULL);
cristy6bfd6902011-12-09 01:33:45 +00004492 dodge_image=EdgeImage(blur_image,radius,1.0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004493 blur_image=DestroyImage(blur_image);
4494 if (dodge_image == (Image *) NULL)
4495 return((Image *) NULL);
cristye23ec9d2011-08-16 18:15:40 +00004496 (void) NormalizeImage(dodge_image,exception);
cristyb3e7c6c2011-07-24 01:43:55 +00004497 (void) NegateImage(dodge_image,MagickFalse,exception);
cristye941a752011-10-15 01:52:48 +00004498 (void) TransformImage(&dodge_image,(char *) NULL,"50%",exception);
cristy3ed852e2009-09-05 21:47:34 +00004499 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4500 if (sketch_image == (Image *) NULL)
4501 {
4502 dodge_image=DestroyImage(dodge_image);
4503 return((Image *) NULL);
4504 }
cristyfeb3e962012-03-29 17:25:55 +00004505 (void) CompositeImage(sketch_image,dodge_image,ColorDodgeCompositeOp,
cristy39172402012-03-30 13:04:39 +00004506 MagickTrue,0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004507 dodge_image=DestroyImage(dodge_image);
4508 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4509 if (blend_image == (Image *) NULL)
4510 {
4511 sketch_image=DestroyImage(sketch_image);
4512 return((Image *) NULL);
4513 }
4514 (void) SetImageArtifact(blend_image,"compose:args","20x80");
cristy39172402012-03-30 13:04:39 +00004515 (void) CompositeImage(sketch_image,blend_image,BlendCompositeOp,MagickTrue,
cristyfeb3e962012-03-29 17:25:55 +00004516 0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00004517 blend_image=DestroyImage(blend_image);
4518 return(sketch_image);
4519}
4520
4521/*
4522%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4523% %
4524% %
4525% %
4526% S o l a r i z e I m a g e %
4527% %
4528% %
4529% %
4530%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4531%
4532% SolarizeImage() applies a special effect to the image, similar to the effect
4533% achieved in a photo darkroom by selectively exposing areas of photo
4534% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4535% measure of the extent of the solarization.
4536%
4537% The format of the SolarizeImage method is:
4538%
cristy5cbc0162011-08-29 00:36:28 +00004539% MagickBooleanType SolarizeImage(Image *image,const double threshold,
4540% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004541%
4542% A description of each parameter follows:
4543%
4544% o image: the image.
4545%
4546% o threshold: Define the extent of the solarization.
4547%
cristy5cbc0162011-08-29 00:36:28 +00004548% o exception: return any errors or warnings in this structure.
4549%
cristy3ed852e2009-09-05 21:47:34 +00004550*/
4551MagickExport MagickBooleanType SolarizeImage(Image *image,
cristy5cbc0162011-08-29 00:36:28 +00004552 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004553{
4554#define SolarizeImageTag "Solarize/Image"
4555
cristyc4c8d132010-01-07 01:58:38 +00004556 CacheView
4557 *image_view;
4558
cristy3ed852e2009-09-05 21:47:34 +00004559 MagickBooleanType
4560 status;
4561
cristybb503372010-05-27 20:51:26 +00004562 MagickOffsetType
4563 progress;
4564
4565 ssize_t
4566 y;
4567
cristy3ed852e2009-09-05 21:47:34 +00004568 assert(image != (Image *) NULL);
4569 assert(image->signature == MagickSignature);
4570 if (image->debug != MagickFalse)
4571 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4572 if (image->storage_class == PseudoClass)
4573 {
cristybb503372010-05-27 20:51:26 +00004574 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004575 i;
4576
4577 /*
4578 Solarize colormap.
4579 */
cristybb503372010-05-27 20:51:26 +00004580 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004581 {
4582 if ((MagickRealType) image->colormap[i].red > threshold)
4583 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4584 if ((MagickRealType) image->colormap[i].green > threshold)
4585 image->colormap[i].green=(Quantum) QuantumRange-
4586 image->colormap[i].green;
4587 if ((MagickRealType) image->colormap[i].blue > threshold)
4588 image->colormap[i].blue=(Quantum) QuantumRange-
4589 image->colormap[i].blue;
4590 }
4591 }
4592 /*
4593 Solarize image.
4594 */
4595 status=MagickTrue;
4596 progress=0;
cristydb070952012-04-20 14:33:00 +00004597 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00004598#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00004599 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004600#endif
cristybb503372010-05-27 20:51:26 +00004601 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004602 {
cristybb503372010-05-27 20:51:26 +00004603 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004604 x;
4605
cristy4c08aed2011-07-01 19:47:50 +00004606 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004607 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004608
4609 if (status == MagickFalse)
4610 continue;
cristy5cbc0162011-08-29 00:36:28 +00004611 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00004612 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004613 {
4614 status=MagickFalse;
4615 continue;
4616 }
cristybb503372010-05-27 20:51:26 +00004617 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004618 {
cristy76f512e2011-09-12 01:26:56 +00004619 register ssize_t
4620 i;
4621
cristy10a6c612012-01-29 21:41:05 +00004622 if (GetPixelMask(image,q) != 0)
4623 {
4624 q+=GetPixelChannels(image);
4625 continue;
4626 }
cristy76f512e2011-09-12 01:26:56 +00004627 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4628 {
cristyabace412011-12-11 15:56:53 +00004629 PixelChannel
4630 channel;
4631
cristy76f512e2011-09-12 01:26:56 +00004632 PixelTrait
4633 traits;
4634
cristyabace412011-12-11 15:56:53 +00004635 channel=GetPixelChannelMapChannel(image,i);
4636 traits=GetPixelChannelMapTraits(image,channel);
cristy76f512e2011-09-12 01:26:56 +00004637 if ((traits == UndefinedPixelTrait) ||
4638 ((traits & CopyPixelTrait) != 0))
4639 continue;
4640 if ((MagickRealType) q[i] > threshold)
4641 q[i]=QuantumRange-q[i];
4642 }
cristyed231572011-07-14 02:18:59 +00004643 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00004644 }
4645 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4646 status=MagickFalse;
4647 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4648 {
4649 MagickBooleanType
4650 proceed;
4651
cristyb5d5f722009-11-04 03:03:49 +00004652#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00004653 #pragma omp critical (MagickCore_SolarizeImage)
cristy3ed852e2009-09-05 21:47:34 +00004654#endif
4655 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4656 if (proceed == MagickFalse)
4657 status=MagickFalse;
4658 }
4659 }
4660 image_view=DestroyCacheView(image_view);
4661 return(status);
4662}
4663
4664/*
4665%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4666% %
4667% %
4668% %
4669% S t e g a n o I m a g e %
4670% %
4671% %
4672% %
4673%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4674%
4675% SteganoImage() hides a digital watermark within the image. Recover
4676% the hidden watermark later to prove that the authenticity of an image.
4677% Offset defines the start position within the image to hide the watermark.
4678%
4679% The format of the SteganoImage method is:
4680%
4681% Image *SteganoImage(const Image *image,Image *watermark,
4682% ExceptionInfo *exception)
4683%
4684% A description of each parameter follows:
4685%
4686% o image: the image.
4687%
4688% o watermark: the watermark image.
4689%
4690% o exception: return any errors or warnings in this structure.
4691%
4692*/
4693MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4694 ExceptionInfo *exception)
4695{
cristye1bf8ad2010-09-19 17:07:03 +00004696#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004697#define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
cristyeaedf062010-05-29 22:36:02 +00004698 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004699#define SteganoImageTag "Stegano/Image"
4700
cristyb0d3bb92010-09-22 14:37:58 +00004701 CacheView
4702 *stegano_view,
4703 *watermark_view;
4704
cristy3ed852e2009-09-05 21:47:34 +00004705 Image
4706 *stegano_image;
4707
4708 int
4709 c;
4710
cristy3ed852e2009-09-05 21:47:34 +00004711 MagickBooleanType
4712 status;
4713
cristy101ab702011-10-13 13:06:32 +00004714 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004715 pixel;
4716
cristy4c08aed2011-07-01 19:47:50 +00004717 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004718 *q;
4719
cristye1bf8ad2010-09-19 17:07:03 +00004720 register ssize_t
4721 x;
4722
cristybb503372010-05-27 20:51:26 +00004723 size_t
cristyeaedf062010-05-29 22:36:02 +00004724 depth,
4725 one;
cristy3ed852e2009-09-05 21:47:34 +00004726
cristye1bf8ad2010-09-19 17:07:03 +00004727 ssize_t
4728 i,
4729 j,
4730 k,
4731 y;
4732
cristy3ed852e2009-09-05 21:47:34 +00004733 /*
4734 Initialize steganographic image attributes.
4735 */
4736 assert(image != (const Image *) NULL);
4737 assert(image->signature == MagickSignature);
4738 if (image->debug != MagickFalse)
4739 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4740 assert(watermark != (const Image *) NULL);
4741 assert(watermark->signature == MagickSignature);
4742 assert(exception != (ExceptionInfo *) NULL);
4743 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004744 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004745 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4746 if (stegano_image == (Image *) NULL)
4747 return((Image *) NULL);
cristyf61b1832012-04-01 01:38:19 +00004748 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
cristy574cc262011-08-05 01:23:58 +00004749 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004750 {
cristy3ed852e2009-09-05 21:47:34 +00004751 stegano_image=DestroyImage(stegano_image);
4752 return((Image *) NULL);
4753 }
cristy3ed852e2009-09-05 21:47:34 +00004754 /*
4755 Hide watermark in low-order bits of image.
4756 */
4757 c=0;
4758 i=0;
4759 j=0;
4760 depth=stegano_image->depth;
cristyf61b1832012-04-01 01:38:19 +00004761 k=stegano_image->offset;
cristyda16f162011-02-19 23:52:17 +00004762 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +00004763 watermark_view=AcquireVirtualCacheView(watermark,exception);
4764 stegano_view=AcquireAuthenticCacheView(stegano_image,exception);
cristybb503372010-05-27 20:51:26 +00004765 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004766 {
cristybb503372010-05-27 20:51:26 +00004767 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004768 {
cristybb503372010-05-27 20:51:26 +00004769 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004770 {
cristy1707c6c2012-01-18 23:30:54 +00004771 ssize_t
4772 offset;
4773
cristyf05d4942012-03-17 16:26:09 +00004774 (void) GetOneCacheViewVirtualPixelInfo(watermark_view,x,y,&pixel,
cristyda1f9c12011-10-02 21:39:49 +00004775 exception);
cristy1707c6c2012-01-18 23:30:54 +00004776 offset=k/(ssize_t) stegano_image->columns;
4777 if (offset >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004778 break;
cristyb0d3bb92010-09-22 14:37:58 +00004779 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4780 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4781 exception);
cristyacd2ed22011-08-30 01:44:23 +00004782 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004783 break;
4784 switch (c)
4785 {
4786 case 0:
4787 {
cristyf61b1832012-04-01 01:38:19 +00004788 SetPixelRed(stegano_image,SetBit(GetPixelRed(stegano_image,q),j,
4789 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004790 break;
4791 }
4792 case 1:
4793 {
cristyf61b1832012-04-01 01:38:19 +00004794 SetPixelGreen(stegano_image,SetBit(GetPixelGreen(stegano_image,q),j,
4795 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004796 break;
4797 }
4798 case 2:
4799 {
cristyf61b1832012-04-01 01:38:19 +00004800 SetPixelBlue(stegano_image,SetBit(GetPixelBlue(stegano_image,q),j,
4801 GetBit(GetPixelInfoIntensity(&pixel),i)),q);
cristy3ed852e2009-09-05 21:47:34 +00004802 break;
4803 }
4804 }
cristyb0d3bb92010-09-22 14:37:58 +00004805 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004806 break;
4807 c++;
4808 if (c == 3)
4809 c=0;
4810 k++;
cristybb503372010-05-27 20:51:26 +00004811 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004812 k=0;
cristyf61b1832012-04-01 01:38:19 +00004813 if (k == stegano_image->offset)
cristy3ed852e2009-09-05 21:47:34 +00004814 j++;
4815 }
4816 }
cristy8b27a6d2010-02-14 03:31:15 +00004817 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004818 {
cristy8b27a6d2010-02-14 03:31:15 +00004819 MagickBooleanType
4820 proceed;
4821
4822 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4823 (depth-i),depth);
4824 if (proceed == MagickFalse)
4825 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004826 }
4827 }
cristyb0d3bb92010-09-22 14:37:58 +00004828 stegano_view=DestroyCacheView(stegano_view);
4829 watermark_view=DestroyCacheView(watermark_view);
cristyda16f162011-02-19 23:52:17 +00004830 if (status == MagickFalse)
4831 {
4832 stegano_image=DestroyImage(stegano_image);
4833 return((Image *) NULL);
4834 }
cristy3ed852e2009-09-05 21:47:34 +00004835 return(stegano_image);
4836}
4837
4838/*
4839%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4840% %
4841% %
4842% %
4843% S t e r e o A n a g l y p h I m a g e %
4844% %
4845% %
4846% %
4847%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4848%
4849% StereoAnaglyphImage() combines two images and produces a single image that
4850% is the composite of a left and right image of a stereo pair. Special
4851% red-green stereo glasses are required to view this effect.
4852%
4853% The format of the StereoAnaglyphImage method is:
4854%
4855% Image *StereoImage(const Image *left_image,const Image *right_image,
4856% ExceptionInfo *exception)
4857% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004858% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004859% ExceptionInfo *exception)
4860%
4861% A description of each parameter follows:
4862%
4863% o left_image: the left image.
4864%
4865% o right_image: the right image.
4866%
4867% o exception: return any errors or warnings in this structure.
4868%
4869% o x_offset: amount, in pixels, by which the left image is offset to the
4870% right of the right image.
4871%
4872% o y_offset: amount, in pixels, by which the left image is offset to the
4873% bottom of the right image.
4874%
4875%
4876*/
4877MagickExport Image *StereoImage(const Image *left_image,
4878 const Image *right_image,ExceptionInfo *exception)
4879{
4880 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4881}
4882
4883MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004884 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004885 ExceptionInfo *exception)
4886{
4887#define StereoImageTag "Stereo/Image"
4888
4889 const Image
4890 *image;
4891
4892 Image
4893 *stereo_image;
4894
cristy3ed852e2009-09-05 21:47:34 +00004895 MagickBooleanType
4896 status;
4897
cristy9d314ff2011-03-09 01:30:28 +00004898 ssize_t
4899 y;
4900
cristy3ed852e2009-09-05 21:47:34 +00004901 assert(left_image != (const Image *) NULL);
4902 assert(left_image->signature == MagickSignature);
4903 if (left_image->debug != MagickFalse)
4904 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4905 left_image->filename);
4906 assert(right_image != (const Image *) NULL);
4907 assert(right_image->signature == MagickSignature);
4908 assert(exception != (ExceptionInfo *) NULL);
4909 assert(exception->signature == MagickSignature);
4910 assert(right_image != (const Image *) NULL);
4911 image=left_image;
4912 if ((left_image->columns != right_image->columns) ||
4913 (left_image->rows != right_image->rows))
4914 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4915 /*
4916 Initialize stereo image attributes.
4917 */
4918 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4919 MagickTrue,exception);
4920 if (stereo_image == (Image *) NULL)
4921 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004922 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004923 {
cristy3ed852e2009-09-05 21:47:34 +00004924 stereo_image=DestroyImage(stereo_image);
4925 return((Image *) NULL);
4926 }
4927 /*
4928 Copy left image to red channel and right image to blue channel.
4929 */
cristyda16f162011-02-19 23:52:17 +00004930 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004931 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004932 {
cristy4c08aed2011-07-01 19:47:50 +00004933 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004934 *restrict p,
4935 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004936
cristybb503372010-05-27 20:51:26 +00004937 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004938 x;
4939
cristy4c08aed2011-07-01 19:47:50 +00004940 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004941 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004942
4943 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4944 exception);
4945 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4946 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
cristy76f512e2011-09-12 01:26:56 +00004947 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
4948 (r == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004949 break;
cristybb503372010-05-27 20:51:26 +00004950 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004951 {
cristy4c08aed2011-07-01 19:47:50 +00004952 SetPixelRed(image,GetPixelRed(left_image,p),r);
cristy76f512e2011-09-12 01:26:56 +00004953 SetPixelGreen(image,GetPixelGreen(right_image,q),r);
4954 SetPixelBlue(image,GetPixelBlue(right_image,q),r);
4955 if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
4956 SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4957 GetPixelAlpha(right_image,q))/2,r);
cristyed231572011-07-14 02:18:59 +00004958 p+=GetPixelChannels(left_image);
cristy76f512e2011-09-12 01:26:56 +00004959 q+=GetPixelChannels(right_image);
4960 r+=GetPixelChannels(stereo_image);
cristy3ed852e2009-09-05 21:47:34 +00004961 }
4962 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4963 break;
cristy8b27a6d2010-02-14 03:31:15 +00004964 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004965 {
cristy8b27a6d2010-02-14 03:31:15 +00004966 MagickBooleanType
4967 proceed;
4968
cristybb503372010-05-27 20:51:26 +00004969 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4970 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004971 if (proceed == MagickFalse)
4972 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004973 }
4974 }
cristyda16f162011-02-19 23:52:17 +00004975 if (status == MagickFalse)
4976 {
4977 stereo_image=DestroyImage(stereo_image);
4978 return((Image *) NULL);
4979 }
cristy3ed852e2009-09-05 21:47:34 +00004980 return(stereo_image);
4981}
4982
4983/*
4984%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4985% %
4986% %
4987% %
4988% S w i r l I m a g e %
4989% %
4990% %
4991% %
4992%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4993%
4994% SwirlImage() swirls the pixels about the center of the image, where
4995% degrees indicates the sweep of the arc through which each pixel is moved.
4996% You get a more dramatic effect as the degrees move from 1 to 360.
4997%
4998% The format of the SwirlImage method is:
4999%
5000% Image *SwirlImage(const Image *image,double degrees,
cristy76f512e2011-09-12 01:26:56 +00005001% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005002%
5003% A description of each parameter follows:
5004%
5005% o image: the image.
5006%
5007% o degrees: Define the tightness of the swirling effect.
5008%
cristy76f512e2011-09-12 01:26:56 +00005009% o method: the pixel interpolation method.
5010%
cristy3ed852e2009-09-05 21:47:34 +00005011% o exception: return any errors or warnings in this structure.
5012%
5013*/
5014MagickExport 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#define SwirlImageTag "Swirl/Image"
5018
cristyfa112112010-01-04 17:48:07 +00005019 CacheView
5020 *image_view,
5021 *swirl_view;
5022
cristy3ed852e2009-09-05 21:47:34 +00005023 Image
5024 *swirl_image;
5025
cristy3ed852e2009-09-05 21:47:34 +00005026 MagickBooleanType
5027 status;
5028
cristybb503372010-05-27 20:51:26 +00005029 MagickOffsetType
5030 progress;
5031
cristy3ed852e2009-09-05 21:47:34 +00005032 MagickRealType
5033 radius;
5034
5035 PointInfo
5036 center,
5037 scale;
5038
cristybb503372010-05-27 20:51:26 +00005039 ssize_t
5040 y;
5041
cristy3ed852e2009-09-05 21:47:34 +00005042 /*
5043 Initialize swirl image attributes.
5044 */
5045 assert(image != (const Image *) NULL);
5046 assert(image->signature == MagickSignature);
5047 if (image->debug != MagickFalse)
5048 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5049 assert(exception != (ExceptionInfo *) NULL);
5050 assert(exception->signature == MagickSignature);
cristy76f512e2011-09-12 01:26:56 +00005051 swirl_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005052 if (swirl_image == (Image *) NULL)
5053 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005054 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005055 {
cristy3ed852e2009-09-05 21:47:34 +00005056 swirl_image=DestroyImage(swirl_image);
5057 return((Image *) NULL);
5058 }
cristy4c08aed2011-07-01 19:47:50 +00005059 if (swirl_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005060 swirl_image->matte=MagickTrue;
5061 /*
5062 Compute scaling factor.
5063 */
5064 center.x=(double) image->columns/2.0;
5065 center.y=(double) image->rows/2.0;
5066 radius=MagickMax(center.x,center.y);
5067 scale.x=1.0;
5068 scale.y=1.0;
5069 if (image->columns > image->rows)
5070 scale.y=(double) image->columns/(double) image->rows;
5071 else
5072 if (image->columns < image->rows)
5073 scale.x=(double) image->rows/(double) image->columns;
5074 degrees=(double) DegreesToRadians(degrees);
5075 /*
5076 Swirl image.
5077 */
5078 status=MagickTrue;
5079 progress=0;
cristydb070952012-04-20 14:33:00 +00005080 image_view=AcquireVirtualCacheView(image,exception);
5081 swirl_view=AcquireAuthenticCacheView(swirl_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00005082#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005083 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005084#endif
cristybb503372010-05-27 20:51:26 +00005085 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005086 {
cristy3ed852e2009-09-05 21:47:34 +00005087 MagickRealType
5088 distance;
5089
5090 PointInfo
5091 delta;
5092
cristy6d188022011-09-12 13:23:33 +00005093 register const Quantum
5094 *restrict p;
5095
cristybb503372010-05-27 20:51:26 +00005096 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005097 x;
5098
cristy4c08aed2011-07-01 19:47:50 +00005099 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005100 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005101
5102 if (status == MagickFalse)
5103 continue;
cristy6d188022011-09-12 13:23:33 +00005104 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy1707c6c2012-01-18 23:30:54 +00005105 q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00005106 exception);
cristy6d188022011-09-12 13:23:33 +00005107 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005108 {
5109 status=MagickFalse;
5110 continue;
5111 }
cristy3ed852e2009-09-05 21:47:34 +00005112 delta.y=scale.y*(double) (y-center.y);
cristybb503372010-05-27 20:51:26 +00005113 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005114 {
5115 /*
5116 Determine if the pixel is within an ellipse.
5117 */
cristy10a6c612012-01-29 21:41:05 +00005118 if (GetPixelMask(image,p) != 0)
5119 {
5120 p+=GetPixelChannels(image);
5121 q+=GetPixelChannels(swirl_image);
5122 continue;
5123 }
cristy3ed852e2009-09-05 21:47:34 +00005124 delta.x=scale.x*(double) (x-center.x);
5125 distance=delta.x*delta.x+delta.y*delta.y;
cristy6d188022011-09-12 13:23:33 +00005126 if (distance >= (radius*radius))
5127 {
cristy1707c6c2012-01-18 23:30:54 +00005128 register ssize_t
5129 i;
5130
cristy6d188022011-09-12 13:23:33 +00005131 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy1707c6c2012-01-18 23:30:54 +00005132 {
5133 PixelChannel
5134 channel;
5135
5136 PixelTrait
5137 swirl_traits,
5138 traits;
5139
5140 channel=GetPixelChannelMapChannel(image,i);
5141 traits=GetPixelChannelMapTraits(image,channel);
5142 swirl_traits=GetPixelChannelMapTraits(swirl_image,channel);
5143 if ((traits == UndefinedPixelTrait) ||
5144 (swirl_traits == UndefinedPixelTrait))
5145 continue;
5146 SetPixelChannel(swirl_image,channel,p[i],q);
5147 }
cristy6d188022011-09-12 13:23:33 +00005148 }
5149 else
cristy3ed852e2009-09-05 21:47:34 +00005150 {
5151 MagickRealType
5152 cosine,
5153 factor,
5154 sine;
5155
5156 /*
5157 Swirl the pixel.
5158 */
5159 factor=1.0-sqrt((double) distance)/radius;
5160 sine=sin((double) (degrees*factor*factor));
5161 cosine=cos((double) (degrees*factor*factor));
cristy76f512e2011-09-12 01:26:56 +00005162 status=InterpolatePixelChannels(image,image_view,swirl_image,method,
5163 ((cosine*delta.x-sine*delta.y)/scale.x+center.x),(double)
5164 ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,exception);
cristy3ed852e2009-09-05 21:47:34 +00005165 }
cristy6d188022011-09-12 13:23:33 +00005166 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00005167 q+=GetPixelChannels(swirl_image);
cristy3ed852e2009-09-05 21:47:34 +00005168 }
5169 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5170 status=MagickFalse;
5171 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5172 {
5173 MagickBooleanType
5174 proceed;
5175
cristyb5d5f722009-11-04 03:03:49 +00005176#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005177 #pragma omp critical (MagickCore_SwirlImage)
cristy3ed852e2009-09-05 21:47:34 +00005178#endif
5179 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5180 if (proceed == MagickFalse)
5181 status=MagickFalse;
5182 }
5183 }
5184 swirl_view=DestroyCacheView(swirl_view);
5185 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005186 if (status == MagickFalse)
5187 swirl_image=DestroyImage(swirl_image);
5188 return(swirl_image);
5189}
5190
5191/*
5192%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5193% %
5194% %
5195% %
5196% T i n t I m a g e %
5197% %
5198% %
5199% %
5200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5201%
5202% TintImage() applies a color vector to each pixel in the image. The length
5203% of the vector is 0 for black and white and at its maximum for the midtones.
5204% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5205%
5206% The format of the TintImage method is:
5207%
cristyb817c3f2011-10-03 14:00:35 +00005208% Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005209% const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005210%
5211% A description of each parameter follows:
5212%
5213% o image: the image.
5214%
cristyb817c3f2011-10-03 14:00:35 +00005215% o blend: A color value used for tinting.
cristy3ed852e2009-09-05 21:47:34 +00005216%
5217% o tint: A color value used for tinting.
5218%
5219% o exception: return any errors or warnings in this structure.
5220%
5221*/
cristyb817c3f2011-10-03 14:00:35 +00005222MagickExport Image *TintImage(const Image *image,const char *blend,
cristy28474bf2011-09-11 23:32:52 +00005223 const PixelInfo *tint,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005224{
5225#define TintImageTag "Tint/Image"
5226
cristyc4c8d132010-01-07 01:58:38 +00005227 CacheView
5228 *image_view,
5229 *tint_view;
5230
cristy3ed852e2009-09-05 21:47:34 +00005231 GeometryInfo
5232 geometry_info;
5233
5234 Image
5235 *tint_image;
5236
cristy3ed852e2009-09-05 21:47:34 +00005237 MagickBooleanType
5238 status;
5239
cristybb503372010-05-27 20:51:26 +00005240 MagickOffsetType
5241 progress;
cristy3ed852e2009-09-05 21:47:34 +00005242
cristy28474bf2011-09-11 23:32:52 +00005243 MagickRealType
5244 intensity;
5245
cristy4c08aed2011-07-01 19:47:50 +00005246 PixelInfo
cristy1707c6c2012-01-18 23:30:54 +00005247 color_vector;
cristy3ed852e2009-09-05 21:47:34 +00005248
cristybb503372010-05-27 20:51:26 +00005249 MagickStatusType
5250 flags;
5251
5252 ssize_t
5253 y;
5254
cristy3ed852e2009-09-05 21:47:34 +00005255 /*
5256 Allocate tint image.
5257 */
5258 assert(image != (const Image *) NULL);
5259 assert(image->signature == MagickSignature);
5260 if (image->debug != MagickFalse)
5261 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5262 assert(exception != (ExceptionInfo *) NULL);
5263 assert(exception->signature == MagickSignature);
5264 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5265 if (tint_image == (Image *) NULL)
5266 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005267 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005268 {
cristy3ed852e2009-09-05 21:47:34 +00005269 tint_image=DestroyImage(tint_image);
5270 return((Image *) NULL);
5271 }
cristyaed9c382011-10-03 17:54:21 +00005272 if (blend == (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005273 return(tint_image);
5274 /*
5275 Determine RGB values of the color.
5276 */
cristy1707c6c2012-01-18 23:30:54 +00005277 GetPixelInfo(image,&color_vector);
cristyb817c3f2011-10-03 14:00:35 +00005278 flags=ParseGeometry(blend,&geometry_info);
cristy1707c6c2012-01-18 23:30:54 +00005279 color_vector.red=geometry_info.rho;
5280 color_vector.green=geometry_info.rho;
5281 color_vector.blue=geometry_info.rho;
5282 color_vector.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005283 if ((flags & SigmaValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005284 color_vector.green=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00005285 if ((flags & XiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005286 color_vector.blue=geometry_info.xi;
cristyb817c3f2011-10-03 14:00:35 +00005287 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005288 color_vector.alpha=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005289 if (image->colorspace == CMYKColorspace)
5290 {
cristy1707c6c2012-01-18 23:30:54 +00005291 color_vector.black=geometry_info.rho;
cristy76f512e2011-09-12 01:26:56 +00005292 if ((flags & PsiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005293 color_vector.black=geometry_info.psi;
cristy76f512e2011-09-12 01:26:56 +00005294 if ((flags & ChiValue) != 0)
cristy1707c6c2012-01-18 23:30:54 +00005295 color_vector.alpha=geometry_info.chi;
cristy76f512e2011-09-12 01:26:56 +00005296 }
cristy28474bf2011-09-11 23:32:52 +00005297 intensity=(MagickRealType) GetPixelInfoIntensity(tint);
cristy1707c6c2012-01-18 23:30:54 +00005298 color_vector.red=(MagickRealType) (color_vector.red*tint->red/100.0-
5299 intensity);
5300 color_vector.green=(MagickRealType) (color_vector.green*tint->green/100.0-
5301 intensity);
5302 color_vector.blue=(MagickRealType) (color_vector.blue*tint->blue/100.0-
5303 intensity);
5304 color_vector.black=(MagickRealType) (color_vector.black*tint->black/100.0-
5305 intensity);
5306 color_vector.alpha=(MagickRealType) (color_vector.alpha*tint->alpha/100.0-
5307 intensity);
cristy3ed852e2009-09-05 21:47:34 +00005308 /*
5309 Tint image.
5310 */
5311 status=MagickTrue;
5312 progress=0;
cristydb070952012-04-20 14:33:00 +00005313 image_view=AcquireVirtualCacheView(image,exception);
5314 tint_view=AcquireAuthenticCacheView(tint_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00005315#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005316 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005317#endif
cristybb503372010-05-27 20:51:26 +00005318 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005319 {
cristy4c08aed2011-07-01 19:47:50 +00005320 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00005321 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005322
cristy4c08aed2011-07-01 19:47:50 +00005323 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005324 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005325
cristy6b91acb2011-04-19 12:23:54 +00005326 register ssize_t
5327 x;
5328
cristy3ed852e2009-09-05 21:47:34 +00005329 if (status == MagickFalse)
5330 continue;
5331 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5332 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5333 exception);
cristy4c08aed2011-07-01 19:47:50 +00005334 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00005335 {
5336 status=MagickFalse;
5337 continue;
5338 }
cristybb503372010-05-27 20:51:26 +00005339 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005340 {
cristy4c08aed2011-07-01 19:47:50 +00005341 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005342 pixel;
5343
5344 MagickRealType
5345 weight;
5346
cristy1707c6c2012-01-18 23:30:54 +00005347 register ssize_t
5348 i;
5349
cristy10a6c612012-01-29 21:41:05 +00005350 if (GetPixelMask(image,p) != 0)
5351 {
5352 p+=GetPixelChannels(image);
5353 q+=GetPixelChannels(tint_image);
5354 continue;
5355 }
cristy1707c6c2012-01-18 23:30:54 +00005356 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
5357 {
5358 PixelChannel
5359 channel;
5360
5361 PixelTrait
5362 tint_traits,
5363 traits;
5364
5365 channel=GetPixelChannelMapChannel(image,i);
5366 traits=GetPixelChannelMapTraits(image,channel);
5367 tint_traits=GetPixelChannelMapTraits(tint_image,channel);
5368 if ((traits == UndefinedPixelTrait) ||
5369 (tint_traits == UndefinedPixelTrait))
5370 continue;
5371 if ((tint_traits & CopyPixelTrait) != 0)
5372 {
5373 SetPixelChannel(tint_image,channel,p[i],q);
5374 continue;
5375 }
5376 }
5377 GetPixelInfo(image,&pixel);
5378 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5379 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5380 (1.0-(4.0*(weight*weight)));
5381 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5382 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5383 (1.0-(4.0*(weight*weight)));
5384 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5385 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5386 (1.0-(4.0*(weight*weight)));
5387 weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5388 pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5389 (1.0-(4.0*(weight*weight)));
5390 SetPixelInfoPixel(tint_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00005391 p+=GetPixelChannels(image);
5392 q+=GetPixelChannels(tint_image);
cristy3ed852e2009-09-05 21:47:34 +00005393 }
5394 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5395 status=MagickFalse;
5396 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5397 {
5398 MagickBooleanType
5399 proceed;
5400
cristyb5d5f722009-11-04 03:03:49 +00005401#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005402 #pragma omp critical (MagickCore_TintImage)
cristy3ed852e2009-09-05 21:47:34 +00005403#endif
5404 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5405 if (proceed == MagickFalse)
5406 status=MagickFalse;
5407 }
5408 }
5409 tint_view=DestroyCacheView(tint_view);
5410 image_view=DestroyCacheView(image_view);
5411 if (status == MagickFalse)
5412 tint_image=DestroyImage(tint_image);
5413 return(tint_image);
5414}
5415
5416/*
5417%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5418% %
5419% %
5420% %
5421% V i g n e t t e I m a g e %
5422% %
5423% %
5424% %
5425%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5426%
5427% VignetteImage() softens the edges of the image in vignette style.
5428%
5429% The format of the VignetteImage method is:
5430%
5431% Image *VignetteImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00005432% const double sigma,const ssize_t x,const ssize_t y,
cristy05c0c9a2011-09-05 23:16:13 +00005433% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005434%
5435% A description of each parameter follows:
5436%
5437% o image: the image.
5438%
5439% o radius: the radius of the pixel neighborhood.
5440%
5441% o sigma: the standard deviation of the Gaussian, in pixels.
5442%
5443% o x, y: Define the x and y ellipse offset.
5444%
5445% o exception: return any errors or warnings in this structure.
5446%
5447*/
5448MagickExport Image *VignetteImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00005449 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005450{
5451 char
5452 ellipse[MaxTextExtent];
5453
5454 DrawInfo
5455 *draw_info;
5456
5457 Image
5458 *canvas_image,
5459 *blur_image,
5460 *oval_image,
5461 *vignette_image;
5462
5463 assert(image != (Image *) NULL);
5464 assert(image->signature == MagickSignature);
5465 if (image->debug != MagickFalse)
5466 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5467 assert(exception != (ExceptionInfo *) NULL);
5468 assert(exception->signature == MagickSignature);
5469 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5470 if (canvas_image == (Image *) NULL)
5471 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005472 if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005473 {
cristy3ed852e2009-09-05 21:47:34 +00005474 canvas_image=DestroyImage(canvas_image);
5475 return((Image *) NULL);
5476 }
5477 canvas_image->matte=MagickTrue;
cristy98621462011-12-31 22:31:11 +00005478 oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
5479 MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005480 if (oval_image == (Image *) NULL)
5481 {
5482 canvas_image=DestroyImage(canvas_image);
5483 return((Image *) NULL);
5484 }
cristy9950d572011-10-01 18:22:35 +00005485 (void) QueryColorCompliance("#000000",AllCompliance,
5486 &oval_image->background_color,exception);
cristyea1a8aa2011-10-20 13:24:06 +00005487 (void) SetImageBackgroundColor(oval_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005488 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
cristy9950d572011-10-01 18:22:35 +00005489 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5490 exception);
5491 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5492 exception);
cristy1707c6c2012-01-18 23:30:54 +00005493 (void) FormatLocaleString(ellipse,MaxTextExtent,"ellipse %g,%g,%g,%g,"
5494 "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5495 image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005496 draw_info->primitive=AcquireString(ellipse);
cristy018f07f2011-09-04 21:15:19 +00005497 (void) DrawImage(oval_image,draw_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005498 draw_info=DestroyDrawInfo(draw_info);
cristyaa2c16c2012-03-25 22:21:35 +00005499 blur_image=BlurImage(oval_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00005500 oval_image=DestroyImage(oval_image);
5501 if (blur_image == (Image *) NULL)
5502 {
5503 canvas_image=DestroyImage(canvas_image);
5504 return((Image *) NULL);
5505 }
5506 blur_image->matte=MagickFalse;
cristy39172402012-03-30 13:04:39 +00005507 (void) CompositeImage(canvas_image,blur_image,IntensityCompositeOp,MagickTrue,
5508 0,0,exception);
cristy3ed852e2009-09-05 21:47:34 +00005509 blur_image=DestroyImage(blur_image);
5510 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5511 canvas_image=DestroyImage(canvas_image);
5512 return(vignette_image);
5513}
5514
5515/*
5516%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5517% %
5518% %
5519% %
5520% W a v e I m a g e %
5521% %
5522% %
5523% %
5524%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5525%
5526% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005527% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005528% by the given parameters.
5529%
5530% The format of the WaveImage method is:
5531%
5532% Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005533% const double wave_length,const PixelInterpolateMethod method,
5534% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005535%
5536% A description of each parameter follows:
5537%
5538% o image: the image.
5539%
5540% o amplitude, wave_length: Define the amplitude and wave length of the
5541% sine wave.
5542%
cristy5c4e2582011-09-11 19:21:03 +00005543% o interpolate: the pixel interpolation method.
5544%
cristy3ed852e2009-09-05 21:47:34 +00005545% o exception: return any errors or warnings in this structure.
5546%
5547*/
5548MagickExport Image *WaveImage(const Image *image,const double amplitude,
cristy5c4e2582011-09-11 19:21:03 +00005549 const double wave_length,const PixelInterpolateMethod method,
5550 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005551{
5552#define WaveImageTag "Wave/Image"
5553
cristyfa112112010-01-04 17:48:07 +00005554 CacheView
cristyd76c51e2011-03-26 00:21:26 +00005555 *image_view,
cristyfa112112010-01-04 17:48:07 +00005556 *wave_view;
5557
cristy3ed852e2009-09-05 21:47:34 +00005558 Image
5559 *wave_image;
5560
cristy3ed852e2009-09-05 21:47:34 +00005561 MagickBooleanType
5562 status;
5563
cristybb503372010-05-27 20:51:26 +00005564 MagickOffsetType
5565 progress;
5566
cristy3ed852e2009-09-05 21:47:34 +00005567 MagickRealType
5568 *sine_map;
5569
cristybb503372010-05-27 20:51:26 +00005570 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005571 i;
5572
cristybb503372010-05-27 20:51:26 +00005573 ssize_t
5574 y;
5575
cristy3ed852e2009-09-05 21:47:34 +00005576 /*
5577 Initialize wave image attributes.
5578 */
5579 assert(image != (Image *) NULL);
5580 assert(image->signature == MagickSignature);
5581 if (image->debug != MagickFalse)
5582 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5583 assert(exception != (ExceptionInfo *) NULL);
5584 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005585 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005586 fabs(amplitude)),MagickTrue,exception);
5587 if (wave_image == (Image *) NULL)
5588 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00005589 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00005590 {
cristy3ed852e2009-09-05 21:47:34 +00005591 wave_image=DestroyImage(wave_image);
5592 return((Image *) NULL);
5593 }
cristy4c08aed2011-07-01 19:47:50 +00005594 if (wave_image->background_color.alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00005595 wave_image->matte=MagickTrue;
5596 /*
5597 Allocate sine map.
5598 */
5599 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5600 sizeof(*sine_map));
5601 if (sine_map == (MagickRealType *) NULL)
5602 {
5603 wave_image=DestroyImage(wave_image);
5604 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5605 }
cristybb503372010-05-27 20:51:26 +00005606 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005607 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5608 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005609 /*
5610 Wave image.
5611 */
5612 status=MagickTrue;
5613 progress=0;
cristydb070952012-04-20 14:33:00 +00005614 image_view=AcquireVirtualCacheView(image,exception);
5615 wave_view=AcquireAuthenticCacheView(wave_image,exception);
cristyd76c51e2011-03-26 00:21:26 +00005616 (void) SetCacheViewVirtualPixelMethod(image_view,
5617 BackgroundVirtualPixelMethod);
cristyb5d5f722009-11-04 03:03:49 +00005618#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00005619 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005620#endif
cristybb503372010-05-27 20:51:26 +00005621 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005622 {
cristy4c08aed2011-07-01 19:47:50 +00005623 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00005624 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005625
cristye97bb922011-04-03 01:36:52 +00005626 register ssize_t
5627 x;
5628
cristy3ed852e2009-09-05 21:47:34 +00005629 if (status == MagickFalse)
5630 continue;
5631 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5632 exception);
cristyacd2ed22011-08-30 01:44:23 +00005633 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005634 {
5635 status=MagickFalse;
5636 continue;
5637 }
cristybb503372010-05-27 20:51:26 +00005638 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005639 {
cristy5c4e2582011-09-11 19:21:03 +00005640 status=InterpolatePixelChannels(image,image_view,wave_image,method,
5641 (double) x,(double) (y-sine_map[x]),q,exception);
cristyed231572011-07-14 02:18:59 +00005642 q+=GetPixelChannels(wave_image);
cristy3ed852e2009-09-05 21:47:34 +00005643 }
5644 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5645 status=MagickFalse;
5646 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5647 {
5648 MagickBooleanType
5649 proceed;
5650
cristyb5d5f722009-11-04 03:03:49 +00005651#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy69cfa022012-01-23 12:47:29 +00005652 #pragma omp critical (MagickCore_WaveImage)
cristy3ed852e2009-09-05 21:47:34 +00005653#endif
5654 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5655 if (proceed == MagickFalse)
5656 status=MagickFalse;
5657 }
5658 }
5659 wave_view=DestroyCacheView(wave_view);
cristyd76c51e2011-03-26 00:21:26 +00005660 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00005661 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5662 if (status == MagickFalse)
5663 wave_image=DestroyImage(wave_image);
5664 return(wave_image);
5665}