blob: bb91bd2d76a4d6cf923ef284332f2d031c3f0935 [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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*/
43#include "magick/studio.h"
44#include "magick/annotate.h"
45#include "magick/artifact.h"
cristycd220102009-10-14 19:39:10 +000046#include "magick/attribute.h"
cristy3ed852e2009-09-05 21:47:34 +000047#include "magick/cache.h"
48#include "magick/cache-view.h"
49#include "magick/color.h"
50#include "magick/color-private.h"
51#include "magick/composite.h"
52#include "magick/decorate.h"
53#include "magick/draw.h"
54#include "magick/effect.h"
55#include "magick/enhance.h"
56#include "magick/exception.h"
57#include "magick/exception-private.h"
58#include "magick/fx.h"
59#include "magick/fx-private.h"
60#include "magick/gem.h"
61#include "magick/geometry.h"
62#include "magick/layer.h"
63#include "magick/list.h"
64#include "magick/log.h"
65#include "magick/image.h"
66#include "magick/image-private.h"
cristye7f51092010-01-17 00:39:37 +000067#include "magick/magick.h"
cristy3ed852e2009-09-05 21:47:34 +000068#include "magick/memory_.h"
69#include "magick/monitor.h"
70#include "magick/monitor-private.h"
71#include "magick/option.h"
72#include "magick/pixel-private.h"
73#include "magick/property.h"
74#include "magick/quantum.h"
cristyce70c172010-01-07 17:15:30 +000075#include "magick/quantum-private.h"
cristy3ed852e2009-09-05 21:47:34 +000076#include "magick/random_.h"
77#include "magick/random-private.h"
78#include "magick/resample.h"
79#include "magick/resample-private.h"
80#include "magick/resize.h"
81#include "magick/shear.h"
82#include "magick/splay-tree.h"
83#include "magick/statistic.h"
84#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000085#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000086#include "magick/thread-private.h"
87#include "magick/transform.h"
88#include "magick/utility.h"
89
90/*
91 Define declarations.
92*/
93#define LeftShiftOperator 0xf5
94#define RightShiftOperator 0xf6
95#define LessThanEqualOperator 0xf7
96#define GreaterThanEqualOperator 0xf8
97#define EqualOperator 0xf9
98#define NotEqualOperator 0xfa
99#define LogicalAndOperator 0xfb
100#define LogicalOrOperator 0xfc
cristy116af162010-08-13 01:25:47 +0000101#define ExponentialNotation 0xfd
cristy3ed852e2009-09-05 21:47:34 +0000102
103struct _FxInfo
104{
105 const Image
106 *images;
107
cristy3ed852e2009-09-05 21:47:34 +0000108 char
109 *expression;
110
111 FILE
112 *file;
113
114 SplayTreeInfo
115 *colors,
116 *symbols;
117
118 ResampleFilter
119 **resample_filter;
120
121 RandomInfo
122 *random_info;
123
124 ExceptionInfo
125 *exception;
126};
127
128/*
129%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
130% %
131% %
132% %
133+ A c q u i r e F x I n f o %
134% %
135% %
136% %
137%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
138%
139% AcquireFxInfo() allocates the FxInfo structure.
140%
141% The format of the AcquireFxInfo method is:
142%
143% FxInfo *AcquireFxInfo(Image *image,const char *expression)
cristy0a9b3722010-10-23 18:45:49 +0000144%
cristy3ed852e2009-09-05 21:47:34 +0000145% A description of each parameter follows:
146%
147% o image: the image.
148%
149% o expression: the expression.
150%
151*/
152MagickExport FxInfo *AcquireFxInfo(const Image *image,const char *expression)
153{
154 char
155 fx_op[2];
156
cristycb180922011-03-11 14:41:24 +0000157 const Image
158 *next;
159
cristy3ed852e2009-09-05 21:47:34 +0000160 FxInfo
161 *fx_info;
162
cristybb503372010-05-27 20:51:26 +0000163 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000164 i;
165
cristy73bd4a52010-10-05 11:24:23 +0000166 fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +0000167 if (fx_info == (FxInfo *) NULL)
168 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
169 (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
170 fx_info->exception=AcquireExceptionInfo();
anthony7d86e172011-03-23 12:37:06 +0000171 fx_info->images=image;
cristy3ed852e2009-09-05 21:47:34 +0000172 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
173 RelinquishMagickMemory);
174 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
175 RelinquishMagickMemory);
176 fx_info->resample_filter=(ResampleFilter **) AcquireQuantumMemory(
177 GetImageListLength(fx_info->images),sizeof(*fx_info->resample_filter));
178 if (fx_info->resample_filter == (ResampleFilter **) NULL)
179 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristya2262262011-03-11 02:50:37 +0000180 i=0;
cristy0ea377f2011-03-24 00:54:19 +0000181 next=GetFirstImageInList(fx_info->images);
182 for ( ; next != (Image *) NULL; next=next->next)
cristy3ed852e2009-09-05 21:47:34 +0000183 {
cristya2262262011-03-11 02:50:37 +0000184 fx_info->resample_filter[i]=AcquireResampleFilter(next,fx_info->exception);
cristy3ed852e2009-09-05 21:47:34 +0000185 SetResampleFilter(fx_info->resample_filter[i],PointFilter,1.0);
cristye910b0c2011-03-13 01:02:15 +0000186 (void) SetResampleFilterInterpolateMethod(fx_info->resample_filter[i],
cristycb180922011-03-11 14:41:24 +0000187 NearestNeighborInterpolatePixel);
cristya2262262011-03-11 02:50:37 +0000188 i++;
cristy3ed852e2009-09-05 21:47:34 +0000189 }
190 fx_info->random_info=AcquireRandomInfo();
191 fx_info->expression=ConstantString(expression);
192 fx_info->file=stderr;
193 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
194 if ((strstr(fx_info->expression,"e+") != (char *) NULL) ||
195 (strstr(fx_info->expression,"e-") != (char *) NULL))
196 {
197 /*
cristy116af162010-08-13 01:25:47 +0000198 Convert scientific notation.
cristy3ed852e2009-09-05 21:47:34 +0000199 */
cristy116af162010-08-13 01:25:47 +0000200 (void) SubstituteString(&fx_info->expression,"0e+","0**10^");
201 (void) SubstituteString(&fx_info->expression,"1e+","1**10^");
202 (void) SubstituteString(&fx_info->expression,"2e+","2**10^");
203 (void) SubstituteString(&fx_info->expression,"3e+","3**10^");
204 (void) SubstituteString(&fx_info->expression,"4e+","4**10^");
205 (void) SubstituteString(&fx_info->expression,"5e+","5**10^");
206 (void) SubstituteString(&fx_info->expression,"6e+","6**10^");
207 (void) SubstituteString(&fx_info->expression,"7e+","7**10^");
208 (void) SubstituteString(&fx_info->expression,"8e+","8**10^");
209 (void) SubstituteString(&fx_info->expression,"9e+","9**10^");
210 (void) SubstituteString(&fx_info->expression,"0e-","0**10^-");
211 (void) SubstituteString(&fx_info->expression,"1e-","1**10^-");
212 (void) SubstituteString(&fx_info->expression,"2e-","2**10^-");
213 (void) SubstituteString(&fx_info->expression,"3e-","3**10^-");
214 (void) SubstituteString(&fx_info->expression,"4e-","4**10^-");
215 (void) SubstituteString(&fx_info->expression,"5e-","5**10^-");
216 (void) SubstituteString(&fx_info->expression,"6e-","6**10^-");
217 (void) SubstituteString(&fx_info->expression,"7e-","7**10^-");
218 (void) SubstituteString(&fx_info->expression,"8e-","8**10^-");
219 (void) SubstituteString(&fx_info->expression,"9e-","9**10^-");
cristy3ed852e2009-09-05 21:47:34 +0000220 }
221 /*
cristy8b8a3ae2010-10-23 18:49:46 +0000222 Force right-to-left associativity for unary negation.
223 */
224 (void) SubstituteString(&fx_info->expression,"-","-1.0*");
225 /*
cristy3ed852e2009-09-05 21:47:34 +0000226 Convert complex to simple operators.
227 */
228 fx_op[1]='\0';
229 *fx_op=(char) LeftShiftOperator;
230 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
231 *fx_op=(char) RightShiftOperator;
232 (void) SubstituteString(&fx_info->expression,">>",fx_op);
233 *fx_op=(char) LessThanEqualOperator;
234 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
235 *fx_op=(char) GreaterThanEqualOperator;
236 (void) SubstituteString(&fx_info->expression,">=",fx_op);
237 *fx_op=(char) EqualOperator;
238 (void) SubstituteString(&fx_info->expression,"==",fx_op);
239 *fx_op=(char) NotEqualOperator;
240 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
241 *fx_op=(char) LogicalAndOperator;
242 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
243 *fx_op=(char) LogicalOrOperator;
244 (void) SubstituteString(&fx_info->expression,"||",fx_op);
cristy116af162010-08-13 01:25:47 +0000245 *fx_op=(char) ExponentialNotation;
246 (void) SubstituteString(&fx_info->expression,"**",fx_op);
cristy3ed852e2009-09-05 21:47:34 +0000247 return(fx_info);
248}
249
250/*
251%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252% %
253% %
254% %
255% A d d N o i s e I m a g e %
256% %
257% %
258% %
259%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
260%
261% AddNoiseImage() adds random noise to the image.
262%
263% The format of the AddNoiseImage method is:
264%
265% Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
266% ExceptionInfo *exception)
267% Image *AddNoiseImageChannel(const Image *image,const ChannelType channel,
268% const NoiseType noise_type,ExceptionInfo *exception)
269%
270% A description of each parameter follows:
271%
272% o image: the image.
273%
274% o channel: the channel type.
275%
276% o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
277% Impulse, Laplacian, or Poisson.
278%
279% o exception: return any errors or warnings in this structure.
280%
281*/
282MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
283 ExceptionInfo *exception)
284{
285 Image
286 *noise_image;
287
288 noise_image=AddNoiseImageChannel(image,DefaultChannels,noise_type,exception);
289 return(noise_image);
290}
291
292MagickExport Image *AddNoiseImageChannel(const Image *image,
293 const ChannelType channel,const NoiseType noise_type,ExceptionInfo *exception)
294{
295#define AddNoiseImageTag "AddNoise/Image"
296
cristyfa112112010-01-04 17:48:07 +0000297 CacheView
298 *image_view,
299 *noise_view;
300
cristy3ed852e2009-09-05 21:47:34 +0000301 const char
302 *option;
303
304 Image
305 *noise_image;
306
cristy3ed852e2009-09-05 21:47:34 +0000307 MagickBooleanType
308 status;
309
cristybb503372010-05-27 20:51:26 +0000310 MagickOffsetType
311 progress;
312
cristy3ed852e2009-09-05 21:47:34 +0000313 MagickRealType
314 attenuate;
315
316 RandomInfo
cristyfa112112010-01-04 17:48:07 +0000317 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +0000318
cristybb503372010-05-27 20:51:26 +0000319 ssize_t
320 y;
321
cristy3ed852e2009-09-05 21:47:34 +0000322 /*
323 Initialize noise image attributes.
324 */
325 assert(image != (const Image *) NULL);
326 assert(image->signature == MagickSignature);
327 if (image->debug != MagickFalse)
328 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
329 assert(exception != (ExceptionInfo *) NULL);
330 assert(exception->signature == MagickSignature);
331 noise_image=CloneImage(image,0,0,MagickTrue,exception);
332 if (noise_image == (Image *) NULL)
333 return((Image *) NULL);
334 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
335 {
336 InheritException(exception,&noise_image->exception);
337 noise_image=DestroyImage(noise_image);
338 return((Image *) NULL);
339 }
340 /*
341 Add noise in each row.
342 */
343 attenuate=1.0;
344 option=GetImageArtifact(image,"attenuate");
345 if (option != (char *) NULL)
cristyf2f27272009-12-17 14:48:46 +0000346 attenuate=StringToDouble(option);
cristy3ed852e2009-09-05 21:47:34 +0000347 status=MagickTrue;
348 progress=0;
349 random_info=AcquireRandomInfoThreadSet();
350 image_view=AcquireCacheView(image);
351 noise_view=AcquireCacheView(noise_image);
cristy319a1e72010-02-21 15:13:11 +0000352#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000353 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000354#endif
cristybb503372010-05-27 20:51:26 +0000355 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000356 {
cristy5c9e6f22010-09-17 17:31:01 +0000357 const int
358 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000359
cristy3ed852e2009-09-05 21:47:34 +0000360 MagickBooleanType
361 sync;
362
363 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000364 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000365
366 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000367 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000368
369 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000370 *restrict noise_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000371
cristybb503372010-05-27 20:51:26 +0000372 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000373 x;
374
375 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000376 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000377
378 if (status == MagickFalse)
379 continue;
380 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
381 q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
382 exception);
383 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
384 {
385 status=MagickFalse;
386 continue;
387 }
388 indexes=GetCacheViewVirtualIndexQueue(image_view);
389 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
cristybb503372010-05-27 20:51:26 +0000390 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000391 {
392 if ((channel & RedChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000393 q->red=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000394 p->red,noise_type,attenuate));
395 if ((channel & GreenChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000396 q->green=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000397 p->green,noise_type,attenuate));
398 if ((channel & BlueChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000399 q->blue=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000400 p->blue,noise_type,attenuate));
401 if ((channel & OpacityChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000402 q->opacity=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000403 p->opacity,noise_type,attenuate));
404 if (((channel & IndexChannel) != 0) &&
405 (image->colorspace == CMYKColorspace))
cristyce70c172010-01-07 17:15:30 +0000406 noise_indexes[x]=(IndexPacket) ClampToQuantum(GenerateDifferentialNoise(
cristy3ed852e2009-09-05 21:47:34 +0000407 random_info[id],indexes[x],noise_type,attenuate));
408 p++;
409 q++;
410 }
411 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
412 if (sync == MagickFalse)
413 status=MagickFalse;
414 if (image->progress_monitor != (MagickProgressMonitor) NULL)
415 {
416 MagickBooleanType
417 proceed;
418
cristyb5d5f722009-11-04 03:03:49 +0000419#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy319a1e72010-02-21 15:13:11 +0000420 #pragma omp critical (MagickCore_AddNoiseImage)
cristy3ed852e2009-09-05 21:47:34 +0000421#endif
422 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
423 image->rows);
424 if (proceed == MagickFalse)
425 status=MagickFalse;
426 }
427 }
428 noise_view=DestroyCacheView(noise_view);
429 image_view=DestroyCacheView(image_view);
430 random_info=DestroyRandomInfoThreadSet(random_info);
431 if (status == MagickFalse)
432 noise_image=DestroyImage(noise_image);
433 return(noise_image);
434}
435
436/*
437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438% %
439% %
440% %
441% B l u e S h i f t I m a g e %
442% %
443% %
444% %
445%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
446%
447% BlueShiftImage() mutes the colors of the image to simulate a scene at
448% nighttime in the moonlight.
449%
450% The format of the BlueShiftImage method is:
451%
452% Image *BlueShiftImage(const Image *image,const double factor,
453% ExceptionInfo *exception)
454%
455% A description of each parameter follows:
456%
457% o image: the image.
458%
459% o factor: the shift factor.
460%
461% o exception: return any errors or warnings in this structure.
462%
463*/
464MagickExport Image *BlueShiftImage(const Image *image,const double factor,
465 ExceptionInfo *exception)
466{
467#define BlueShiftImageTag "BlueShift/Image"
468
cristyc4c8d132010-01-07 01:58:38 +0000469 CacheView
470 *image_view,
471 *shift_view;
472
cristy3ed852e2009-09-05 21:47:34 +0000473 Image
474 *shift_image;
475
cristy3ed852e2009-09-05 21:47:34 +0000476 MagickBooleanType
477 status;
478
cristybb503372010-05-27 20:51:26 +0000479 MagickOffsetType
480 progress;
481
482 ssize_t
483 y;
484
cristy3ed852e2009-09-05 21:47:34 +0000485 /*
486 Allocate blue shift image.
487 */
488 assert(image != (const Image *) NULL);
489 assert(image->signature == MagickSignature);
490 if (image->debug != MagickFalse)
491 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
492 assert(exception != (ExceptionInfo *) NULL);
493 assert(exception->signature == MagickSignature);
494 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,
495 exception);
496 if (shift_image == (Image *) NULL)
497 return((Image *) NULL);
498 if (SetImageStorageClass(shift_image,DirectClass) == MagickFalse)
499 {
500 InheritException(exception,&shift_image->exception);
501 shift_image=DestroyImage(shift_image);
502 return((Image *) NULL);
503 }
504 /*
505 Blue-shift DirectClass image.
506 */
507 status=MagickTrue;
508 progress=0;
509 image_view=AcquireCacheView(image);
510 shift_view=AcquireCacheView(shift_image);
cristy319a1e72010-02-21 15:13:11 +0000511#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000512 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000513#endif
cristybb503372010-05-27 20:51:26 +0000514 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000515 {
516 MagickBooleanType
517 sync;
518
519 MagickPixelPacket
520 pixel;
521
522 Quantum
523 quantum;
524
525 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000526 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000527
cristybb503372010-05-27 20:51:26 +0000528 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000529 x;
530
531 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000532 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000533
534 if (status == MagickFalse)
535 continue;
536 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
537 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
538 exception);
539 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
540 {
541 status=MagickFalse;
542 continue;
543 }
cristybb503372010-05-27 20:51:26 +0000544 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000545 {
cristyce70c172010-01-07 17:15:30 +0000546 quantum=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000547 if (p->green < quantum)
cristyce70c172010-01-07 17:15:30 +0000548 quantum=GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000549 if (p->blue < quantum)
cristyce70c172010-01-07 17:15:30 +0000550 quantum=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000551 pixel.red=0.5*(p->red+factor*quantum);
552 pixel.green=0.5*(p->green+factor*quantum);
553 pixel.blue=0.5*(p->blue+factor*quantum);
cristyce70c172010-01-07 17:15:30 +0000554 quantum=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000555 if (p->green > quantum)
cristyce70c172010-01-07 17:15:30 +0000556 quantum=GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000557 if (p->blue > quantum)
cristyce70c172010-01-07 17:15:30 +0000558 quantum=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000559 pixel.red=0.5*(pixel.red+factor*quantum);
560 pixel.green=0.5*(pixel.green+factor*quantum);
561 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristyce70c172010-01-07 17:15:30 +0000562 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
563 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
564 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +0000565 p++;
566 q++;
567 }
568 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
569 if (sync == MagickFalse)
570 status=MagickFalse;
571 if (image->progress_monitor != (MagickProgressMonitor) NULL)
572 {
573 MagickBooleanType
574 proceed;
575
cristy319a1e72010-02-21 15:13:11 +0000576#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000577 #pragma omp critical (MagickCore_BlueShiftImage)
578#endif
579 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
580 image->rows);
581 if (proceed == MagickFalse)
582 status=MagickFalse;
583 }
584 }
585 image_view=DestroyCacheView(image_view);
586 shift_view=DestroyCacheView(shift_view);
587 if (status == MagickFalse)
588 shift_image=DestroyImage(shift_image);
589 return(shift_image);
590}
591
592/*
593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
594% %
595% %
596% %
597% C h a r c o a l I m a g e %
598% %
599% %
600% %
601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
602%
603% CharcoalImage() creates a new image that is a copy of an existing one with
604% the edge highlighted. It allocates the memory necessary for the new Image
605% structure and returns a pointer to the new image.
606%
607% The format of the CharcoalImage method is:
608%
609% Image *CharcoalImage(const Image *image,const double radius,
610% const double sigma,ExceptionInfo *exception)
611%
612% A description of each parameter follows:
613%
614% o image: the image.
615%
616% o radius: the radius of the pixel neighborhood.
617%
618% o sigma: the standard deviation of the Gaussian, in pixels.
619%
620% o exception: return any errors or warnings in this structure.
621%
622*/
623MagickExport Image *CharcoalImage(const Image *image,const double radius,
624 const double sigma,ExceptionInfo *exception)
625{
626 Image
627 *charcoal_image,
628 *clone_image,
629 *edge_image;
630
631 assert(image != (Image *) NULL);
632 assert(image->signature == MagickSignature);
633 if (image->debug != MagickFalse)
634 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
635 assert(exception != (ExceptionInfo *) NULL);
636 assert(exception->signature == MagickSignature);
637 clone_image=CloneImage(image,0,0,MagickTrue,exception);
638 if (clone_image == (Image *) NULL)
639 return((Image *) NULL);
640 (void) SetImageType(clone_image,GrayscaleType);
641 edge_image=EdgeImage(clone_image,radius,exception);
642 clone_image=DestroyImage(clone_image);
643 if (edge_image == (Image *) NULL)
644 return((Image *) NULL);
645 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
646 edge_image=DestroyImage(edge_image);
647 if (charcoal_image == (Image *) NULL)
648 return((Image *) NULL);
649 (void) NormalizeImage(charcoal_image);
650 (void) NegateImage(charcoal_image,MagickFalse);
651 (void) SetImageType(charcoal_image,GrayscaleType);
652 return(charcoal_image);
653}
654
655/*
656%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
657% %
658% %
659% %
660% C o l o r i z e I m a g e %
661% %
662% %
663% %
664%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
665%
666% ColorizeImage() blends the fill color with each pixel in the image.
667% A percentage blend is specified with opacity. Control the application
668% of different color components by specifying a different percentage for
669% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
670%
671% The format of the ColorizeImage method is:
672%
673% Image *ColorizeImage(const Image *image,const char *opacity,
674% const PixelPacket colorize,ExceptionInfo *exception)
675%
676% A description of each parameter follows:
677%
678% o image: the image.
679%
680% o opacity: A character string indicating the level of opacity as a
681% percentage.
682%
683% o colorize: A color value.
684%
685% o exception: return any errors or warnings in this structure.
686%
687*/
688MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
689 const PixelPacket colorize,ExceptionInfo *exception)
690{
691#define ColorizeImageTag "Colorize/Image"
692
cristyc4c8d132010-01-07 01:58:38 +0000693 CacheView
694 *colorize_view,
695 *image_view;
696
cristy3ed852e2009-09-05 21:47:34 +0000697 GeometryInfo
698 geometry_info;
699
700 Image
701 *colorize_image;
702
cristy3ed852e2009-09-05 21:47:34 +0000703 MagickBooleanType
704 status;
705
cristybb503372010-05-27 20:51:26 +0000706 MagickOffsetType
707 progress;
708
cristy3ed852e2009-09-05 21:47:34 +0000709 MagickPixelPacket
710 pixel;
711
712 MagickStatusType
713 flags;
714
cristybb503372010-05-27 20:51:26 +0000715 ssize_t
716 y;
717
cristy3ed852e2009-09-05 21:47:34 +0000718 /*
719 Allocate colorized image.
720 */
721 assert(image != (const Image *) NULL);
722 assert(image->signature == MagickSignature);
723 if (image->debug != MagickFalse)
724 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
725 assert(exception != (ExceptionInfo *) NULL);
726 assert(exception->signature == MagickSignature);
727 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
728 exception);
729 if (colorize_image == (Image *) NULL)
730 return((Image *) NULL);
731 if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
732 {
733 InheritException(exception,&colorize_image->exception);
734 colorize_image=DestroyImage(colorize_image);
735 return((Image *) NULL);
736 }
737 if (opacity == (const char *) NULL)
738 return(colorize_image);
739 /*
740 Determine RGB values of the pen color.
741 */
742 flags=ParseGeometry(opacity,&geometry_info);
743 pixel.red=geometry_info.rho;
744 pixel.green=geometry_info.rho;
745 pixel.blue=geometry_info.rho;
746 pixel.opacity=(MagickRealType) OpaqueOpacity;
747 if ((flags & SigmaValue) != 0)
748 pixel.green=geometry_info.sigma;
749 if ((flags & XiValue) != 0)
750 pixel.blue=geometry_info.xi;
751 if ((flags & PsiValue) != 0)
752 pixel.opacity=geometry_info.psi;
753 /*
754 Colorize DirectClass image.
755 */
756 status=MagickTrue;
757 progress=0;
758 image_view=AcquireCacheView(image);
759 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000760#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000761 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000762#endif
cristybb503372010-05-27 20:51:26 +0000763 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000764 {
765 MagickBooleanType
766 sync;
767
768 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000769 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000770
cristybb503372010-05-27 20:51:26 +0000771 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000772 x;
773
774 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000775 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000776
777 if (status == MagickFalse)
778 continue;
779 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
780 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
781 exception);
782 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
783 {
784 status=MagickFalse;
785 continue;
786 }
cristybb503372010-05-27 20:51:26 +0000787 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000788 {
789 q->red=(Quantum) ((p->red*(100.0-pixel.red)+
790 colorize.red*pixel.red)/100.0);
791 q->green=(Quantum) ((p->green*(100.0-pixel.green)+
792 colorize.green*pixel.green)/100.0);
793 q->blue=(Quantum) ((p->blue*(100.0-pixel.blue)+
794 colorize.blue*pixel.blue)/100.0);
795 q->opacity=(Quantum) ((p->opacity*(100.0-pixel.opacity)+
796 colorize.opacity*pixel.opacity)/100.0);
797 p++;
798 q++;
799 }
800 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
801 if (sync == MagickFalse)
802 status=MagickFalse;
803 if (image->progress_monitor != (MagickProgressMonitor) NULL)
804 {
805 MagickBooleanType
806 proceed;
807
cristy319a1e72010-02-21 15:13:11 +0000808#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000809 #pragma omp critical (MagickCore_ColorizeImage)
810#endif
811 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
812 if (proceed == MagickFalse)
813 status=MagickFalse;
814 }
815 }
816 image_view=DestroyCacheView(image_view);
817 colorize_view=DestroyCacheView(colorize_view);
818 if (status == MagickFalse)
819 colorize_image=DestroyImage(colorize_image);
820 return(colorize_image);
821}
822
823/*
824%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
825% %
826% %
827% %
cristye6365592010-04-02 17:31:23 +0000828% C o l o r M a t r i x I m a g e %
829% %
830% %
831% %
832%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
833%
834% ColorMatrixImage() applies color transformation to an image. This method
835% permits saturation changes, hue rotation, luminance to alpha, and various
836% other effects. Although variable-sized transformation matrices can be used,
837% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
838% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
839% except offsets are in column 6 rather than 5 (in support of CMYKA images)
840% and offsets are normalized (divide Flash offset by 255).
841%
842% The format of the ColorMatrixImage method is:
843%
844% Image *ColorMatrixImage(const Image *image,
845% const KernelInfo *color_matrix,ExceptionInfo *exception)
846%
847% A description of each parameter follows:
848%
849% o image: the image.
850%
851% o color_matrix: the color matrix.
852%
853% o exception: return any errors or warnings in this structure.
854%
855*/
856MagickExport Image *ColorMatrixImage(const Image *image,
857 const KernelInfo *color_matrix,ExceptionInfo *exception)
858{
859#define ColorMatrixImageTag "ColorMatrix/Image"
860
861 CacheView
862 *color_view,
863 *image_view;
864
865 double
866 ColorMatrix[6][6] =
867 {
868 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
869 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
870 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
871 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
872 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
873 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
874 };
875
876 Image
877 *color_image;
878
cristye6365592010-04-02 17:31:23 +0000879 MagickBooleanType
880 status;
881
cristybb503372010-05-27 20:51:26 +0000882 MagickOffsetType
883 progress;
884
885 register ssize_t
cristye6365592010-04-02 17:31:23 +0000886 i;
887
cristybb503372010-05-27 20:51:26 +0000888 ssize_t
889 u,
890 v,
891 y;
892
cristye6365592010-04-02 17:31:23 +0000893 /*
894 Create color matrix.
895 */
896 assert(image != (Image *) NULL);
897 assert(image->signature == MagickSignature);
898 if (image->debug != MagickFalse)
899 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
900 assert(exception != (ExceptionInfo *) NULL);
901 assert(exception->signature == MagickSignature);
902 i=0;
cristybb503372010-05-27 20:51:26 +0000903 for (v=0; v < (ssize_t) color_matrix->height; v++)
904 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000905 {
906 if ((v < 6) && (u < 6))
907 ColorMatrix[v][u]=color_matrix->values[i];
908 i++;
909 }
910 /*
911 Initialize color image.
912 */
cristy12550e62010-06-07 12:46:40 +0000913 color_image=CloneImage(image,0,0,MagickTrue,exception);
cristye6365592010-04-02 17:31:23 +0000914 if (color_image == (Image *) NULL)
915 return((Image *) NULL);
916 if (SetImageStorageClass(color_image,DirectClass) == MagickFalse)
917 {
918 InheritException(exception,&color_image->exception);
919 color_image=DestroyImage(color_image);
920 return((Image *) NULL);
921 }
922 if (image->debug != MagickFalse)
923 {
924 char
925 format[MaxTextExtent],
926 *message;
927
928 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
929 " ColorMatrix image with color matrix:");
930 message=AcquireString("");
931 for (v=0; v < 6; v++)
932 {
933 *message='\0';
cristye8c25f92010-06-03 00:53:06 +0000934 (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000935 (void) ConcatenateString(&message,format);
936 for (u=0; u < 6; u++)
937 {
938 (void) FormatMagickString(format,MaxTextExtent,"%+f ",
939 ColorMatrix[v][u]);
940 (void) ConcatenateString(&message,format);
941 }
942 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
943 }
944 message=DestroyString(message);
945 }
946 /*
947 ColorMatrix image.
948 */
949 status=MagickTrue;
950 progress=0;
951 image_view=AcquireCacheView(image);
952 color_view=AcquireCacheView(color_image);
953#if defined(MAGICKCORE_OPENMP_SUPPORT)
954 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
955#endif
cristybb503372010-05-27 20:51:26 +0000956 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000957 {
958 MagickRealType
959 pixel;
960
961 register const IndexPacket
962 *restrict indexes;
963
964 register const PixelPacket
965 *restrict p;
966
cristybb503372010-05-27 20:51:26 +0000967 register ssize_t
cristye6365592010-04-02 17:31:23 +0000968 x;
969
970 register IndexPacket
971 *restrict color_indexes;
972
973 register PixelPacket
974 *restrict q;
975
976 if (status == MagickFalse)
977 continue;
978 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
979 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
980 exception);
981 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
982 {
983 status=MagickFalse;
984 continue;
985 }
986 indexes=GetCacheViewVirtualIndexQueue(image_view);
987 color_indexes=GetCacheViewAuthenticIndexQueue(color_view);
cristybb503372010-05-27 20:51:26 +0000988 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +0000989 {
cristybb503372010-05-27 20:51:26 +0000990 register ssize_t
cristye6365592010-04-02 17:31:23 +0000991 v;
992
cristybb503372010-05-27 20:51:26 +0000993 size_t
cristye6365592010-04-02 17:31:23 +0000994 height;
995
996 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +0000997 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +0000998 {
999 pixel=ColorMatrix[v][0]*p->red+ColorMatrix[v][1]*p->green+
1000 ColorMatrix[v][2]*p->blue;
1001 if (image->matte != MagickFalse)
1002 pixel+=ColorMatrix[v][3]*(QuantumRange-p->opacity);
1003 if (image->colorspace == CMYKColorspace)
1004 pixel+=ColorMatrix[v][4]*indexes[x];
1005 pixel+=QuantumRange*ColorMatrix[v][5];
1006 switch (v)
1007 {
1008 case 0: q->red=ClampToQuantum(pixel); break;
1009 case 1: q->green=ClampToQuantum(pixel); break;
1010 case 2: q->blue=ClampToQuantum(pixel); break;
1011 case 3:
1012 {
1013 if (image->matte != MagickFalse)
1014 q->opacity=ClampToQuantum(QuantumRange-pixel);
1015 break;
1016 }
1017 case 4:
1018 {
1019 if (image->colorspace == CMYKColorspace)
1020 color_indexes[x]=ClampToQuantum(pixel);
1021 break;
1022 }
1023 }
1024 }
1025 p++;
1026 q++;
1027 }
1028 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1029 status=MagickFalse;
1030 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1031 {
1032 MagickBooleanType
1033 proceed;
1034
1035#if defined(MAGICKCORE_OPENMP_SUPPORT)
1036 #pragma omp critical (MagickCore_ColorMatrixImage)
1037#endif
1038 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1039 image->rows);
1040 if (proceed == MagickFalse)
1041 status=MagickFalse;
1042 }
1043 }
1044 color_view=DestroyCacheView(color_view);
1045 image_view=DestroyCacheView(image_view);
1046 if (status == MagickFalse)
1047 color_image=DestroyImage(color_image);
1048 return(color_image);
1049}
1050
1051/*
1052%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1053% %
1054% %
1055% %
cristy3ed852e2009-09-05 21:47:34 +00001056+ D e s t r o y F x I n f o %
1057% %
1058% %
1059% %
1060%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1061%
1062% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1063%
1064% The format of the DestroyFxInfo method is:
1065%
1066% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1067%
1068% A description of each parameter follows:
1069%
1070% o fx_info: the fx info.
1071%
1072*/
1073MagickExport FxInfo *DestroyFxInfo(FxInfo *fx_info)
1074{
cristybb503372010-05-27 20:51:26 +00001075 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001076 i;
1077
1078 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1079 fx_info->expression=DestroyString(fx_info->expression);
1080 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1081 fx_info->colors=DestroySplayTree(fx_info->colors);
cristy0ea377f2011-03-24 00:54:19 +00001082 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +00001083 fx_info->resample_filter[i]=DestroyResampleFilter(
1084 fx_info->resample_filter[i]);
1085 fx_info->resample_filter=(ResampleFilter **) RelinquishMagickMemory(
1086 fx_info->resample_filter);
1087 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1088 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1089 return(fx_info);
1090}
1091
1092/*
1093%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1094% %
1095% %
1096% %
cristy3ed852e2009-09-05 21:47:34 +00001097+ 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 %
1098% %
1099% %
1100% %
1101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1102%
1103% FxEvaluateChannelExpression() evaluates an expression and returns the
1104% results.
1105%
1106% The format of the FxEvaluateExpression method is:
1107%
1108% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristybb503372010-05-27 20:51:26 +00001109% const ChannelType channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001110% MagickRealType *alpha,Exceptioninfo *exception)
1111% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1112% MagickRealType *alpha,Exceptioninfo *exception)
1113%
1114% A description of each parameter follows:
1115%
1116% o fx_info: the fx info.
1117%
1118% o channel: the channel.
1119%
1120% o x,y: the pixel position.
1121%
1122% o alpha: the result.
1123%
1124% o exception: return any errors or warnings in this structure.
1125%
1126*/
1127
cristy351842f2010-03-07 15:27:38 +00001128static inline double MagickMax(const double x,const double y)
1129{
1130 if (x > y)
1131 return(x);
1132 return(y);
1133}
1134
1135static inline double MagickMin(const double x,const double y)
1136{
1137 if (x < y)
1138 return(x);
1139 return(y);
1140}
1141
cristy3ed852e2009-09-05 21:47:34 +00001142static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
1143 ChannelType channel,const char *symbol,ExceptionInfo *exception)
1144{
1145 char
1146 key[MaxTextExtent],
1147 statistic[MaxTextExtent];
1148
1149 const char
1150 *value;
1151
1152 register const char
1153 *p;
1154
1155 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1156 if (*p == '.')
1157 switch (*++p) /* e.g. depth.r */
1158 {
1159 case 'r': channel=RedChannel; break;
1160 case 'g': channel=GreenChannel; break;
1161 case 'b': channel=BlueChannel; break;
1162 case 'c': channel=CyanChannel; break;
1163 case 'm': channel=MagentaChannel; break;
1164 case 'y': channel=YellowChannel; break;
1165 case 'k': channel=BlackChannel; break;
1166 default: break;
1167 }
cristye8c25f92010-06-03 00:53:06 +00001168 (void) FormatMagickString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
1169 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001170 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1171 if (value != (const char *) NULL)
cristyf2f27272009-12-17 14:48:46 +00001172 return(QuantumScale*StringToDouble(value));
cristy3ed852e2009-09-05 21:47:34 +00001173 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1174 if (LocaleNCompare(symbol,"depth",5) == 0)
1175 {
cristybb503372010-05-27 20:51:26 +00001176 size_t
cristy3ed852e2009-09-05 21:47:34 +00001177 depth;
1178
1179 depth=GetImageChannelDepth(image,channel,exception);
cristye8c25f92010-06-03 00:53:06 +00001180 (void) FormatMagickString(statistic,MaxTextExtent,"%.20g",(double)
1181 depth);
cristy3ed852e2009-09-05 21:47:34 +00001182 }
1183 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1184 {
1185 double
1186 kurtosis,
1187 skewness;
1188
1189 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1190 exception);
cristye7f51092010-01-17 00:39:37 +00001191 (void) FormatMagickString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001192 }
1193 if (LocaleNCompare(symbol,"maxima",6) == 0)
1194 {
1195 double
1196 maxima,
1197 minima;
1198
1199 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristye7f51092010-01-17 00:39:37 +00001200 (void) FormatMagickString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001201 }
1202 if (LocaleNCompare(symbol,"mean",4) == 0)
1203 {
1204 double
1205 mean,
1206 standard_deviation;
1207
1208 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1209 exception);
cristye7f51092010-01-17 00:39:37 +00001210 (void) FormatMagickString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001211 }
1212 if (LocaleNCompare(symbol,"minima",6) == 0)
1213 {
1214 double
1215 maxima,
1216 minima;
1217
1218 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristye7f51092010-01-17 00:39:37 +00001219 (void) FormatMagickString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001220 }
1221 if (LocaleNCompare(symbol,"skewness",8) == 0)
1222 {
1223 double
1224 kurtosis,
1225 skewness;
1226
1227 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1228 exception);
cristye7f51092010-01-17 00:39:37 +00001229 (void) FormatMagickString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001230 }
1231 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1232 {
1233 double
1234 mean,
1235 standard_deviation;
1236
1237 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1238 exception);
cristye7f51092010-01-17 00:39:37 +00001239 (void) FormatMagickString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001240 standard_deviation);
1241 }
1242 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1243 ConstantString(statistic));
cristyf2f27272009-12-17 14:48:46 +00001244 return(QuantumScale*StringToDouble(statistic));
cristy3ed852e2009-09-05 21:47:34 +00001245}
1246
1247static MagickRealType
cristye85007d2010-06-06 22:51:36 +00001248 FxEvaluateSubexpression(FxInfo *,const ChannelType,const ssize_t,
1249 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001250
1251static inline MagickRealType FxMax(FxInfo *fx_info,const ChannelType channel,
cristye85007d2010-06-06 22:51:36 +00001252 const ssize_t x,const ssize_t y,const char *expression,
1253 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001254{
1255 MagickRealType
1256 alpha,
1257 beta;
1258
1259 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1260 return((MagickRealType) MagickMax((double) alpha,(double) beta));
1261}
1262
1263static inline MagickRealType FxMin(FxInfo *fx_info,ChannelType channel,
cristye85007d2010-06-06 22:51:36 +00001264 const ssize_t x,const ssize_t y,const char *expression,
1265 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001266{
1267 MagickRealType
1268 alpha,
1269 beta;
1270
1271 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1272 return((MagickRealType) MagickMin((double) alpha,(double) beta));
1273}
1274
1275static inline const char *FxSubexpression(const char *expression,
1276 ExceptionInfo *exception)
1277{
1278 const char
1279 *subexpression;
1280
cristybb503372010-05-27 20:51:26 +00001281 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001282 level;
1283
1284 level=0;
1285 subexpression=expression;
1286 while ((*subexpression != '\0') &&
1287 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1288 {
1289 if (strchr("(",(int) *subexpression) != (char *) NULL)
1290 level++;
1291 else
1292 if (strchr(")",(int) *subexpression) != (char *) NULL)
1293 level--;
1294 subexpression++;
1295 }
1296 if (*subexpression == '\0')
1297 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1298 "UnbalancedParenthesis","`%s'",expression);
1299 return(subexpression);
1300}
1301
1302static MagickRealType FxGetSymbol(FxInfo *fx_info,const ChannelType channel,
cristye85007d2010-06-06 22:51:36 +00001303 const ssize_t x,const ssize_t y,const char *expression,
1304 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001305{
1306 char
1307 *q,
1308 subexpression[MaxTextExtent],
1309 symbol[MaxTextExtent];
1310
1311 const char
1312 *p,
1313 *value;
1314
1315 Image
1316 *image;
1317
1318 MagickPixelPacket
1319 pixel;
1320
1321 MagickRealType
1322 alpha,
1323 beta;
1324
1325 PointInfo
1326 point;
1327
cristybb503372010-05-27 20:51:26 +00001328 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001329 i;
1330
1331 size_t
1332 length;
1333
cristybb503372010-05-27 20:51:26 +00001334 size_t
cristy3ed852e2009-09-05 21:47:34 +00001335 level;
1336
1337 p=expression;
1338 i=GetImageIndexInList(fx_info->images);
1339 level=0;
1340 point.x=(double) x;
1341 point.y=(double) y;
1342 if (isalpha((int) *(p+1)) == 0)
1343 {
1344 if (strchr("suv",(int) *p) != (char *) NULL)
1345 {
1346 switch (*p)
1347 {
1348 case 's':
1349 default:
1350 {
1351 i=GetImageIndexInList(fx_info->images);
1352 break;
1353 }
1354 case 'u': i=0; break;
1355 case 'v': i=1; break;
1356 }
1357 p++;
1358 if (*p == '[')
1359 {
1360 level++;
1361 q=subexpression;
1362 for (p++; *p != '\0'; )
1363 {
1364 if (*p == '[')
1365 level++;
1366 else
1367 if (*p == ']')
1368 {
1369 level--;
1370 if (level == 0)
1371 break;
1372 }
1373 *q++=(*p++);
1374 }
1375 *q='\0';
1376 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1377 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001378 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001379 p++;
1380 }
1381 if (*p == '.')
1382 p++;
1383 }
1384 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1385 {
1386 p++;
1387 if (*p == '{')
1388 {
1389 level++;
1390 q=subexpression;
1391 for (p++; *p != '\0'; )
1392 {
1393 if (*p == '{')
1394 level++;
1395 else
1396 if (*p == '}')
1397 {
1398 level--;
1399 if (level == 0)
1400 break;
1401 }
1402 *q++=(*p++);
1403 }
1404 *q='\0';
1405 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1406 &beta,exception);
1407 point.x=alpha;
1408 point.y=beta;
1409 p++;
1410 }
1411 else
1412 if (*p == '[')
1413 {
1414 level++;
1415 q=subexpression;
1416 for (p++; *p != '\0'; )
1417 {
1418 if (*p == '[')
1419 level++;
1420 else
1421 if (*p == ']')
1422 {
1423 level--;
1424 if (level == 0)
1425 break;
1426 }
1427 *q++=(*p++);
1428 }
1429 *q='\0';
1430 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1431 &beta,exception);
1432 point.x+=alpha;
1433 point.y+=beta;
1434 p++;
1435 }
1436 if (*p == '.')
1437 p++;
1438 }
1439 }
1440 length=GetImageListLength(fx_info->images);
1441 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001442 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001443 i%=length;
1444 image=GetImageFromList(fx_info->images,i);
1445 if (image == (Image *) NULL)
1446 {
1447 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1448 "NoSuchImage","`%s'",expression);
1449 return(0.0);
1450 }
1451 (void) ResamplePixelColor(fx_info->resample_filter[i],point.x,point.y,&pixel);
1452 if ((strlen(p) > 2) &&
1453 (LocaleCompare(p,"intensity") != 0) &&
1454 (LocaleCompare(p,"luminance") != 0) &&
1455 (LocaleCompare(p,"hue") != 0) &&
1456 (LocaleCompare(p,"saturation") != 0) &&
1457 (LocaleCompare(p,"lightness") != 0))
1458 {
1459 char
1460 name[MaxTextExtent];
1461
1462 (void) CopyMagickString(name,p,MaxTextExtent);
1463 for (q=name+(strlen(name)-1); q > name; q--)
1464 {
1465 if (*q == ')')
1466 break;
1467 if (*q == '.')
1468 {
1469 *q='\0';
1470 break;
1471 }
1472 }
1473 if ((strlen(name) > 2) &&
1474 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1475 {
1476 MagickPixelPacket
1477 *color;
1478
1479 color=(MagickPixelPacket *) GetValueFromSplayTree(fx_info->colors,
1480 name);
1481 if (color != (MagickPixelPacket *) NULL)
1482 {
1483 pixel=(*color);
1484 p+=strlen(name);
1485 }
1486 else
1487 if (QueryMagickColor(name,&pixel,fx_info->exception) != MagickFalse)
1488 {
1489 (void) AddValueToSplayTree(fx_info->colors,ConstantString(name),
1490 CloneMagickPixelPacket(&pixel));
1491 p+=strlen(name);
1492 }
1493 }
1494 }
1495 (void) CopyMagickString(symbol,p,MaxTextExtent);
1496 StripString(symbol);
1497 if (*symbol == '\0')
1498 {
1499 switch (channel)
1500 {
1501 case RedChannel: return(QuantumScale*pixel.red);
1502 case GreenChannel: return(QuantumScale*pixel.green);
1503 case BlueChannel: return(QuantumScale*pixel.blue);
1504 case OpacityChannel:
1505 {
1506 if (pixel.matte == MagickFalse)
cristycb180922011-03-11 14:41:24 +00001507 return(1.0);
cristy46f08202010-01-10 04:04:21 +00001508 return((MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel)));
cristy3ed852e2009-09-05 21:47:34 +00001509 }
1510 case IndexChannel:
1511 {
1512 if (image->colorspace != CMYKColorspace)
1513 {
1514 (void) ThrowMagickException(exception,GetMagickModule(),
1515 OptionError,"ColorSeparatedImageRequired","`%s'",
1516 image->filename);
1517 return(0.0);
1518 }
1519 return(QuantumScale*pixel.index);
1520 }
cristyf364ed42010-12-15 01:54:43 +00001521 case DefaultChannels:
1522 {
1523 return(QuantumScale*MagickPixelIntensityToQuantum(&pixel));
1524 }
cristy3ed852e2009-09-05 21:47:34 +00001525 default:
1526 break;
1527 }
1528 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1529 "UnableToParseExpression","`%s'",p);
1530 return(0.0);
1531 }
1532 switch (*symbol)
1533 {
1534 case 'A':
1535 case 'a':
1536 {
1537 if (LocaleCompare(symbol,"a") == 0)
cristy46f08202010-01-10 04:04:21 +00001538 return((MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel)));
cristy3ed852e2009-09-05 21:47:34 +00001539 break;
1540 }
1541 case 'B':
1542 case 'b':
1543 {
1544 if (LocaleCompare(symbol,"b") == 0)
1545 return(QuantumScale*pixel.blue);
1546 break;
1547 }
1548 case 'C':
1549 case 'c':
1550 {
1551 if (LocaleNCompare(symbol,"channel",7) == 0)
1552 {
1553 GeometryInfo
1554 channel_info;
1555
1556 MagickStatusType
1557 flags;
1558
1559 flags=ParseGeometry(symbol+7,&channel_info);
1560 if (image->colorspace == CMYKColorspace)
1561 switch (channel)
1562 {
1563 case CyanChannel:
1564 {
1565 if ((flags & RhoValue) == 0)
1566 return(0.0);
1567 return(channel_info.rho);
1568 }
1569 case MagentaChannel:
1570 {
1571 if ((flags & SigmaValue) == 0)
1572 return(0.0);
1573 return(channel_info.sigma);
1574 }
1575 case YellowChannel:
1576 {
1577 if ((flags & XiValue) == 0)
1578 return(0.0);
1579 return(channel_info.xi);
1580 }
1581 case BlackChannel:
1582 {
1583 if ((flags & PsiValue) == 0)
1584 return(0.0);
1585 return(channel_info.psi);
1586 }
1587 case OpacityChannel:
1588 {
1589 if ((flags & ChiValue) == 0)
1590 return(0.0);
1591 return(channel_info.chi);
1592 }
1593 default:
1594 return(0.0);
1595 }
1596 switch (channel)
1597 {
1598 case RedChannel:
1599 {
1600 if ((flags & RhoValue) == 0)
1601 return(0.0);
1602 return(channel_info.rho);
1603 }
1604 case GreenChannel:
1605 {
1606 if ((flags & SigmaValue) == 0)
1607 return(0.0);
1608 return(channel_info.sigma);
1609 }
1610 case BlueChannel:
1611 {
1612 if ((flags & XiValue) == 0)
1613 return(0.0);
1614 return(channel_info.xi);
1615 }
1616 case OpacityChannel:
1617 {
1618 if ((flags & PsiValue) == 0)
1619 return(0.0);
1620 return(channel_info.psi);
1621 }
1622 case IndexChannel:
1623 {
1624 if ((flags & ChiValue) == 0)
1625 return(0.0);
1626 return(channel_info.chi);
1627 }
1628 default:
1629 return(0.0);
1630 }
1631 return(0.0);
1632 }
1633 if (LocaleCompare(symbol,"c") == 0)
1634 return(QuantumScale*pixel.red);
1635 break;
1636 }
1637 case 'D':
1638 case 'd':
1639 {
1640 if (LocaleNCompare(symbol,"depth",5) == 0)
1641 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1642 break;
1643 }
1644 case 'G':
1645 case 'g':
1646 {
1647 if (LocaleCompare(symbol,"g") == 0)
1648 return(QuantumScale*pixel.green);
1649 break;
1650 }
1651 case 'K':
1652 case 'k':
1653 {
1654 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1655 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1656 if (LocaleCompare(symbol,"k") == 0)
1657 {
1658 if (image->colorspace != CMYKColorspace)
1659 {
1660 (void) ThrowMagickException(exception,GetMagickModule(),
1661 OptionError,"ColorSeparatedImageRequired","`%s'",
1662 image->filename);
1663 return(0.0);
1664 }
1665 return(QuantumScale*pixel.index);
1666 }
1667 break;
1668 }
1669 case 'H':
1670 case 'h':
1671 {
1672 if (LocaleCompare(symbol,"h") == 0)
1673 return((MagickRealType) image->rows);
1674 if (LocaleCompare(symbol,"hue") == 0)
1675 {
1676 double
1677 hue,
1678 lightness,
1679 saturation;
1680
cristyce70c172010-01-07 17:15:30 +00001681 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1682 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001683 return(hue);
1684 }
1685 break;
1686 }
1687 case 'I':
1688 case 'i':
1689 {
1690 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1691 (LocaleCompare(symbol,"image.minima") == 0) ||
1692 (LocaleCompare(symbol,"image.maxima") == 0) ||
1693 (LocaleCompare(symbol,"image.mean") == 0) ||
1694 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1695 (LocaleCompare(symbol,"image.skewness") == 0) ||
1696 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1697 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1698 if (LocaleCompare(symbol,"image.resolution.x") == 0)
1699 return(image->x_resolution);
1700 if (LocaleCompare(symbol,"image.resolution.y") == 0)
1701 return(image->y_resolution);
1702 if (LocaleCompare(symbol,"intensity") == 0)
1703 return(QuantumScale*MagickPixelIntensityToQuantum(&pixel));
1704 if (LocaleCompare(symbol,"i") == 0)
1705 return((MagickRealType) x);
1706 break;
1707 }
1708 case 'J':
1709 case 'j':
1710 {
1711 if (LocaleCompare(symbol,"j") == 0)
1712 return((MagickRealType) y);
1713 break;
1714 }
1715 case 'L':
1716 case 'l':
1717 {
1718 if (LocaleCompare(symbol,"lightness") == 0)
1719 {
1720 double
1721 hue,
1722 lightness,
1723 saturation;
1724
cristyce70c172010-01-07 17:15:30 +00001725 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1726 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001727 return(lightness);
1728 }
1729 if (LocaleCompare(symbol,"luminance") == 0)
1730 {
1731 double
1732 luminence;
1733
1734 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1735 return(QuantumScale*luminence);
1736 }
1737 break;
1738 }
1739 case 'M':
1740 case 'm':
1741 {
1742 if (LocaleNCompare(symbol,"maxima",6) == 0)
1743 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1744 if (LocaleNCompare(symbol,"mean",4) == 0)
1745 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1746 if (LocaleNCompare(symbol,"minima",6) == 0)
1747 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1748 if (LocaleCompare(symbol,"m") == 0)
1749 return(QuantumScale*pixel.blue);
1750 break;
1751 }
1752 case 'N':
1753 case 'n':
1754 {
1755 if (LocaleCompare(symbol,"n") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001756 return((MagickRealType) GetImageListLength(fx_info->images));
cristy3ed852e2009-09-05 21:47:34 +00001757 break;
1758 }
1759 case 'O':
1760 case 'o':
1761 {
1762 if (LocaleCompare(symbol,"o") == 0)
1763 return(QuantumScale*pixel.opacity);
1764 break;
1765 }
1766 case 'P':
1767 case 'p':
1768 {
1769 if (LocaleCompare(symbol,"page.height") == 0)
1770 return((MagickRealType) image->page.height);
1771 if (LocaleCompare(symbol,"page.width") == 0)
1772 return((MagickRealType) image->page.width);
1773 if (LocaleCompare(symbol,"page.x") == 0)
1774 return((MagickRealType) image->page.x);
1775 if (LocaleCompare(symbol,"page.y") == 0)
1776 return((MagickRealType) image->page.y);
1777 break;
1778 }
1779 case 'R':
1780 case 'r':
1781 {
1782 if (LocaleCompare(symbol,"resolution.x") == 0)
1783 return(image->x_resolution);
1784 if (LocaleCompare(symbol,"resolution.y") == 0)
1785 return(image->y_resolution);
1786 if (LocaleCompare(symbol,"r") == 0)
1787 return(QuantumScale*pixel.red);
1788 break;
1789 }
1790 case 'S':
1791 case 's':
1792 {
1793 if (LocaleCompare(symbol,"saturation") == 0)
1794 {
1795 double
1796 hue,
1797 lightness,
1798 saturation;
1799
cristyce70c172010-01-07 17:15:30 +00001800 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1801 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001802 return(saturation);
1803 }
1804 if (LocaleNCompare(symbol,"skewness",8) == 0)
1805 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1806 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1807 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1808 break;
1809 }
1810 case 'T':
1811 case 't':
1812 {
1813 if (LocaleCompare(symbol,"t") == 0)
anthony374f5dd2011-03-25 10:08:53 +00001814 return((MagickRealType) fx_info->images->scene);
cristy3ed852e2009-09-05 21:47:34 +00001815 break;
1816 }
1817 case 'W':
1818 case 'w':
1819 {
1820 if (LocaleCompare(symbol,"w") == 0)
1821 return((MagickRealType) image->columns);
1822 break;
1823 }
1824 case 'Y':
1825 case 'y':
1826 {
1827 if (LocaleCompare(symbol,"y") == 0)
1828 return(QuantumScale*pixel.green);
1829 break;
1830 }
1831 case 'Z':
1832 case 'z':
1833 {
1834 if (LocaleCompare(symbol,"z") == 0)
1835 {
1836 MagickRealType
1837 depth;
1838
1839 depth=(MagickRealType) GetImageChannelDepth(image,channel,
1840 fx_info->exception);
1841 return(depth);
1842 }
1843 break;
1844 }
1845 default:
1846 break;
1847 }
1848 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1849 if (value != (const char *) NULL)
cristyf2f27272009-12-17 14:48:46 +00001850 return((MagickRealType) StringToDouble(value));
cristy3ed852e2009-09-05 21:47:34 +00001851 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1852 "UnableToParseExpression","`%s'",symbol);
1853 return(0.0);
1854}
1855
1856static const char *FxOperatorPrecedence(const char *expression,
1857 ExceptionInfo *exception)
1858{
1859 typedef enum
1860 {
1861 UndefinedPrecedence,
1862 NullPrecedence,
1863 BitwiseComplementPrecedence,
1864 ExponentPrecedence,
cristy116af162010-08-13 01:25:47 +00001865 ExponentialNotationPrecedence,
cristy3ed852e2009-09-05 21:47:34 +00001866 MultiplyPrecedence,
1867 AdditionPrecedence,
1868 ShiftPrecedence,
1869 RelationalPrecedence,
1870 EquivalencyPrecedence,
1871 BitwiseAndPrecedence,
1872 BitwiseOrPrecedence,
1873 LogicalAndPrecedence,
1874 LogicalOrPrecedence,
1875 TernaryPrecedence,
1876 AssignmentPrecedence,
1877 CommaPrecedence,
1878 SeparatorPrecedence
1879 } FxPrecedence;
1880
1881 FxPrecedence
1882 precedence,
1883 target;
1884
1885 register const char
1886 *subexpression;
1887
1888 register int
1889 c;
1890
cristybb503372010-05-27 20:51:26 +00001891 size_t
cristy3ed852e2009-09-05 21:47:34 +00001892 level;
1893
1894 c=0;
1895 level=0;
1896 subexpression=(const char *) NULL;
1897 target=NullPrecedence;
1898 while (*expression != '\0')
1899 {
1900 precedence=UndefinedPrecedence;
1901 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1902 {
1903 expression++;
1904 continue;
1905 }
cristy488fa882010-03-01 22:34:24 +00001906 switch (*expression)
1907 {
1908 case 'A':
1909 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001910 {
cristy488fa882010-03-01 22:34:24 +00001911 if (LocaleNCompare(expression,"atan2",5) == 0)
1912 {
1913 expression+=5;
1914 break;
1915 }
1916 break;
cristy3ed852e2009-09-05 21:47:34 +00001917 }
cristy488fa882010-03-01 22:34:24 +00001918 case 'J':
1919 case 'j':
1920 {
1921 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1922 (LocaleNCompare(expression,"j1",2) == 0))
1923 {
1924 expression+=2;
1925 break;
1926 }
1927 break;
1928 }
cristy2def9322010-06-18 23:59:37 +00001929 case '#':
1930 {
1931 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1932 expression++;
1933 break;
1934 }
cristy488fa882010-03-01 22:34:24 +00001935 default:
1936 break;
1937 }
cristy3ed852e2009-09-05 21:47:34 +00001938 if ((c == (int) '{') || (c == (int) '['))
1939 level++;
1940 else
1941 if ((c == (int) '}') || (c == (int) ']'))
1942 level--;
1943 if (level == 0)
1944 switch ((unsigned char) *expression)
1945 {
1946 case '~':
1947 case '!':
1948 {
1949 precedence=BitwiseComplementPrecedence;
1950 break;
1951 }
1952 case '^':
cristy6621e252010-08-13 00:42:57 +00001953 case '@':
cristy3ed852e2009-09-05 21:47:34 +00001954 {
1955 precedence=ExponentPrecedence;
1956 break;
1957 }
1958 default:
1959 {
1960 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1961 (strchr(")",c) != (char *) NULL))) &&
1962 (((islower((int) ((char) *expression)) != 0) ||
1963 (strchr("(",(int) *expression) != (char *) NULL)) ||
1964 ((isdigit((int) ((char) c)) == 0) &&
1965 (isdigit((int) ((char) *expression)) != 0))) &&
1966 (strchr("xy",(int) *expression) == (char *) NULL))
1967 precedence=MultiplyPrecedence;
1968 break;
1969 }
1970 case '*':
1971 case '/':
1972 case '%':
1973 {
1974 precedence=MultiplyPrecedence;
1975 break;
1976 }
1977 case '+':
1978 case '-':
1979 {
1980 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1981 (isalpha(c) != 0))
1982 precedence=AdditionPrecedence;
1983 break;
1984 }
1985 case LeftShiftOperator:
1986 case RightShiftOperator:
1987 {
1988 precedence=ShiftPrecedence;
1989 break;
1990 }
1991 case '<':
1992 case LessThanEqualOperator:
1993 case GreaterThanEqualOperator:
1994 case '>':
1995 {
1996 precedence=RelationalPrecedence;
1997 break;
1998 }
1999 case EqualOperator:
2000 case NotEqualOperator:
2001 {
2002 precedence=EquivalencyPrecedence;
2003 break;
2004 }
2005 case '&':
2006 {
2007 precedence=BitwiseAndPrecedence;
2008 break;
2009 }
2010 case '|':
2011 {
2012 precedence=BitwiseOrPrecedence;
2013 break;
2014 }
2015 case LogicalAndOperator:
2016 {
2017 precedence=LogicalAndPrecedence;
2018 break;
2019 }
2020 case LogicalOrOperator:
2021 {
2022 precedence=LogicalOrPrecedence;
2023 break;
2024 }
cristy116af162010-08-13 01:25:47 +00002025 case ExponentialNotation:
2026 {
2027 precedence=ExponentialNotationPrecedence;
2028 break;
2029 }
cristy3ed852e2009-09-05 21:47:34 +00002030 case ':':
2031 case '?':
2032 {
2033 precedence=TernaryPrecedence;
2034 break;
2035 }
2036 case '=':
2037 {
2038 precedence=AssignmentPrecedence;
2039 break;
2040 }
2041 case ',':
2042 {
2043 precedence=CommaPrecedence;
2044 break;
2045 }
2046 case ';':
2047 {
2048 precedence=SeparatorPrecedence;
2049 break;
2050 }
2051 }
2052 if ((precedence == BitwiseComplementPrecedence) ||
2053 (precedence == TernaryPrecedence) ||
2054 (precedence == AssignmentPrecedence))
2055 {
2056 if (precedence > target)
2057 {
2058 /*
2059 Right-to-left associativity.
2060 */
2061 target=precedence;
2062 subexpression=expression;
2063 }
2064 }
2065 else
2066 if (precedence >= target)
2067 {
2068 /*
2069 Left-to-right associativity.
2070 */
2071 target=precedence;
2072 subexpression=expression;
2073 }
2074 if (strchr("(",(int) *expression) != (char *) NULL)
2075 expression=FxSubexpression(expression,exception);
2076 c=(int) (*expression++);
2077 }
2078 return(subexpression);
2079}
2080
2081static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristye85007d2010-06-06 22:51:36 +00002082 const ChannelType channel,const ssize_t x,const ssize_t y,
2083 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002084{
2085 char
2086 *q,
2087 subexpression[MaxTextExtent];
2088
2089 MagickRealType
2090 alpha,
2091 gamma;
2092
2093 register const char
2094 *p;
2095
2096 *beta=0.0;
2097 if (exception->severity != UndefinedException)
2098 return(0.0);
2099 while (isspace((int) *expression) != 0)
2100 expression++;
2101 if (*expression == '\0')
2102 {
2103 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2104 "MissingExpression","`%s'",expression);
2105 return(0.0);
2106 }
cristy66322f02010-05-17 11:40:48 +00002107 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002108 p=FxOperatorPrecedence(expression,exception);
2109 if (p != (const char *) NULL)
2110 {
2111 (void) CopyMagickString(subexpression,expression,(size_t)
2112 (p-expression+1));
2113 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2114 exception);
2115 switch ((unsigned char) *p)
2116 {
2117 case '~':
2118 {
2119 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002120 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002121 return(*beta);
2122 }
2123 case '!':
2124 {
2125 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2126 return(*beta == 0.0 ? 1.0 : 0.0);
2127 }
2128 case '^':
2129 {
2130 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2131 channel,x,y,++p,beta,exception));
2132 return(*beta);
2133 }
2134 case '*':
cristy116af162010-08-13 01:25:47 +00002135 case ExponentialNotation:
cristy3ed852e2009-09-05 21:47:34 +00002136 {
2137 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2138 return(alpha*(*beta));
2139 }
2140 case '/':
2141 {
2142 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2143 if (*beta == 0.0)
2144 {
2145 if (exception->severity == UndefinedException)
2146 (void) ThrowMagickException(exception,GetMagickModule(),
2147 OptionError,"DivideByZero","`%s'",expression);
2148 return(0.0);
2149 }
2150 return(alpha/(*beta));
2151 }
2152 case '%':
2153 {
2154 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2155 *beta=fabs(floor(((double) *beta)+0.5));
2156 if (*beta == 0.0)
2157 {
2158 (void) ThrowMagickException(exception,GetMagickModule(),
2159 OptionError,"DivideByZero","`%s'",expression);
2160 return(0.0);
2161 }
2162 return(fmod((double) alpha,(double) *beta));
2163 }
2164 case '+':
2165 {
2166 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2167 return(alpha+(*beta));
2168 }
2169 case '-':
2170 {
2171 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2172 return(alpha-(*beta));
2173 }
2174 case LeftShiftOperator:
2175 {
2176 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002177 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002178 (gamma+0.5));
2179 return(*beta);
2180 }
2181 case RightShiftOperator:
2182 {
2183 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002184 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002185 (gamma+0.5));
2186 return(*beta);
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 LessThanEqualOperator:
2194 {
2195 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2196 return(alpha <= *beta ? 1.0 : 0.0);
2197 }
2198 case '>':
2199 {
2200 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2201 return(alpha > *beta ? 1.0 : 0.0);
2202 }
2203 case GreaterThanEqualOperator:
2204 {
2205 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2206 return(alpha >= *beta ? 1.0 : 0.0);
2207 }
2208 case EqualOperator:
2209 {
2210 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2211 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2212 }
2213 case NotEqualOperator:
2214 {
2215 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2216 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2217 }
2218 case '&':
2219 {
2220 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002221 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002222 (gamma+0.5));
2223 return(*beta);
2224 }
2225 case '|':
2226 {
2227 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002228 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002229 (gamma+0.5));
2230 return(*beta);
2231 }
2232 case LogicalAndOperator:
2233 {
2234 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2235 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2236 return(*beta);
2237 }
2238 case LogicalOrOperator:
2239 {
2240 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2241 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2242 return(*beta);
2243 }
2244 case '?':
2245 {
2246 MagickRealType
2247 gamma;
2248
2249 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2250 q=subexpression;
2251 p=StringToken(":",&q);
2252 if (q == (char *) NULL)
2253 {
2254 (void) ThrowMagickException(exception,GetMagickModule(),
2255 OptionError,"UnableToParseExpression","`%s'",subexpression);
2256 return(0.0);
2257 }
2258 if (fabs((double) alpha) > MagickEpsilon)
2259 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2260 else
2261 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2262 return(gamma);
2263 }
2264 case '=':
2265 {
2266 char
2267 numeric[MaxTextExtent];
2268
2269 q=subexpression;
2270 while (isalpha((int) ((unsigned char) *q)) != 0)
2271 q++;
2272 if (*q != '\0')
2273 {
2274 (void) ThrowMagickException(exception,GetMagickModule(),
2275 OptionError,"UnableToParseExpression","`%s'",subexpression);
2276 return(0.0);
2277 }
2278 ClearMagickException(exception);
2279 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristye7f51092010-01-17 00:39:37 +00002280 (void) FormatMagickString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002281 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002282 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2283 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2284 subexpression),ConstantString(numeric));
2285 return(*beta);
2286 }
2287 case ',':
2288 {
2289 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2290 return(alpha);
2291 }
2292 case ';':
2293 {
2294 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2295 return(*beta);
2296 }
2297 default:
2298 {
2299 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2300 exception);
2301 return(gamma);
2302 }
2303 }
2304 }
2305 if (strchr("(",(int) *expression) != (char *) NULL)
2306 {
2307 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2308 subexpression[strlen(subexpression)-1]='\0';
2309 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2310 exception);
2311 return(gamma);
2312 }
cristy8b8a3ae2010-10-23 18:49:46 +00002313 switch (*expression)
cristy3ed852e2009-09-05 21:47:34 +00002314 {
2315 case '+':
2316 {
2317 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2318 exception);
2319 return(1.0*gamma);
2320 }
2321 case '-':
2322 {
2323 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2324 exception);
2325 return(-1.0*gamma);
2326 }
2327 case '~':
2328 {
2329 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2330 exception);
cristybb503372010-05-27 20:51:26 +00002331 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002332 }
2333 case 'A':
2334 case 'a':
2335 {
2336 if (LocaleNCompare(expression,"abs",3) == 0)
2337 {
2338 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2339 exception);
2340 return((MagickRealType) fabs((double) alpha));
2341 }
2342 if (LocaleNCompare(expression,"acos",4) == 0)
2343 {
2344 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2345 exception);
2346 return((MagickRealType) acos((double) alpha));
2347 }
cristy43c22f42010-03-30 12:34:07 +00002348#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002349 if (LocaleNCompare(expression,"airy",4) == 0)
2350 {
2351 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2352 exception);
2353 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002354 return(1.0);
2355 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002356 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002357 }
cristy43c22f42010-03-30 12:34:07 +00002358#endif
cristy3ed852e2009-09-05 21:47:34 +00002359 if (LocaleNCompare(expression,"asin",4) == 0)
2360 {
2361 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2362 exception);
2363 return((MagickRealType) asin((double) alpha));
2364 }
2365 if (LocaleNCompare(expression,"alt",3) == 0)
2366 {
2367 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2368 exception);
cristybb503372010-05-27 20:51:26 +00002369 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002370 }
2371 if (LocaleNCompare(expression,"atan2",5) == 0)
2372 {
2373 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2374 exception);
2375 return((MagickRealType) atan2((double) alpha,(double) *beta));
2376 }
2377 if (LocaleNCompare(expression,"atan",4) == 0)
2378 {
2379 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2380 exception);
2381 return((MagickRealType) atan((double) alpha));
2382 }
2383 if (LocaleCompare(expression,"a") == 0)
2384 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2385 break;
2386 }
2387 case 'B':
2388 case 'b':
2389 {
2390 if (LocaleCompare(expression,"b") == 0)
2391 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2392 break;
2393 }
2394 case 'C':
2395 case 'c':
2396 {
2397 if (LocaleNCompare(expression,"ceil",4) == 0)
2398 {
2399 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2400 exception);
2401 return((MagickRealType) ceil((double) alpha));
2402 }
2403 if (LocaleNCompare(expression,"cosh",4) == 0)
2404 {
2405 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2406 exception);
2407 return((MagickRealType) cosh((double) alpha));
2408 }
2409 if (LocaleNCompare(expression,"cos",3) == 0)
2410 {
2411 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2412 exception);
2413 return((MagickRealType) cos((double) alpha));
2414 }
2415 if (LocaleCompare(expression,"c") == 0)
2416 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2417 break;
2418 }
2419 case 'D':
2420 case 'd':
2421 {
2422 if (LocaleNCompare(expression,"debug",5) == 0)
2423 {
2424 const char
2425 *type;
2426
2427 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2428 exception);
2429 if (fx_info->images->colorspace == CMYKColorspace)
2430 switch (channel)
2431 {
2432 case CyanChannel: type="cyan"; break;
2433 case MagentaChannel: type="magenta"; break;
2434 case YellowChannel: type="yellow"; break;
2435 case OpacityChannel: type="opacity"; break;
2436 case BlackChannel: type="black"; break;
2437 default: type="unknown"; break;
2438 }
2439 else
2440 switch (channel)
2441 {
2442 case RedChannel: type="red"; break;
2443 case GreenChannel: type="green"; break;
2444 case BlueChannel: type="blue"; break;
2445 case OpacityChannel: type="opacity"; break;
2446 default: type="unknown"; break;
2447 }
2448 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2449 if (strlen(subexpression) > 1)
2450 subexpression[strlen(subexpression)-1]='\0';
2451 if (fx_info->file != (FILE *) NULL)
cristye8c25f92010-06-03 00:53:06 +00002452 (void) fprintf(fx_info->file,"%s[%.20g,%.20g].%s: %s=%.*g\n",
2453 fx_info->images->filename,(double) x,(double) y,type,
2454 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002455 return(0.0);
2456 }
2457 break;
2458 }
2459 case 'E':
2460 case 'e':
2461 {
2462 if (LocaleCompare(expression,"epsilon") == 0)
2463 return((MagickRealType) MagickEpsilon);
2464 if (LocaleNCompare(expression,"exp",3) == 0)
2465 {
2466 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2467 exception);
2468 return((MagickRealType) exp((double) alpha));
2469 }
2470 if (LocaleCompare(expression,"e") == 0)
2471 return((MagickRealType) 2.7182818284590452354);
2472 break;
2473 }
2474 case 'F':
2475 case 'f':
2476 {
2477 if (LocaleNCompare(expression,"floor",5) == 0)
2478 {
2479 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2480 exception);
2481 return((MagickRealType) floor((double) alpha));
2482 }
2483 break;
2484 }
2485 case 'G':
2486 case 'g':
2487 {
2488 if (LocaleCompare(expression,"g") == 0)
2489 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2490 break;
2491 }
2492 case 'H':
2493 case 'h':
2494 {
2495 if (LocaleCompare(expression,"h") == 0)
2496 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2497 if (LocaleCompare(expression,"hue") == 0)
2498 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2499 if (LocaleNCompare(expression,"hypot",5) == 0)
2500 {
2501 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2502 exception);
2503 return((MagickRealType) hypot((double) alpha,(double) *beta));
2504 }
2505 break;
2506 }
2507 case 'K':
2508 case 'k':
2509 {
2510 if (LocaleCompare(expression,"k") == 0)
2511 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2512 break;
2513 }
2514 case 'I':
2515 case 'i':
2516 {
2517 if (LocaleCompare(expression,"intensity") == 0)
2518 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2519 if (LocaleNCompare(expression,"int",3) == 0)
2520 {
2521 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2522 exception);
cristy16788e42010-08-13 13:44:26 +00002523 return((MagickRealType) floor(alpha));
cristy3ed852e2009-09-05 21:47:34 +00002524 }
2525 if (LocaleCompare(expression,"i") == 0)
2526 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2527 break;
2528 }
2529 case 'J':
2530 case 'j':
2531 {
2532 if (LocaleCompare(expression,"j") == 0)
2533 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002534#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002535 if (LocaleNCompare(expression,"j0",2) == 0)
2536 {
2537 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2538 exception);
2539 return((MagickRealType) j0((double) alpha));
2540 }
cristy161b9262010-03-20 19:34:32 +00002541#endif
2542#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002543 if (LocaleNCompare(expression,"j1",2) == 0)
2544 {
2545 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2546 exception);
2547 return((MagickRealType) j1((double) alpha));
2548 }
cristy161b9262010-03-20 19:34:32 +00002549#endif
cristyaa018fa2010-04-08 23:03:54 +00002550#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002551 if (LocaleNCompare(expression,"jinc",4) == 0)
2552 {
2553 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2554 exception);
cristy0946a822010-03-12 17:14:58 +00002555 if (alpha == 0.0)
2556 return(1.0);
cristy69928f92010-03-12 13:27:09 +00002557 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/
cristyfce2f7b2010-03-12 00:29:49 +00002558 (MagickPI*alpha));
2559 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002560 }
cristyaa018fa2010-04-08 23:03:54 +00002561#endif
cristy3ed852e2009-09-05 21:47:34 +00002562 break;
2563 }
2564 case 'L':
2565 case 'l':
2566 {
2567 if (LocaleNCompare(expression,"ln",2) == 0)
2568 {
2569 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2570 exception);
2571 return((MagickRealType) log((double) alpha));
2572 }
cristyc8ed5322010-08-31 12:07:59 +00002573 if (LocaleNCompare(expression,"logtwo",6) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002574 {
cristyc8ed5322010-08-31 12:07:59 +00002575 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
cristy3ed852e2009-09-05 21:47:34 +00002576 exception);
2577 return((MagickRealType) log10((double) alpha))/log10(2.0);
2578 }
2579 if (LocaleNCompare(expression,"log",3) == 0)
2580 {
2581 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2582 exception);
2583 return((MagickRealType) log10((double) alpha));
2584 }
2585 if (LocaleCompare(expression,"lightness") == 0)
2586 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2587 break;
2588 }
2589 case 'M':
2590 case 'm':
2591 {
2592 if (LocaleCompare(expression,"MaxRGB") == 0)
2593 return((MagickRealType) QuantumRange);
2594 if (LocaleNCompare(expression,"maxima",6) == 0)
2595 break;
2596 if (LocaleNCompare(expression,"max",3) == 0)
2597 return(FxMax(fx_info,channel,x,y,expression+3,exception));
2598 if (LocaleNCompare(expression,"minima",6) == 0)
2599 break;
2600 if (LocaleNCompare(expression,"min",3) == 0)
2601 return(FxMin(fx_info,channel,x,y,expression+3,exception));
2602 if (LocaleNCompare(expression,"mod",3) == 0)
2603 {
2604 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2605 exception);
2606 return((MagickRealType) fmod((double) alpha,(double) *beta));
2607 }
2608 if (LocaleCompare(expression,"m") == 0)
2609 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2610 break;
2611 }
2612 case 'N':
2613 case 'n':
2614 {
2615 if (LocaleCompare(expression,"n") == 0)
2616 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2617 break;
2618 }
2619 case 'O':
2620 case 'o':
2621 {
2622 if (LocaleCompare(expression,"Opaque") == 0)
2623 return(1.0);
2624 if (LocaleCompare(expression,"o") == 0)
2625 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2626 break;
2627 }
2628 case 'P':
2629 case 'p':
2630 {
2631 if (LocaleCompare(expression,"pi") == 0)
2632 return((MagickRealType) MagickPI);
2633 if (LocaleNCompare(expression,"pow",3) == 0)
2634 {
2635 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2636 exception);
2637 return((MagickRealType) pow((double) alpha,(double) *beta));
2638 }
2639 if (LocaleCompare(expression,"p") == 0)
2640 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2641 break;
2642 }
2643 case 'Q':
2644 case 'q':
2645 {
2646 if (LocaleCompare(expression,"QuantumRange") == 0)
2647 return((MagickRealType) QuantumRange);
2648 if (LocaleCompare(expression,"QuantumScale") == 0)
2649 return((MagickRealType) QuantumScale);
2650 break;
2651 }
2652 case 'R':
2653 case 'r':
2654 {
2655 if (LocaleNCompare(expression,"rand",4) == 0)
2656 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2657 if (LocaleNCompare(expression,"round",5) == 0)
2658 {
2659 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2660 exception);
cristy16788e42010-08-13 13:44:26 +00002661 return((MagickRealType) floor((double) alpha+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002662 }
2663 if (LocaleCompare(expression,"r") == 0)
2664 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2665 break;
2666 }
2667 case 'S':
2668 case 's':
2669 {
2670 if (LocaleCompare(expression,"saturation") == 0)
2671 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2672 if (LocaleNCompare(expression,"sign",4) == 0)
2673 {
2674 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2675 exception);
2676 return(alpha < 0.0 ? -1.0 : 1.0);
2677 }
cristya6a09e72010-03-02 14:51:02 +00002678 if (LocaleNCompare(expression,"sinc",4) == 0)
2679 {
2680 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2681 exception);
2682 if (alpha == 0)
2683 return(1.0);
2684 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2685 (MagickPI*alpha));
2686 return(gamma);
2687 }
cristy3ed852e2009-09-05 21:47:34 +00002688 if (LocaleNCompare(expression,"sinh",4) == 0)
2689 {
2690 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2691 exception);
2692 return((MagickRealType) sinh((double) alpha));
2693 }
2694 if (LocaleNCompare(expression,"sin",3) == 0)
2695 {
2696 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2697 exception);
2698 return((MagickRealType) sin((double) alpha));
2699 }
2700 if (LocaleNCompare(expression,"sqrt",4) == 0)
2701 {
2702 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2703 exception);
2704 return((MagickRealType) sqrt((double) alpha));
2705 }
2706 if (LocaleCompare(expression,"s") == 0)
2707 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2708 break;
2709 }
2710 case 'T':
2711 case 't':
2712 {
2713 if (LocaleNCompare(expression,"tanh",4) == 0)
2714 {
2715 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2716 exception);
2717 return((MagickRealType) tanh((double) alpha));
2718 }
2719 if (LocaleNCompare(expression,"tan",3) == 0)
2720 {
2721 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2722 exception);
2723 return((MagickRealType) tan((double) alpha));
2724 }
2725 if (LocaleCompare(expression,"Transparent") == 0)
2726 return(0.0);
cristy16788e42010-08-13 13:44:26 +00002727 if (LocaleNCompare(expression,"trunc",5) == 0)
2728 {
2729 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2730 exception);
2731 if (alpha >= 0.0)
2732 return((MagickRealType) floor((double) alpha));
2733 return((MagickRealType) ceil((double) alpha));
2734 }
cristy3ed852e2009-09-05 21:47:34 +00002735 if (LocaleCompare(expression,"t") == 0)
2736 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2737 break;
2738 }
2739 case 'U':
2740 case 'u':
2741 {
2742 if (LocaleCompare(expression,"u") == 0)
2743 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2744 break;
2745 }
2746 case 'V':
2747 case 'v':
2748 {
2749 if (LocaleCompare(expression,"v") == 0)
2750 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2751 break;
2752 }
2753 case 'W':
2754 case 'w':
2755 {
2756 if (LocaleCompare(expression,"w") == 0)
2757 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2758 break;
2759 }
2760 case 'Y':
2761 case 'y':
2762 {
2763 if (LocaleCompare(expression,"y") == 0)
2764 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2765 break;
2766 }
2767 case 'Z':
2768 case 'z':
2769 {
2770 if (LocaleCompare(expression,"z") == 0)
2771 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2772 break;
2773 }
2774 default:
2775 break;
2776 }
2777 q=(char *) expression;
2778 alpha=strtod(expression,&q);
2779 if (q == expression)
2780 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2781 return(alpha);
2782}
2783
2784MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2785 MagickRealType *alpha,ExceptionInfo *exception)
2786{
2787 MagickBooleanType
2788 status;
2789
cristy30539862010-11-10 14:14:14 +00002790 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
cristy3ed852e2009-09-05 21:47:34 +00002791 return(status);
2792}
2793
2794MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2795 MagickRealType *alpha,ExceptionInfo *exception)
2796{
2797 FILE
2798 *file;
2799
2800 MagickBooleanType
2801 status;
2802
2803 file=fx_info->file;
2804 fx_info->file=(FILE *) NULL;
2805 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2806 fx_info->file=file;
2807 return(status);
2808}
2809
2810MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristye85007d2010-06-06 22:51:36 +00002811 const ChannelType channel,const ssize_t x,const ssize_t y,
2812 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002813{
2814 MagickRealType
2815 beta;
2816
2817 beta=0.0;
2818 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2819 exception);
2820 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2821}
2822
2823/*
2824%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2825% %
2826% %
2827% %
2828% F x I m a g e %
2829% %
2830% %
2831% %
2832%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2833%
2834% FxImage() applies a mathematical expression to the specified image.
2835%
2836% The format of the FxImage method is:
2837%
2838% Image *FxImage(const Image *image,const char *expression,
2839% ExceptionInfo *exception)
2840% Image *FxImageChannel(const Image *image,const ChannelType channel,
2841% const char *expression,ExceptionInfo *exception)
2842%
2843% A description of each parameter follows:
2844%
2845% o image: the image.
2846%
2847% o channel: the channel.
2848%
2849% o expression: A mathematical expression.
2850%
2851% o exception: return any errors or warnings in this structure.
2852%
2853*/
2854
2855static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2856{
cristybb503372010-05-27 20:51:26 +00002857 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002858 i;
2859
2860 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002861 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002862 if (fx_info[i] != (FxInfo *) NULL)
2863 fx_info[i]=DestroyFxInfo(fx_info[i]);
cristyb41ee102010-10-04 16:46:15 +00002864 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
cristy3ed852e2009-09-05 21:47:34 +00002865 return(fx_info);
2866}
2867
2868static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2869 ExceptionInfo *exception)
2870{
2871 char
2872 *fx_expression;
2873
2874 FxInfo
2875 **fx_info;
2876
2877 MagickRealType
2878 alpha;
2879
cristybb503372010-05-27 20:51:26 +00002880 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002881 i;
2882
cristybb503372010-05-27 20:51:26 +00002883 size_t
cristy3ed852e2009-09-05 21:47:34 +00002884 number_threads;
2885
2886 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002887 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +00002888 if (fx_info == (FxInfo **) NULL)
2889 return((FxInfo **) NULL);
2890 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2891 if (*expression != '@')
2892 fx_expression=ConstantString(expression);
2893 else
2894 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00002895 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002896 {
2897 fx_info[i]=AcquireFxInfo(image,fx_expression);
2898 if (fx_info[i] == (FxInfo *) NULL)
2899 return(DestroyFxThreadSet(fx_info));
2900 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2901 }
2902 fx_expression=DestroyString(fx_expression);
2903 return(fx_info);
2904}
2905
2906MagickExport Image *FxImage(const Image *image,const char *expression,
2907 ExceptionInfo *exception)
2908{
2909 Image
2910 *fx_image;
2911
2912 fx_image=FxImageChannel(image,GrayChannel,expression,exception);
2913 return(fx_image);
2914}
2915
2916MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
2917 const char *expression,ExceptionInfo *exception)
2918{
2919#define FxImageTag "Fx/Image"
2920
cristyfa112112010-01-04 17:48:07 +00002921 CacheView
2922 *fx_view;
2923
cristy3ed852e2009-09-05 21:47:34 +00002924 FxInfo
cristyfa112112010-01-04 17:48:07 +00002925 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00002926
2927 Image
2928 *fx_image;
2929
cristy3ed852e2009-09-05 21:47:34 +00002930 MagickBooleanType
2931 status;
2932
cristybb503372010-05-27 20:51:26 +00002933 MagickOffsetType
2934 progress;
2935
cristy3ed852e2009-09-05 21:47:34 +00002936 MagickRealType
2937 alpha;
2938
cristybb503372010-05-27 20:51:26 +00002939 ssize_t
2940 y;
2941
cristy3ed852e2009-09-05 21:47:34 +00002942 assert(image != (Image *) NULL);
2943 assert(image->signature == MagickSignature);
2944 if (image->debug != MagickFalse)
2945 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2946 fx_image=CloneImage(image,0,0,MagickTrue,exception);
2947 if (fx_image == (Image *) NULL)
2948 return((Image *) NULL);
2949 if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
2950 {
2951 InheritException(exception,&fx_image->exception);
2952 fx_image=DestroyImage(fx_image);
2953 return((Image *) NULL);
2954 }
2955 fx_info=AcquireFxThreadSet(image,expression,exception);
2956 if (fx_info == (FxInfo **) NULL)
2957 {
2958 fx_image=DestroyImage(fx_image);
2959 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2960 }
2961 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
2962 if (status == MagickFalse)
2963 {
2964 fx_image=DestroyImage(fx_image);
2965 fx_info=DestroyFxThreadSet(fx_info);
2966 return((Image *) NULL);
2967 }
2968 /*
2969 Fx image.
2970 */
2971 status=MagickTrue;
2972 progress=0;
2973 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00002974#if defined(MAGICKCORE_OPENMP_SUPPORT)
2975 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002976#endif
cristybb503372010-05-27 20:51:26 +00002977 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002978 {
cristy5c9e6f22010-09-17 17:31:01 +00002979 const int
2980 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00002981
cristy3ed852e2009-09-05 21:47:34 +00002982 MagickRealType
2983 alpha;
2984
2985 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002986 *restrict fx_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002987
cristybb503372010-05-27 20:51:26 +00002988 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002989 x;
2990
2991 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002992 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002993
2994 if (status == MagickFalse)
2995 continue;
2996 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
2997 if (q == (PixelPacket *) NULL)
2998 {
2999 status=MagickFalse;
3000 continue;
3001 }
3002 fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
cristy3ed852e2009-09-05 21:47:34 +00003003 alpha=0.0;
cristybb503372010-05-27 20:51:26 +00003004 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003005 {
3006 if ((channel & RedChannel) != 0)
3007 {
3008 (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
3009 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00003010 q->red=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00003011 }
3012 if ((channel & GreenChannel) != 0)
3013 {
3014 (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
3015 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00003016 q->green=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00003017 }
3018 if ((channel & BlueChannel) != 0)
3019 {
3020 (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
3021 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00003022 q->blue=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00003023 }
3024 if ((channel & OpacityChannel) != 0)
3025 {
3026 (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
3027 &alpha,exception);
3028 if (image->matte == MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003029 q->opacity=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00003030 else
cristyce70c172010-01-07 17:15:30 +00003031 q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
cristy3ed852e2009-09-05 21:47:34 +00003032 QuantumRange*alpha));
3033 }
3034 if (((channel & IndexChannel) != 0) &&
3035 (fx_image->colorspace == CMYKColorspace))
3036 {
3037 (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
3038 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00003039 fx_indexes[x]=(IndexPacket) ClampToQuantum((MagickRealType)
cristy3ed852e2009-09-05 21:47:34 +00003040 QuantumRange*alpha);
3041 }
3042 q++;
3043 }
3044 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3045 status=MagickFalse;
3046 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3047 {
3048 MagickBooleanType
3049 proceed;
3050
cristyb5d5f722009-11-04 03:03:49 +00003051#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003052 #pragma omp critical (MagickCore_FxImageChannel)
3053#endif
3054 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3055 if (proceed == MagickFalse)
3056 status=MagickFalse;
3057 }
3058 }
cristy3ed852e2009-09-05 21:47:34 +00003059 fx_view=DestroyCacheView(fx_view);
3060 fx_info=DestroyFxThreadSet(fx_info);
3061 if (status == MagickFalse)
3062 fx_image=DestroyImage(fx_image);
3063 return(fx_image);
3064}
3065
3066/*
3067%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3068% %
3069% %
3070% %
3071% I m p l o d e I m a g e %
3072% %
3073% %
3074% %
3075%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3076%
3077% ImplodeImage() creates a new image that is a copy of an existing
3078% one with the image pixels "implode" by the specified percentage. It
3079% allocates the memory necessary for the new Image structure and returns a
3080% pointer to the new image.
3081%
3082% The format of the ImplodeImage method is:
3083%
3084% Image *ImplodeImage(const Image *image,const double amount,
3085% ExceptionInfo *exception)
3086%
3087% A description of each parameter follows:
3088%
3089% o implode_image: Method ImplodeImage returns a pointer to the image
3090% after it is implode. A null image is returned if there is a memory
3091% shortage.
3092%
3093% o image: the image.
3094%
3095% o amount: Define the extent of the implosion.
3096%
3097% o exception: return any errors or warnings in this structure.
3098%
3099*/
3100MagickExport Image *ImplodeImage(const Image *image,const double amount,
3101 ExceptionInfo *exception)
3102{
3103#define ImplodeImageTag "Implode/Image"
3104
cristyfa112112010-01-04 17:48:07 +00003105 CacheView
3106 *image_view,
3107 *implode_view;
3108
cristy3ed852e2009-09-05 21:47:34 +00003109 Image
3110 *implode_image;
3111
cristy3ed852e2009-09-05 21:47:34 +00003112 MagickBooleanType
3113 status;
3114
cristybb503372010-05-27 20:51:26 +00003115 MagickOffsetType
3116 progress;
3117
cristy3ed852e2009-09-05 21:47:34 +00003118 MagickPixelPacket
3119 zero;
3120
3121 MagickRealType
3122 radius;
3123
3124 PointInfo
3125 center,
3126 scale;
3127
3128 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00003129 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00003130
cristybb503372010-05-27 20:51:26 +00003131 ssize_t
3132 y;
3133
cristy3ed852e2009-09-05 21:47:34 +00003134 /*
3135 Initialize implode image attributes.
3136 */
3137 assert(image != (Image *) NULL);
3138 assert(image->signature == MagickSignature);
3139 if (image->debug != MagickFalse)
3140 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3141 assert(exception != (ExceptionInfo *) NULL);
3142 assert(exception->signature == MagickSignature);
3143 implode_image=CloneImage(image,0,0,MagickTrue,exception);
3144 if (implode_image == (Image *) NULL)
3145 return((Image *) NULL);
3146 if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
3147 {
3148 InheritException(exception,&implode_image->exception);
3149 implode_image=DestroyImage(implode_image);
3150 return((Image *) NULL);
3151 }
3152 if (implode_image->background_color.opacity != OpaqueOpacity)
3153 implode_image->matte=MagickTrue;
3154 /*
3155 Compute scaling factor.
3156 */
3157 scale.x=1.0;
3158 scale.y=1.0;
3159 center.x=0.5*image->columns;
3160 center.y=0.5*image->rows;
3161 radius=center.x;
3162 if (image->columns > image->rows)
3163 scale.y=(double) image->columns/(double) image->rows;
3164 else
3165 if (image->columns < image->rows)
3166 {
3167 scale.x=(double) image->rows/(double) image->columns;
3168 radius=center.y;
3169 }
3170 /*
3171 Implode image.
3172 */
3173 status=MagickTrue;
3174 progress=0;
3175 GetMagickPixelPacket(implode_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00003176 resample_filter=AcquireResampleFilterThreadSet(image,
3177 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003178 image_view=AcquireCacheView(image);
3179 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003180#if defined(MAGICKCORE_OPENMP_SUPPORT)
3181 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003182#endif
cristybb503372010-05-27 20:51:26 +00003183 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003184 {
cristy5c9e6f22010-09-17 17:31:01 +00003185 const int
3186 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003187
cristy3ed852e2009-09-05 21:47:34 +00003188 MagickPixelPacket
3189 pixel;
3190
3191 MagickRealType
3192 distance;
3193
3194 PointInfo
3195 delta;
3196
3197 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003198 *restrict implode_indexes;
cristy3ed852e2009-09-05 21:47:34 +00003199
cristybb503372010-05-27 20:51:26 +00003200 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003201 x;
3202
3203 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003204 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003205
3206 if (status == MagickFalse)
3207 continue;
3208 q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3209 exception);
3210 if (q == (PixelPacket *) NULL)
3211 {
3212 status=MagickFalse;
3213 continue;
3214 }
3215 implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
3216 delta.y=scale.y*(double) (y-center.y);
3217 pixel=zero;
cristybb503372010-05-27 20:51:26 +00003218 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003219 {
3220 /*
3221 Determine if the pixel is within an ellipse.
3222 */
3223 delta.x=scale.x*(double) (x-center.x);
3224 distance=delta.x*delta.x+delta.y*delta.y;
3225 if (distance < (radius*radius))
3226 {
3227 double
3228 factor;
3229
3230 /*
3231 Implode the pixel.
3232 */
3233 factor=1.0;
3234 if (distance > 0.0)
3235 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3236 radius/2)),-amount);
3237 (void) ResamplePixelColor(resample_filter[id],(double)
3238 (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3239 scale.y+center.y),&pixel);
3240 SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
3241 }
3242 q++;
3243 }
3244 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3245 status=MagickFalse;
3246 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3247 {
3248 MagickBooleanType
3249 proceed;
3250
cristyb5d5f722009-11-04 03:03:49 +00003251#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003252 #pragma omp critical (MagickCore_ImplodeImage)
3253#endif
3254 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3255 if (proceed == MagickFalse)
3256 status=MagickFalse;
3257 }
3258 }
3259 implode_view=DestroyCacheView(implode_view);
3260 image_view=DestroyCacheView(image_view);
3261 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
3262 if (status == MagickFalse)
3263 implode_image=DestroyImage(implode_image);
3264 return(implode_image);
3265}
3266
3267/*
3268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3269% %
3270% %
3271% %
3272% M o r p h I m a g e s %
3273% %
3274% %
3275% %
3276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3277%
3278% The MorphImages() method requires a minimum of two images. The first
3279% image is transformed into the second by a number of intervening images
3280% as specified by frames.
3281%
3282% The format of the MorphImage method is:
3283%
cristybb503372010-05-27 20:51:26 +00003284% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003285% ExceptionInfo *exception)
3286%
3287% A description of each parameter follows:
3288%
3289% o image: the image.
3290%
3291% o number_frames: Define the number of in-between image to generate.
3292% The more in-between frames, the smoother the morph.
3293%
3294% o exception: return any errors or warnings in this structure.
3295%
3296*/
3297MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003298 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003299{
3300#define MorphImageTag "Morph/Image"
3301
3302 Image
3303 *morph_image,
3304 *morph_images;
3305
cristy9d314ff2011-03-09 01:30:28 +00003306 MagickBooleanType
3307 status;
cristy3ed852e2009-09-05 21:47:34 +00003308
3309 MagickOffsetType
3310 scene;
3311
3312 MagickRealType
3313 alpha,
3314 beta;
3315
3316 register const Image
3317 *next;
3318
cristybb503372010-05-27 20:51:26 +00003319 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003320 i;
3321
cristy9d314ff2011-03-09 01:30:28 +00003322 ssize_t
3323 y;
cristy3ed852e2009-09-05 21:47:34 +00003324
3325 /*
3326 Clone first frame in sequence.
3327 */
3328 assert(image != (Image *) NULL);
3329 assert(image->signature == MagickSignature);
3330 if (image->debug != MagickFalse)
3331 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3332 assert(exception != (ExceptionInfo *) NULL);
3333 assert(exception->signature == MagickSignature);
3334 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3335 if (morph_images == (Image *) NULL)
3336 return((Image *) NULL);
3337 if (GetNextImageInList(image) == (Image *) NULL)
3338 {
3339 /*
3340 Morph single image.
3341 */
cristybb503372010-05-27 20:51:26 +00003342 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003343 {
3344 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3345 if (morph_image == (Image *) NULL)
3346 {
3347 morph_images=DestroyImageList(morph_images);
3348 return((Image *) NULL);
3349 }
3350 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003351 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003352 {
cristy8b27a6d2010-02-14 03:31:15 +00003353 MagickBooleanType
3354 proceed;
3355
cristybb503372010-05-27 20:51:26 +00003356 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3357 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003358 if (proceed == MagickFalse)
3359 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003360 }
3361 }
3362 return(GetFirstImageInList(morph_images));
3363 }
3364 /*
3365 Morph image sequence.
3366 */
3367 status=MagickTrue;
3368 scene=0;
3369 next=image;
3370 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3371 {
cristybb503372010-05-27 20:51:26 +00003372 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003373 {
3374 CacheView
3375 *image_view,
3376 *morph_view;
3377
3378 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3379 alpha=1.0-beta;
cristy15b98cd2010-09-12 19:42:50 +00003380 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
cristybb503372010-05-27 20:51:26 +00003381 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
cristy15b98cd2010-09-12 19:42:50 +00003382 next->rows+beta*GetNextImageInList(next)->rows+0.5),
3383 next->filter,next->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003384 if (morph_image == (Image *) NULL)
3385 {
3386 morph_images=DestroyImageList(morph_images);
3387 return((Image *) NULL);
3388 }
3389 if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
3390 {
3391 InheritException(exception,&morph_image->exception);
3392 morph_image=DestroyImage(morph_image);
3393 return((Image *) NULL);
3394 }
3395 AppendImageToList(&morph_images,morph_image);
3396 morph_images=GetLastImageInList(morph_images);
cristy15b98cd2010-09-12 19:42:50 +00003397 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3398 morph_images->rows,GetNextImageInList(next)->filter,
3399 GetNextImageInList(next)->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003400 if (morph_image == (Image *) NULL)
3401 {
3402 morph_images=DestroyImageList(morph_images);
3403 return((Image *) NULL);
3404 }
3405 image_view=AcquireCacheView(morph_image);
3406 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003407#if defined(MAGICKCORE_OPENMP_SUPPORT)
3408 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003409#endif
cristybb503372010-05-27 20:51:26 +00003410 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003411 {
3412 MagickBooleanType
3413 sync;
3414
3415 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003416 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003417
cristybb503372010-05-27 20:51:26 +00003418 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003419 x;
3420
3421 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003422 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003423
3424 if (status == MagickFalse)
3425 continue;
3426 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3427 exception);
3428 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3429 exception);
3430 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3431 {
3432 status=MagickFalse;
3433 continue;
3434 }
cristybb503372010-05-27 20:51:26 +00003435 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003436 {
cristyce70c172010-01-07 17:15:30 +00003437 q->red=ClampToQuantum(alpha*q->red+beta*GetRedPixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00003438 q->green=ClampToQuantum(alpha*q->green+beta*
3439 GetGreenPixelComponent(p));
cristyce70c172010-01-07 17:15:30 +00003440 q->blue=ClampToQuantum(alpha*q->blue+beta*GetBluePixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00003441 q->opacity=ClampToQuantum(alpha*q->opacity+beta*
3442 GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00003443 p++;
3444 q++;
3445 }
3446 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3447 if (sync == MagickFalse)
3448 status=MagickFalse;
3449 }
3450 morph_view=DestroyCacheView(morph_view);
3451 image_view=DestroyCacheView(image_view);
3452 morph_image=DestroyImage(morph_image);
3453 }
cristybb503372010-05-27 20:51:26 +00003454 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003455 break;
3456 /*
3457 Clone last frame in sequence.
3458 */
3459 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3460 if (morph_image == (Image *) NULL)
3461 {
3462 morph_images=DestroyImageList(morph_images);
3463 return((Image *) NULL);
3464 }
3465 AppendImageToList(&morph_images,morph_image);
3466 morph_images=GetLastImageInList(morph_images);
3467 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3468 {
3469 MagickBooleanType
3470 proceed;
3471
cristyb5d5f722009-11-04 03:03:49 +00003472#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003473 #pragma omp critical (MagickCore_MorphImages)
3474#endif
3475 proceed=SetImageProgress(image,MorphImageTag,scene,
3476 GetImageListLength(image));
3477 if (proceed == MagickFalse)
3478 status=MagickFalse;
3479 }
3480 scene++;
3481 }
3482 if (GetNextImageInList(next) != (Image *) NULL)
3483 {
3484 morph_images=DestroyImageList(morph_images);
3485 return((Image *) NULL);
3486 }
3487 return(GetFirstImageInList(morph_images));
3488}
3489
3490/*
3491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3492% %
3493% %
3494% %
3495% P l a s m a I m a g e %
3496% %
3497% %
3498% %
3499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3500%
3501% PlasmaImage() initializes an image with plasma fractal values. The image
3502% must be initialized with a base color and the random number generator
3503% seeded before this method is called.
3504%
3505% The format of the PlasmaImage method is:
3506%
3507% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristybb503372010-05-27 20:51:26 +00003508% size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003509%
3510% A description of each parameter follows:
3511%
3512% o image: the image.
3513%
3514% o segment: Define the region to apply plasma fractals values.
3515%
glennrp7dae1ca2010-09-16 12:17:35 +00003516% o attenuate: Define the plasma attenuation factor.
cristy3ed852e2009-09-05 21:47:34 +00003517%
3518% o depth: Limit the plasma recursion depth.
3519%
3520*/
3521
3522static inline Quantum PlasmaPixel(RandomInfo *random_info,
3523 const MagickRealType pixel,const MagickRealType noise)
3524{
3525 Quantum
3526 plasma;
3527
cristyce70c172010-01-07 17:15:30 +00003528 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003529 noise/2.0);
3530 return(plasma);
3531}
3532
3533MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
cristyc5c6f662010-09-22 14:23:02 +00003534 CacheView *image_view,RandomInfo *random_info,const SegmentInfo *segment,
3535 size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003536{
3537 ExceptionInfo
3538 *exception;
3539
cristy3ed852e2009-09-05 21:47:34 +00003540 MagickRealType
3541 plasma;
3542
3543 PixelPacket
3544 u,
3545 v;
3546
cristy9d314ff2011-03-09 01:30:28 +00003547 ssize_t
3548 x,
3549 x_mid,
3550 y,
3551 y_mid;
3552
cristy3ed852e2009-09-05 21:47:34 +00003553 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3554 return(MagickTrue);
3555 if (depth != 0)
3556 {
3557 SegmentInfo
3558 local_info;
3559
3560 /*
3561 Divide the area into quadrants and recurse.
3562 */
3563 depth--;
3564 attenuate++;
cristybb503372010-05-27 20:51:26 +00003565 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3566 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003567 local_info=(*segment);
3568 local_info.x2=(double) x_mid;
3569 local_info.y2=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003570 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3571 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003572 local_info=(*segment);
3573 local_info.y1=(double) y_mid;
3574 local_info.x2=(double) x_mid;
cristyc5c6f662010-09-22 14:23:02 +00003575 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3576 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003577 local_info=(*segment);
3578 local_info.x1=(double) x_mid;
3579 local_info.y2=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003580 (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3581 attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003582 local_info=(*segment);
3583 local_info.x1=(double) x_mid;
3584 local_info.y1=(double) y_mid;
cristyc5c6f662010-09-22 14:23:02 +00003585 return(PlasmaImageProxy(image,image_view,random_info,&local_info,
3586 attenuate,depth));
cristy3ed852e2009-09-05 21:47:34 +00003587 }
cristybb503372010-05-27 20:51:26 +00003588 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3589 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003590 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3591 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3592 return(MagickFalse);
3593 /*
3594 Average pixels and apply plasma.
3595 */
3596 exception=(&image->exception);
3597 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3598 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3599 {
3600 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003601 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003602
3603 /*
3604 Left pixel.
3605 */
cristybb503372010-05-27 20:51:26 +00003606 x=(ssize_t) ceil(segment->x1-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003607 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3608 ceil(segment->y1-0.5),&u,exception);
3609 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3610 ceil(segment->y2-0.5),&v,exception);
3611 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00003612 if (q == (PixelPacket *) NULL)
3613 return(MagickTrue);
3614 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3615 plasma);
3616 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3617 plasma);
3618 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3619 plasma);
cristyc5c6f662010-09-22 14:23:02 +00003620 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003621 if (segment->x1 != segment->x2)
3622 {
3623 /*
3624 Right pixel.
3625 */
cristybb503372010-05-27 20:51:26 +00003626 x=(ssize_t) ceil(segment->x2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003627 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3628 ceil(segment->y1-0.5),&u,exception);
3629 (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3630 ceil(segment->y2-0.5),&v,exception);
3631 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00003632 if (q == (PixelPacket *) NULL)
3633 return(MagickTrue);
3634 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3635 plasma);
3636 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3637 2.0,plasma);
3638 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
cristyc5c6f662010-09-22 14:23:02 +00003639 plasma);
3640 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003641 }
3642 }
3643 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3644 {
3645 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3646 {
3647 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003648 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003649
3650 /*
3651 Bottom pixel.
3652 */
cristybb503372010-05-27 20:51:26 +00003653 y=(ssize_t) ceil(segment->y2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003654 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3655 ceil(segment->x1-0.5),y,&u,exception);
3656 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3657 ceil(segment->x2-0.5),y,&v,exception);
3658 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00003659 if (q == (PixelPacket *) NULL)
3660 return(MagickTrue);
3661 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3662 plasma);
3663 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3664 2.0,plasma);
3665 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3666 plasma);
cristyc5c6f662010-09-22 14:23:02 +00003667 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003668 }
3669 if (segment->y1 != segment->y2)
3670 {
3671 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003672 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003673
3674 /*
3675 Top pixel.
3676 */
cristybb503372010-05-27 20:51:26 +00003677 y=(ssize_t) ceil(segment->y1-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003678 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3679 ceil(segment->x1-0.5),y,&u,exception);
3680 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3681 ceil(segment->x2-0.5),y,&v,exception);
3682 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00003683 if (q == (PixelPacket *) NULL)
3684 return(MagickTrue);
3685 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3686 plasma);
3687 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3688 2.0,plasma);
3689 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3690 plasma);
cristyc5c6f662010-09-22 14:23:02 +00003691 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003692 }
3693 }
3694 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3695 {
3696 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003697 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003698
3699 /*
3700 Middle pixel.
3701 */
cristybb503372010-05-27 20:51:26 +00003702 x=(ssize_t) ceil(segment->x1-0.5);
3703 y=(ssize_t) ceil(segment->y1-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003704 (void) GetOneVirtualPixel(image,x,y,&u,exception);
cristybb503372010-05-27 20:51:26 +00003705 x=(ssize_t) ceil(segment->x2-0.5);
3706 y=(ssize_t) ceil(segment->y2-0.5);
cristyc5c6f662010-09-22 14:23:02 +00003707 (void) GetOneCacheViewVirtualPixel(image_view,x,y,&v,exception);
3708 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00003709 if (q == (PixelPacket *) NULL)
3710 return(MagickTrue);
3711 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3712 plasma);
3713 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3714 plasma);
3715 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3716 plasma);
cristyc5c6f662010-09-22 14:23:02 +00003717 (void) SyncCacheViewAuthenticPixels(image_view,exception);
cristy3ed852e2009-09-05 21:47:34 +00003718 }
3719 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3720 return(MagickTrue);
3721 return(MagickFalse);
3722}
3723
3724MagickExport MagickBooleanType PlasmaImage(Image *image,
cristybb503372010-05-27 20:51:26 +00003725 const SegmentInfo *segment,size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003726{
cristyc5c6f662010-09-22 14:23:02 +00003727 CacheView
3728 *image_view;
3729
cristy3ed852e2009-09-05 21:47:34 +00003730 MagickBooleanType
3731 status;
3732
3733 RandomInfo
3734 *random_info;
3735
3736 if (image->debug != MagickFalse)
3737 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3738 assert(image != (Image *) NULL);
3739 assert(image->signature == MagickSignature);
3740 if (image->debug != MagickFalse)
3741 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristyc5c6f662010-09-22 14:23:02 +00003742 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
3743 return(MagickFalse);
3744 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00003745 random_info=AcquireRandomInfo();
cristyc5c6f662010-09-22 14:23:02 +00003746 status=PlasmaImageProxy(image,image_view,random_info,segment,attenuate,depth);
cristy3ed852e2009-09-05 21:47:34 +00003747 random_info=DestroyRandomInfo(random_info);
cristyc5c6f662010-09-22 14:23:02 +00003748 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003749 return(status);
3750}
3751
3752/*
3753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3754% %
3755% %
3756% %
3757% P o l a r o i d I m a g e %
3758% %
3759% %
3760% %
3761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3762%
3763% PolaroidImage() simulates a Polaroid picture.
3764%
3765% The format of the AnnotateImage method is:
3766%
3767% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3768% const double angle,ExceptionInfo exception)
3769%
3770% A description of each parameter follows:
3771%
3772% o image: the image.
3773%
3774% o draw_info: the draw info.
3775%
cristycee97112010-05-28 00:44:52 +00003776% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003777%
3778% o exception: return any errors or warnings in this structure.
3779%
3780*/
3781MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3782 const double angle,ExceptionInfo *exception)
3783{
3784 const char
3785 *value;
3786
cristy3ed852e2009-09-05 21:47:34 +00003787 Image
3788 *bend_image,
3789 *caption_image,
3790 *flop_image,
3791 *picture_image,
3792 *polaroid_image,
3793 *rotate_image,
3794 *trim_image;
3795
cristybb503372010-05-27 20:51:26 +00003796 size_t
cristy3ed852e2009-09-05 21:47:34 +00003797 height;
3798
cristy9d314ff2011-03-09 01:30:28 +00003799 ssize_t
3800 quantum;
3801
cristy3ed852e2009-09-05 21:47:34 +00003802 /*
3803 Simulate a Polaroid picture.
3804 */
3805 assert(image != (Image *) NULL);
3806 assert(image->signature == MagickSignature);
3807 if (image->debug != MagickFalse)
3808 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3809 assert(exception != (ExceptionInfo *) NULL);
3810 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003811 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003812 image->rows)/25.0,10.0);
3813 height=image->rows+2*quantum;
3814 caption_image=(Image *) NULL;
3815 value=GetImageProperty(image,"Caption");
3816 if (value != (const char *) NULL)
3817 {
3818 char
3819 *caption,
3820 geometry[MaxTextExtent];
3821
3822 DrawInfo
3823 *annotate_info;
3824
cristy3ed852e2009-09-05 21:47:34 +00003825 MagickBooleanType
3826 status;
3827
cristy9d314ff2011-03-09 01:30:28 +00003828 ssize_t
3829 count;
3830
cristy3ed852e2009-09-05 21:47:34 +00003831 TypeMetric
3832 metrics;
3833
3834 /*
3835 Generate caption image.
3836 */
3837 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3838 if (caption_image == (Image *) NULL)
3839 return((Image *) NULL);
3840 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
3841 caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
3842 value);
3843 (void) CloneString(&annotate_info->text,caption);
cristy6b1d05e2010-09-22 19:17:27 +00003844 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
3845 &caption);
cristybb503372010-05-27 20:51:26 +00003846 status=SetImageExtent(caption_image,image->columns,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00003847 ((count+1)*(metrics.ascent-metrics.descent)+0.5));
3848 if (status == MagickFalse)
3849 caption_image=DestroyImage(caption_image);
3850 else
3851 {
3852 caption_image->background_color=image->border_color;
3853 (void) SetImageBackgroundColor(caption_image);
3854 (void) CloneString(&annotate_info->text,caption);
cristye7f51092010-01-17 00:39:37 +00003855 (void) FormatMagickString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00003856 metrics.ascent);
3857 if (annotate_info->gravity == UndefinedGravity)
3858 (void) CloneString(&annotate_info->geometry,AcquireString(
3859 geometry));
3860 (void) AnnotateImage(caption_image,annotate_info);
3861 height+=caption_image->rows;
3862 }
3863 annotate_info=DestroyDrawInfo(annotate_info);
3864 caption=DestroyString(caption);
3865 }
3866 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3867 exception);
3868 if (picture_image == (Image *) NULL)
3869 {
3870 if (caption_image != (Image *) NULL)
3871 caption_image=DestroyImage(caption_image);
3872 return((Image *) NULL);
3873 }
3874 picture_image->background_color=image->border_color;
3875 (void) SetImageBackgroundColor(picture_image);
3876 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
3877 if (caption_image != (Image *) NULL)
3878 {
3879 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
cristybb503372010-05-27 20:51:26 +00003880 quantum,(ssize_t) (image->rows+3*quantum/2));
cristy3ed852e2009-09-05 21:47:34 +00003881 caption_image=DestroyImage(caption_image);
3882 }
3883 (void) QueryColorDatabase("none",&picture_image->background_color,exception);
3884 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
3885 rotate_image=RotateImage(picture_image,90.0,exception);
3886 picture_image=DestroyImage(picture_image);
3887 if (rotate_image == (Image *) NULL)
3888 return((Image *) NULL);
3889 picture_image=rotate_image;
3890 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
3891 picture_image->columns,exception);
3892 picture_image=DestroyImage(picture_image);
3893 if (bend_image == (Image *) NULL)
3894 return((Image *) NULL);
3895 InheritException(&bend_image->exception,exception);
3896 picture_image=bend_image;
3897 rotate_image=RotateImage(picture_image,-90.0,exception);
3898 picture_image=DestroyImage(picture_image);
3899 if (rotate_image == (Image *) NULL)
3900 return((Image *) NULL);
3901 picture_image=rotate_image;
3902 picture_image->background_color=image->background_color;
3903 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
3904 exception);
3905 if (polaroid_image == (Image *) NULL)
3906 {
3907 picture_image=DestroyImage(picture_image);
3908 return(picture_image);
3909 }
3910 flop_image=FlopImage(polaroid_image,exception);
3911 polaroid_image=DestroyImage(polaroid_image);
3912 if (flop_image == (Image *) NULL)
3913 {
3914 picture_image=DestroyImage(picture_image);
3915 return(picture_image);
3916 }
3917 polaroid_image=flop_image;
3918 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
cristybb503372010-05-27 20:51:26 +00003919 (ssize_t) (-0.01*picture_image->columns/2.0),0L);
cristy3ed852e2009-09-05 21:47:34 +00003920 picture_image=DestroyImage(picture_image);
3921 (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
3922 rotate_image=RotateImage(polaroid_image,angle,exception);
3923 polaroid_image=DestroyImage(polaroid_image);
3924 if (rotate_image == (Image *) NULL)
3925 return((Image *) NULL);
3926 polaroid_image=rotate_image;
3927 trim_image=TrimImage(polaroid_image,exception);
3928 polaroid_image=DestroyImage(polaroid_image);
3929 if (trim_image == (Image *) NULL)
3930 return((Image *) NULL);
3931 polaroid_image=trim_image;
3932 return(polaroid_image);
3933}
3934
3935/*
3936%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3937% %
3938% %
3939% %
cristy3ed852e2009-09-05 21:47:34 +00003940% S e p i a T o n e I m a g e %
3941% %
3942% %
3943% %
3944%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3945%
3946% MagickSepiaToneImage() applies a special effect to the image, similar to the
3947% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
3948% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
3949% threshold of 80% is a good starting point for a reasonable tone.
3950%
3951% The format of the SepiaToneImage method is:
3952%
3953% Image *SepiaToneImage(const Image *image,const double threshold,
3954% ExceptionInfo *exception)
3955%
3956% A description of each parameter follows:
3957%
3958% o image: the image.
3959%
3960% o threshold: the tone threshold.
3961%
3962% o exception: return any errors or warnings in this structure.
3963%
3964*/
3965MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
3966 ExceptionInfo *exception)
3967{
3968#define SepiaToneImageTag "SepiaTone/Image"
3969
cristyc4c8d132010-01-07 01:58:38 +00003970 CacheView
3971 *image_view,
3972 *sepia_view;
3973
cristy3ed852e2009-09-05 21:47:34 +00003974 Image
3975 *sepia_image;
3976
cristy3ed852e2009-09-05 21:47:34 +00003977 MagickBooleanType
3978 status;
3979
cristybb503372010-05-27 20:51:26 +00003980 MagickOffsetType
3981 progress;
3982
3983 ssize_t
3984 y;
3985
cristy3ed852e2009-09-05 21:47:34 +00003986 /*
3987 Initialize sepia-toned image attributes.
3988 */
3989 assert(image != (const Image *) NULL);
3990 assert(image->signature == MagickSignature);
3991 if (image->debug != MagickFalse)
3992 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3993 assert(exception != (ExceptionInfo *) NULL);
3994 assert(exception->signature == MagickSignature);
3995 sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3996 if (sepia_image == (Image *) NULL)
3997 return((Image *) NULL);
3998 if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
3999 {
4000 InheritException(exception,&sepia_image->exception);
4001 sepia_image=DestroyImage(sepia_image);
4002 return((Image *) NULL);
4003 }
4004 /*
4005 Tone each row of the image.
4006 */
4007 status=MagickTrue;
4008 progress=0;
4009 image_view=AcquireCacheView(image);
4010 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004011#if defined(MAGICKCORE_OPENMP_SUPPORT)
4012 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004013#endif
cristybb503372010-05-27 20:51:26 +00004014 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004015 {
4016 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004017 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004018
cristybb503372010-05-27 20:51:26 +00004019 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004020 x;
4021
4022 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004023 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004024
4025 if (status == MagickFalse)
4026 continue;
4027 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4028 q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4029 exception);
4030 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4031 {
4032 status=MagickFalse;
4033 continue;
4034 }
cristybb503372010-05-27 20:51:26 +00004035 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004036 {
4037 MagickRealType
4038 intensity,
4039 tone;
4040
4041 intensity=(MagickRealType) PixelIntensityToQuantum(p);
4042 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4043 (MagickRealType) QuantumRange-threshold;
cristyce70c172010-01-07 17:15:30 +00004044 q->red=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004045 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4046 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristyce70c172010-01-07 17:15:30 +00004047 q->green=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004048 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristyce70c172010-01-07 17:15:30 +00004049 q->blue=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004050 tone=threshold/7.0;
4051 if ((MagickRealType) q->green < tone)
cristyce70c172010-01-07 17:15:30 +00004052 q->green=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004053 if ((MagickRealType) q->blue < tone)
cristyce70c172010-01-07 17:15:30 +00004054 q->blue=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004055 p++;
4056 q++;
4057 }
4058 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4059 status=MagickFalse;
4060 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4061 {
4062 MagickBooleanType
4063 proceed;
4064
cristyb5d5f722009-11-04 03:03:49 +00004065#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004066 #pragma omp critical (MagickCore_SepiaToneImage)
4067#endif
4068 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4069 image->rows);
4070 if (proceed == MagickFalse)
4071 status=MagickFalse;
4072 }
4073 }
4074 sepia_view=DestroyCacheView(sepia_view);
4075 image_view=DestroyCacheView(image_view);
4076 (void) NormalizeImage(sepia_image);
4077 (void) ContrastImage(sepia_image,MagickTrue);
4078 if (status == MagickFalse)
4079 sepia_image=DestroyImage(sepia_image);
4080 return(sepia_image);
4081}
4082
4083/*
4084%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4085% %
4086% %
4087% %
4088% S h a d o w I m a g e %
4089% %
4090% %
4091% %
4092%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4093%
4094% ShadowImage() simulates a shadow from the specified image and returns it.
4095%
4096% The format of the ShadowImage method is:
4097%
4098% Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004099% const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004100% ExceptionInfo *exception)
4101%
4102% A description of each parameter follows:
4103%
4104% o image: the image.
4105%
4106% o opacity: percentage transparency.
4107%
4108% o sigma: the standard deviation of the Gaussian, in pixels.
4109%
4110% o x_offset: the shadow x-offset.
4111%
4112% o y_offset: the shadow y-offset.
4113%
4114% o exception: return any errors or warnings in this structure.
4115%
4116*/
4117MagickExport Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004118 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004119 ExceptionInfo *exception)
4120{
4121#define ShadowImageTag "Shadow/Image"
4122
cristyc4c8d132010-01-07 01:58:38 +00004123 CacheView
4124 *image_view;
4125
cristy3ed852e2009-09-05 21:47:34 +00004126 Image
4127 *border_image,
4128 *clone_image,
4129 *shadow_image;
4130
cristy3ed852e2009-09-05 21:47:34 +00004131 MagickBooleanType
4132 status;
4133
cristybb503372010-05-27 20:51:26 +00004134 MagickOffsetType
4135 progress;
4136
cristy3ed852e2009-09-05 21:47:34 +00004137 RectangleInfo
4138 border_info;
4139
cristybb503372010-05-27 20:51:26 +00004140 ssize_t
4141 y;
4142
cristy3ed852e2009-09-05 21:47:34 +00004143 assert(image != (Image *) NULL);
4144 assert(image->signature == MagickSignature);
4145 if (image->debug != MagickFalse)
4146 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4147 assert(exception != (ExceptionInfo *) NULL);
4148 assert(exception->signature == MagickSignature);
4149 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4150 if (clone_image == (Image *) NULL)
4151 return((Image *) NULL);
4152 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4153 clone_image->compose=OverCompositeOp;
cristybb503372010-05-27 20:51:26 +00004154 border_info.width=(size_t) floor(2.0*sigma+0.5);
4155 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004156 border_info.x=0;
4157 border_info.y=0;
4158 (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4159 border_image=BorderImage(clone_image,&border_info,exception);
4160 clone_image=DestroyImage(clone_image);
4161 if (border_image == (Image *) NULL)
4162 return((Image *) NULL);
4163 if (border_image->matte == MagickFalse)
4164 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4165 /*
4166 Shadow image.
4167 */
4168 status=MagickTrue;
4169 progress=0;
4170 image_view=AcquireCacheView(border_image);
cristyb5d5f722009-11-04 03:03:49 +00004171#if defined(MAGICKCORE_OPENMP_SUPPORT)
4172 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004173#endif
cristybb503372010-05-27 20:51:26 +00004174 for (y=0; y < (ssize_t) border_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004175 {
cristybb503372010-05-27 20:51:26 +00004176 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004177 x;
4178
4179 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004180 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004181
4182 if (status == MagickFalse)
4183 continue;
4184 q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4185 exception);
4186 if (q == (PixelPacket *) NULL)
4187 {
4188 status=MagickFalse;
4189 continue;
4190 }
cristybb503372010-05-27 20:51:26 +00004191 for (x=0; x < (ssize_t) border_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004192 {
4193 q->red=border_image->background_color.red;
4194 q->green=border_image->background_color.green;
4195 q->blue=border_image->background_color.blue;
4196 if (border_image->matte == MagickFalse)
4197 q->opacity=border_image->background_color.opacity;
4198 else
cristy46f08202010-01-10 04:04:21 +00004199 q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
4200 GetAlphaPixelComponent(q)*opacity/100.0));
cristy3ed852e2009-09-05 21:47:34 +00004201 q++;
4202 }
4203 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4204 status=MagickFalse;
4205 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4206 {
4207 MagickBooleanType
4208 proceed;
4209
cristyb5d5f722009-11-04 03:03:49 +00004210#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004211 #pragma omp critical (MagickCore_ShadowImage)
4212#endif
4213 proceed=SetImageProgress(image,ShadowImageTag,progress++,
4214 border_image->rows);
4215 if (proceed == MagickFalse)
4216 status=MagickFalse;
4217 }
4218 }
4219 image_view=DestroyCacheView(image_view);
4220 shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
4221 border_image=DestroyImage(border_image);
4222 if (shadow_image == (Image *) NULL)
4223 return((Image *) NULL);
4224 if (shadow_image->page.width == 0)
4225 shadow_image->page.width=shadow_image->columns;
4226 if (shadow_image->page.height == 0)
4227 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004228 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4229 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4230 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4231 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004232 return(shadow_image);
4233}
4234
4235/*
4236%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4237% %
4238% %
4239% %
4240% S k e t c h I m a g e %
4241% %
4242% %
4243% %
4244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4245%
4246% SketchImage() simulates a pencil sketch. We convolve the image with a
4247% Gaussian operator of the given radius and standard deviation (sigma). For
4248% reasonable results, radius should be larger than sigma. Use a radius of 0
4249% and SketchImage() selects a suitable radius for you. Angle gives the angle
4250% of the sketch.
4251%
4252% The format of the SketchImage method is:
4253%
4254% Image *SketchImage(const Image *image,const double radius,
4255% const double sigma,const double angle,ExceptionInfo *exception)
4256%
4257% A description of each parameter follows:
4258%
4259% o image: the image.
4260%
4261% o radius: the radius of the Gaussian, in pixels, not counting
4262% the center pixel.
4263%
4264% o sigma: the standard deviation of the Gaussian, in pixels.
4265%
cristycee97112010-05-28 00:44:52 +00004266% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00004267%
4268% o exception: return any errors or warnings in this structure.
4269%
4270*/
4271MagickExport Image *SketchImage(const Image *image,const double radius,
4272 const double sigma,const double angle,ExceptionInfo *exception)
4273{
cristyfa112112010-01-04 17:48:07 +00004274 CacheView
4275 *random_view;
4276
cristy3ed852e2009-09-05 21:47:34 +00004277 Image
4278 *blend_image,
4279 *blur_image,
4280 *dodge_image,
4281 *random_image,
4282 *sketch_image;
4283
cristy3ed852e2009-09-05 21:47:34 +00004284 MagickBooleanType
4285 status;
4286
4287 MagickPixelPacket
4288 zero;
4289
4290 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004291 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004292
cristy9d314ff2011-03-09 01:30:28 +00004293 ssize_t
4294 y;
4295
cristy3ed852e2009-09-05 21:47:34 +00004296 /*
4297 Sketch image.
4298 */
4299 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4300 MagickTrue,exception);
4301 if (random_image == (Image *) NULL)
4302 return((Image *) NULL);
4303 status=MagickTrue;
4304 GetMagickPixelPacket(random_image,&zero);
cristy1b784432009-12-19 02:20:40 +00004305 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004306 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004307#if defined(MAGICKCORE_OPENMP_SUPPORT)
4308 #pragma omp parallel for schedule(dynamic,4) shared(status)
4309#endif
cristybb503372010-05-27 20:51:26 +00004310 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004311 {
cristy5c9e6f22010-09-17 17:31:01 +00004312 const int
4313 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004314
cristy3ed852e2009-09-05 21:47:34 +00004315 MagickPixelPacket
4316 pixel;
4317
4318 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004319 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00004320
cristybb503372010-05-27 20:51:26 +00004321 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004322 x;
4323
4324 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004325 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004326
cristy1b784432009-12-19 02:20:40 +00004327 if (status == MagickFalse)
4328 continue;
cristy3ed852e2009-09-05 21:47:34 +00004329 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4330 exception);
4331 if (q == (PixelPacket *) NULL)
4332 {
4333 status=MagickFalse;
4334 continue;
4335 }
4336 indexes=GetCacheViewAuthenticIndexQueue(random_view);
4337 pixel=zero;
cristybb503372010-05-27 20:51:26 +00004338 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004339 {
4340 pixel.red=(MagickRealType) (QuantumRange*
cristy1b784432009-12-19 02:20:40 +00004341 GetPseudoRandomValue(random_info[id]));
cristy3ed852e2009-09-05 21:47:34 +00004342 pixel.green=pixel.red;
4343 pixel.blue=pixel.red;
4344 if (image->colorspace == CMYKColorspace)
4345 pixel.index=pixel.red;
4346 SetPixelPacket(random_image,&pixel,q,indexes+x);
4347 q++;
4348 }
4349 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4350 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004351 }
4352 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004353 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004354 if (status == MagickFalse)
4355 {
4356 random_image=DestroyImage(random_image);
4357 return(random_image);
4358 }
4359 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4360 random_image=DestroyImage(random_image);
4361 if (blur_image == (Image *) NULL)
4362 return((Image *) NULL);
4363 dodge_image=EdgeImage(blur_image,radius,exception);
4364 blur_image=DestroyImage(blur_image);
4365 if (dodge_image == (Image *) NULL)
4366 return((Image *) NULL);
4367 (void) NormalizeImage(dodge_image);
4368 (void) NegateImage(dodge_image,MagickFalse);
4369 (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4370 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4371 if (sketch_image == (Image *) NULL)
4372 {
4373 dodge_image=DestroyImage(dodge_image);
4374 return((Image *) NULL);
4375 }
4376 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4377 dodge_image=DestroyImage(dodge_image);
4378 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4379 if (blend_image == (Image *) NULL)
4380 {
4381 sketch_image=DestroyImage(sketch_image);
4382 return((Image *) NULL);
4383 }
4384 (void) SetImageArtifact(blend_image,"compose:args","20x80");
4385 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4386 blend_image=DestroyImage(blend_image);
4387 return(sketch_image);
4388}
4389
4390/*
4391%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4392% %
4393% %
4394% %
4395% S o l a r i z e I m a g e %
4396% %
4397% %
4398% %
4399%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4400%
4401% SolarizeImage() applies a special effect to the image, similar to the effect
4402% achieved in a photo darkroom by selectively exposing areas of photo
4403% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4404% measure of the extent of the solarization.
4405%
4406% The format of the SolarizeImage method is:
4407%
4408% MagickBooleanType SolarizeImage(Image *image,const double threshold)
4409%
4410% A description of each parameter follows:
4411%
4412% o image: the image.
4413%
4414% o threshold: Define the extent of the solarization.
4415%
4416*/
4417MagickExport MagickBooleanType SolarizeImage(Image *image,
4418 const double threshold)
4419{
4420#define SolarizeImageTag "Solarize/Image"
4421
cristyc4c8d132010-01-07 01:58:38 +00004422 CacheView
4423 *image_view;
4424
cristy3ed852e2009-09-05 21:47:34 +00004425 ExceptionInfo
4426 *exception;
4427
cristy3ed852e2009-09-05 21:47:34 +00004428 MagickBooleanType
4429 status;
4430
cristybb503372010-05-27 20:51:26 +00004431 MagickOffsetType
4432 progress;
4433
4434 ssize_t
4435 y;
4436
cristy3ed852e2009-09-05 21:47:34 +00004437 assert(image != (Image *) NULL);
4438 assert(image->signature == MagickSignature);
4439 if (image->debug != MagickFalse)
4440 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4441 if (image->storage_class == PseudoClass)
4442 {
cristybb503372010-05-27 20:51:26 +00004443 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004444 i;
4445
4446 /*
4447 Solarize colormap.
4448 */
cristybb503372010-05-27 20:51:26 +00004449 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004450 {
4451 if ((MagickRealType) image->colormap[i].red > threshold)
4452 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4453 if ((MagickRealType) image->colormap[i].green > threshold)
4454 image->colormap[i].green=(Quantum) QuantumRange-
4455 image->colormap[i].green;
4456 if ((MagickRealType) image->colormap[i].blue > threshold)
4457 image->colormap[i].blue=(Quantum) QuantumRange-
4458 image->colormap[i].blue;
4459 }
4460 }
4461 /*
4462 Solarize image.
4463 */
4464 status=MagickTrue;
4465 progress=0;
4466 exception=(&image->exception);
4467 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004468#if defined(MAGICKCORE_OPENMP_SUPPORT)
4469 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004470#endif
cristybb503372010-05-27 20:51:26 +00004471 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004472 {
cristybb503372010-05-27 20:51:26 +00004473 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004474 x;
4475
4476 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004477 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004478
4479 if (status == MagickFalse)
4480 continue;
4481 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4482 exception);
4483 if (q == (PixelPacket *) NULL)
4484 {
4485 status=MagickFalse;
4486 continue;
4487 }
cristybb503372010-05-27 20:51:26 +00004488 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004489 {
4490 if ((MagickRealType) q->red > threshold)
4491 q->red=(Quantum) QuantumRange-q->red;
4492 if ((MagickRealType) q->green > threshold)
4493 q->green=(Quantum) QuantumRange-q->green;
4494 if ((MagickRealType) q->blue > threshold)
4495 q->blue=(Quantum) QuantumRange-q->blue;
4496 q++;
4497 }
4498 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4499 status=MagickFalse;
4500 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4501 {
4502 MagickBooleanType
4503 proceed;
4504
cristyb5d5f722009-11-04 03:03:49 +00004505#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004506 #pragma omp critical (MagickCore_SolarizeImage)
4507#endif
4508 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4509 if (proceed == MagickFalse)
4510 status=MagickFalse;
4511 }
4512 }
4513 image_view=DestroyCacheView(image_view);
4514 return(status);
4515}
4516
4517/*
4518%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4519% %
4520% %
4521% %
4522% S t e g a n o I m a g e %
4523% %
4524% %
4525% %
4526%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4527%
4528% SteganoImage() hides a digital watermark within the image. Recover
4529% the hidden watermark later to prove that the authenticity of an image.
4530% Offset defines the start position within the image to hide the watermark.
4531%
4532% The format of the SteganoImage method is:
4533%
4534% Image *SteganoImage(const Image *image,Image *watermark,
4535% ExceptionInfo *exception)
4536%
4537% A description of each parameter follows:
4538%
4539% o image: the image.
4540%
4541% o watermark: the watermark image.
4542%
4543% o exception: return any errors or warnings in this structure.
4544%
4545*/
4546MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4547 ExceptionInfo *exception)
4548{
cristye1bf8ad2010-09-19 17:07:03 +00004549#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
cristyeaedf062010-05-29 22:36:02 +00004550#define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) != 0 ? (size_t) (alpha) \
4551 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004552#define SteganoImageTag "Stegano/Image"
4553
cristyb0d3bb92010-09-22 14:37:58 +00004554 CacheView
4555 *stegano_view,
4556 *watermark_view;
4557
cristy3ed852e2009-09-05 21:47:34 +00004558 Image
4559 *stegano_image;
4560
4561 int
4562 c;
4563
cristy3ed852e2009-09-05 21:47:34 +00004564 MagickBooleanType
4565 status;
4566
4567 PixelPacket
4568 pixel;
4569
cristy3ed852e2009-09-05 21:47:34 +00004570 register PixelPacket
4571 *q;
4572
cristye1bf8ad2010-09-19 17:07:03 +00004573 register ssize_t
4574 x;
4575
cristybb503372010-05-27 20:51:26 +00004576 size_t
cristyeaedf062010-05-29 22:36:02 +00004577 depth,
4578 one;
cristy3ed852e2009-09-05 21:47:34 +00004579
cristye1bf8ad2010-09-19 17:07:03 +00004580 ssize_t
4581 i,
4582 j,
4583 k,
4584 y;
4585
cristy3ed852e2009-09-05 21:47:34 +00004586 /*
4587 Initialize steganographic image attributes.
4588 */
4589 assert(image != (const Image *) NULL);
4590 assert(image->signature == MagickSignature);
4591 if (image->debug != MagickFalse)
4592 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4593 assert(watermark != (const Image *) NULL);
4594 assert(watermark->signature == MagickSignature);
4595 assert(exception != (ExceptionInfo *) NULL);
4596 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004597 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004598 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4599 if (stegano_image == (Image *) NULL)
4600 return((Image *) NULL);
4601 if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
4602 {
4603 InheritException(exception,&stegano_image->exception);
4604 stegano_image=DestroyImage(stegano_image);
4605 return((Image *) NULL);
4606 }
4607 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4608 /*
4609 Hide watermark in low-order bits of image.
4610 */
4611 c=0;
4612 i=0;
4613 j=0;
4614 depth=stegano_image->depth;
4615 k=image->offset;
cristyda16f162011-02-19 23:52:17 +00004616 status=MagickTrue;
cristyb0d3bb92010-09-22 14:37:58 +00004617 watermark_view=AcquireCacheView(watermark);
4618 stegano_view=AcquireCacheView(stegano_image);
cristybb503372010-05-27 20:51:26 +00004619 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004620 {
cristybb503372010-05-27 20:51:26 +00004621 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004622 {
cristybb503372010-05-27 20:51:26 +00004623 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004624 {
cristyb0d3bb92010-09-22 14:37:58 +00004625 (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,&pixel,exception);
cristybb503372010-05-27 20:51:26 +00004626 if ((k/(ssize_t) stegano_image->columns) >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004627 break;
cristyb0d3bb92010-09-22 14:37:58 +00004628 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4629 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4630 exception);
cristy3ed852e2009-09-05 21:47:34 +00004631 if (q == (PixelPacket *) NULL)
4632 break;
4633 switch (c)
4634 {
4635 case 0:
4636 {
4637 SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4638 break;
4639 }
4640 case 1:
4641 {
4642 SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4643 break;
4644 }
4645 case 2:
4646 {
4647 SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4648 break;
4649 }
4650 }
cristyb0d3bb92010-09-22 14:37:58 +00004651 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004652 break;
4653 c++;
4654 if (c == 3)
4655 c=0;
4656 k++;
cristybb503372010-05-27 20:51:26 +00004657 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004658 k=0;
4659 if (k == image->offset)
4660 j++;
4661 }
4662 }
cristy8b27a6d2010-02-14 03:31:15 +00004663 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004664 {
cristy8b27a6d2010-02-14 03:31:15 +00004665 MagickBooleanType
4666 proceed;
4667
4668 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4669 (depth-i),depth);
4670 if (proceed == MagickFalse)
4671 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004672 }
4673 }
cristyb0d3bb92010-09-22 14:37:58 +00004674 stegano_view=DestroyCacheView(stegano_view);
4675 watermark_view=DestroyCacheView(watermark_view);
cristy3ed852e2009-09-05 21:47:34 +00004676 if (stegano_image->storage_class == PseudoClass)
4677 (void) SyncImage(stegano_image);
cristyda16f162011-02-19 23:52:17 +00004678 if (status == MagickFalse)
4679 {
4680 stegano_image=DestroyImage(stegano_image);
4681 return((Image *) NULL);
4682 }
cristy3ed852e2009-09-05 21:47:34 +00004683 return(stegano_image);
4684}
4685
4686/*
4687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4688% %
4689% %
4690% %
4691% S t e r e o A n a g l y p h I m a g e %
4692% %
4693% %
4694% %
4695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4696%
4697% StereoAnaglyphImage() combines two images and produces a single image that
4698% is the composite of a left and right image of a stereo pair. Special
4699% red-green stereo glasses are required to view this effect.
4700%
4701% The format of the StereoAnaglyphImage method is:
4702%
4703% Image *StereoImage(const Image *left_image,const Image *right_image,
4704% ExceptionInfo *exception)
4705% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004706% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004707% ExceptionInfo *exception)
4708%
4709% A description of each parameter follows:
4710%
4711% o left_image: the left image.
4712%
4713% o right_image: the right image.
4714%
4715% o exception: return any errors or warnings in this structure.
4716%
4717% o x_offset: amount, in pixels, by which the left image is offset to the
4718% right of the right image.
4719%
4720% o y_offset: amount, in pixels, by which the left image is offset to the
4721% bottom of the right image.
4722%
4723%
4724*/
4725MagickExport Image *StereoImage(const Image *left_image,
4726 const Image *right_image,ExceptionInfo *exception)
4727{
4728 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4729}
4730
4731MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004732 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004733 ExceptionInfo *exception)
4734{
4735#define StereoImageTag "Stereo/Image"
4736
4737 const Image
4738 *image;
4739
4740 Image
4741 *stereo_image;
4742
cristy3ed852e2009-09-05 21:47:34 +00004743 MagickBooleanType
4744 status;
4745
cristy9d314ff2011-03-09 01:30:28 +00004746 ssize_t
4747 y;
4748
cristy3ed852e2009-09-05 21:47:34 +00004749 assert(left_image != (const Image *) NULL);
4750 assert(left_image->signature == MagickSignature);
4751 if (left_image->debug != MagickFalse)
4752 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4753 left_image->filename);
4754 assert(right_image != (const Image *) NULL);
4755 assert(right_image->signature == MagickSignature);
4756 assert(exception != (ExceptionInfo *) NULL);
4757 assert(exception->signature == MagickSignature);
4758 assert(right_image != (const Image *) NULL);
4759 image=left_image;
4760 if ((left_image->columns != right_image->columns) ||
4761 (left_image->rows != right_image->rows))
4762 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4763 /*
4764 Initialize stereo image attributes.
4765 */
4766 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4767 MagickTrue,exception);
4768 if (stereo_image == (Image *) NULL)
4769 return((Image *) NULL);
4770 if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
4771 {
4772 InheritException(exception,&stereo_image->exception);
4773 stereo_image=DestroyImage(stereo_image);
4774 return((Image *) NULL);
4775 }
4776 /*
4777 Copy left image to red channel and right image to blue channel.
4778 */
cristyda16f162011-02-19 23:52:17 +00004779 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00004780 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004781 {
4782 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004783 *restrict p,
4784 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004785
cristybb503372010-05-27 20:51:26 +00004786 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004787 x;
4788
4789 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004790 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004791
4792 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4793 exception);
4794 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4795 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
4796 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
4797 (r == (PixelPacket *) NULL))
4798 break;
cristybb503372010-05-27 20:51:26 +00004799 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004800 {
cristyce70c172010-01-07 17:15:30 +00004801 r->red=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00004802 r->green=q->green;
4803 r->blue=q->blue;
4804 r->opacity=(Quantum) ((p->opacity+q->opacity)/2);
4805 p++;
4806 q++;
4807 r++;
4808 }
4809 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4810 break;
cristy8b27a6d2010-02-14 03:31:15 +00004811 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004812 {
cristy8b27a6d2010-02-14 03:31:15 +00004813 MagickBooleanType
4814 proceed;
4815
cristybb503372010-05-27 20:51:26 +00004816 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4817 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004818 if (proceed == MagickFalse)
4819 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004820 }
4821 }
cristyda16f162011-02-19 23:52:17 +00004822 if (status == MagickFalse)
4823 {
4824 stereo_image=DestroyImage(stereo_image);
4825 return((Image *) NULL);
4826 }
cristy3ed852e2009-09-05 21:47:34 +00004827 return(stereo_image);
4828}
4829
4830/*
4831%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4832% %
4833% %
4834% %
4835% S w i r l I m a g e %
4836% %
4837% %
4838% %
4839%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4840%
4841% SwirlImage() swirls the pixels about the center of the image, where
4842% degrees indicates the sweep of the arc through which each pixel is moved.
4843% You get a more dramatic effect as the degrees move from 1 to 360.
4844%
4845% The format of the SwirlImage method is:
4846%
4847% Image *SwirlImage(const Image *image,double degrees,
4848% ExceptionInfo *exception)
4849%
4850% A description of each parameter follows:
4851%
4852% o image: the image.
4853%
4854% o degrees: Define the tightness of the swirling effect.
4855%
4856% o exception: return any errors or warnings in this structure.
4857%
4858*/
4859MagickExport Image *SwirlImage(const Image *image,double degrees,
4860 ExceptionInfo *exception)
4861{
4862#define SwirlImageTag "Swirl/Image"
4863
cristyfa112112010-01-04 17:48:07 +00004864 CacheView
4865 *image_view,
4866 *swirl_view;
4867
cristy3ed852e2009-09-05 21:47:34 +00004868 Image
4869 *swirl_image;
4870
cristy3ed852e2009-09-05 21:47:34 +00004871 MagickBooleanType
4872 status;
4873
cristybb503372010-05-27 20:51:26 +00004874 MagickOffsetType
4875 progress;
4876
cristy3ed852e2009-09-05 21:47:34 +00004877 MagickPixelPacket
4878 zero;
4879
4880 MagickRealType
4881 radius;
4882
4883 PointInfo
4884 center,
4885 scale;
4886
4887 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00004888 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00004889
cristybb503372010-05-27 20:51:26 +00004890 ssize_t
4891 y;
4892
cristy3ed852e2009-09-05 21:47:34 +00004893 /*
4894 Initialize swirl image attributes.
4895 */
4896 assert(image != (const Image *) NULL);
4897 assert(image->signature == MagickSignature);
4898 if (image->debug != MagickFalse)
4899 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4900 assert(exception != (ExceptionInfo *) NULL);
4901 assert(exception->signature == MagickSignature);
4902 swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4903 if (swirl_image == (Image *) NULL)
4904 return((Image *) NULL);
4905 if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
4906 {
4907 InheritException(exception,&swirl_image->exception);
4908 swirl_image=DestroyImage(swirl_image);
4909 return((Image *) NULL);
4910 }
4911 if (swirl_image->background_color.opacity != OpaqueOpacity)
4912 swirl_image->matte=MagickTrue;
4913 /*
4914 Compute scaling factor.
4915 */
4916 center.x=(double) image->columns/2.0;
4917 center.y=(double) image->rows/2.0;
4918 radius=MagickMax(center.x,center.y);
4919 scale.x=1.0;
4920 scale.y=1.0;
4921 if (image->columns > image->rows)
4922 scale.y=(double) image->columns/(double) image->rows;
4923 else
4924 if (image->columns < image->rows)
4925 scale.x=(double) image->rows/(double) image->columns;
4926 degrees=(double) DegreesToRadians(degrees);
4927 /*
4928 Swirl image.
4929 */
4930 status=MagickTrue;
4931 progress=0;
4932 GetMagickPixelPacket(swirl_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00004933 resample_filter=AcquireResampleFilterThreadSet(image,
4934 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004935 image_view=AcquireCacheView(image);
4936 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00004937#if defined(MAGICKCORE_OPENMP_SUPPORT)
4938 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004939#endif
cristybb503372010-05-27 20:51:26 +00004940 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004941 {
cristy5c9e6f22010-09-17 17:31:01 +00004942 const int
4943 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00004944
cristy3ed852e2009-09-05 21:47:34 +00004945 MagickPixelPacket
4946 pixel;
4947
4948 MagickRealType
4949 distance;
4950
4951 PointInfo
4952 delta;
4953
4954 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004955 *restrict swirl_indexes;
cristy3ed852e2009-09-05 21:47:34 +00004956
cristybb503372010-05-27 20:51:26 +00004957 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004958 x;
4959
4960 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004961 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004962
4963 if (status == MagickFalse)
4964 continue;
4965 q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
4966 exception);
4967 if (q == (PixelPacket *) NULL)
4968 {
4969 status=MagickFalse;
4970 continue;
4971 }
4972 swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
4973 delta.y=scale.y*(double) (y-center.y);
4974 pixel=zero;
cristybb503372010-05-27 20:51:26 +00004975 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004976 {
4977 /*
4978 Determine if the pixel is within an ellipse.
4979 */
4980 delta.x=scale.x*(double) (x-center.x);
4981 distance=delta.x*delta.x+delta.y*delta.y;
4982 if (distance < (radius*radius))
4983 {
4984 MagickRealType
4985 cosine,
4986 factor,
4987 sine;
4988
4989 /*
4990 Swirl the pixel.
4991 */
4992 factor=1.0-sqrt((double) distance)/radius;
4993 sine=sin((double) (degrees*factor*factor));
4994 cosine=cos((double) (degrees*factor*factor));
4995 (void) ResamplePixelColor(resample_filter[id],(double) ((cosine*
4996 delta.x-sine*delta.y)/scale.x+center.x),(double) ((sine*delta.x+
4997 cosine*delta.y)/scale.y+center.y),&pixel);
4998 SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
4999 }
5000 q++;
5001 }
5002 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5003 status=MagickFalse;
5004 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5005 {
5006 MagickBooleanType
5007 proceed;
5008
cristyb5d5f722009-11-04 03:03:49 +00005009#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005010 #pragma omp critical (MagickCore_SwirlImage)
5011#endif
5012 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5013 if (proceed == MagickFalse)
5014 status=MagickFalse;
5015 }
5016 }
5017 swirl_view=DestroyCacheView(swirl_view);
5018 image_view=DestroyCacheView(image_view);
5019 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5020 if (status == MagickFalse)
5021 swirl_image=DestroyImage(swirl_image);
5022 return(swirl_image);
5023}
5024
5025/*
5026%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5027% %
5028% %
5029% %
5030% T i n t I m a g e %
5031% %
5032% %
5033% %
5034%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5035%
5036% TintImage() applies a color vector to each pixel in the image. The length
5037% of the vector is 0 for black and white and at its maximum for the midtones.
5038% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5039%
5040% The format of the TintImage method is:
5041%
5042% Image *TintImage(const Image *image,const char *opacity,
5043% const PixelPacket tint,ExceptionInfo *exception)
5044%
5045% A description of each parameter follows:
5046%
5047% o image: the image.
5048%
5049% o opacity: A color value used for tinting.
5050%
5051% o tint: A color value used for tinting.
5052%
5053% o exception: return any errors or warnings in this structure.
5054%
5055*/
5056MagickExport Image *TintImage(const Image *image,const char *opacity,
5057 const PixelPacket tint,ExceptionInfo *exception)
5058{
5059#define TintImageTag "Tint/Image"
5060
cristyc4c8d132010-01-07 01:58:38 +00005061 CacheView
5062 *image_view,
5063 *tint_view;
5064
cristy3ed852e2009-09-05 21:47:34 +00005065 GeometryInfo
5066 geometry_info;
5067
5068 Image
5069 *tint_image;
5070
cristy3ed852e2009-09-05 21:47:34 +00005071 MagickBooleanType
5072 status;
5073
cristybb503372010-05-27 20:51:26 +00005074 MagickOffsetType
5075 progress;
cristy3ed852e2009-09-05 21:47:34 +00005076
5077 MagickPixelPacket
5078 color_vector,
5079 pixel;
5080
cristybb503372010-05-27 20:51:26 +00005081 MagickStatusType
5082 flags;
5083
5084 ssize_t
5085 y;
5086
cristy3ed852e2009-09-05 21:47:34 +00005087 /*
5088 Allocate tint image.
5089 */
5090 assert(image != (const Image *) NULL);
5091 assert(image->signature == MagickSignature);
5092 if (image->debug != MagickFalse)
5093 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5094 assert(exception != (ExceptionInfo *) NULL);
5095 assert(exception->signature == MagickSignature);
5096 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5097 if (tint_image == (Image *) NULL)
5098 return((Image *) NULL);
5099 if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5100 {
5101 InheritException(exception,&tint_image->exception);
5102 tint_image=DestroyImage(tint_image);
5103 return((Image *) NULL);
5104 }
5105 if (opacity == (const char *) NULL)
5106 return(tint_image);
5107 /*
5108 Determine RGB values of the color.
5109 */
5110 flags=ParseGeometry(opacity,&geometry_info);
5111 pixel.red=geometry_info.rho;
5112 if ((flags & SigmaValue) != 0)
5113 pixel.green=geometry_info.sigma;
5114 else
5115 pixel.green=pixel.red;
5116 if ((flags & XiValue) != 0)
5117 pixel.blue=geometry_info.xi;
5118 else
5119 pixel.blue=pixel.red;
5120 if ((flags & PsiValue) != 0)
5121 pixel.opacity=geometry_info.psi;
5122 else
5123 pixel.opacity=(MagickRealType) OpaqueOpacity;
5124 color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5125 PixelIntensity(&tint));
5126 color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5127 PixelIntensity(&tint));
5128 color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5129 PixelIntensity(&tint));
5130 /*
5131 Tint image.
5132 */
5133 status=MagickTrue;
5134 progress=0;
5135 image_view=AcquireCacheView(image);
5136 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005137#if defined(MAGICKCORE_OPENMP_SUPPORT)
5138 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005139#endif
cristybb503372010-05-27 20:51:26 +00005140 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005141 {
5142 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005143 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005144
cristybb503372010-05-27 20:51:26 +00005145 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005146 x;
5147
5148 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005149 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005150
5151 if (status == MagickFalse)
5152 continue;
5153 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5154 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5155 exception);
5156 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5157 {
5158 status=MagickFalse;
5159 continue;
5160 }
cristybb503372010-05-27 20:51:26 +00005161 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005162 {
5163 MagickPixelPacket
5164 pixel;
5165
5166 MagickRealType
5167 weight;
5168
5169 weight=QuantumScale*p->red-0.5;
5170 pixel.red=(MagickRealType) p->red+color_vector.red*(1.0-(4.0*
5171 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005172 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00005173 weight=QuantumScale*p->green-0.5;
5174 pixel.green=(MagickRealType) p->green+color_vector.green*(1.0-(4.0*
5175 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005176 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00005177 weight=QuantumScale*p->blue-0.5;
5178 pixel.blue=(MagickRealType) p->blue+color_vector.blue*(1.0-(4.0*
5179 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005180 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5181 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00005182 p++;
5183 q++;
5184 }
5185 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5186 status=MagickFalse;
5187 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5188 {
5189 MagickBooleanType
5190 proceed;
5191
cristyb5d5f722009-11-04 03:03:49 +00005192#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005193 #pragma omp critical (MagickCore_TintImage)
5194#endif
5195 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5196 if (proceed == MagickFalse)
5197 status=MagickFalse;
5198 }
5199 }
5200 tint_view=DestroyCacheView(tint_view);
5201 image_view=DestroyCacheView(image_view);
5202 if (status == MagickFalse)
5203 tint_image=DestroyImage(tint_image);
5204 return(tint_image);
5205}
5206
5207/*
5208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5209% %
5210% %
5211% %
5212% V i g n e t t e I m a g e %
5213% %
5214% %
5215% %
5216%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5217%
5218% VignetteImage() softens the edges of the image in vignette style.
5219%
5220% The format of the VignetteImage method is:
5221%
5222% Image *VignetteImage(const Image *image,const double radius,
cristybb503372010-05-27 20:51:26 +00005223% const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005224%
5225% A description of each parameter follows:
5226%
5227% o image: the image.
5228%
5229% o radius: the radius of the pixel neighborhood.
5230%
5231% o sigma: the standard deviation of the Gaussian, in pixels.
5232%
5233% o x, y: Define the x and y ellipse offset.
5234%
5235% o exception: return any errors or warnings in this structure.
5236%
5237*/
5238MagickExport Image *VignetteImage(const Image *image,const double radius,
cristybb503372010-05-27 20:51:26 +00005239 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005240{
5241 char
5242 ellipse[MaxTextExtent];
5243
5244 DrawInfo
5245 *draw_info;
5246
5247 Image
5248 *canvas_image,
5249 *blur_image,
5250 *oval_image,
5251 *vignette_image;
5252
5253 assert(image != (Image *) NULL);
5254 assert(image->signature == MagickSignature);
5255 if (image->debug != MagickFalse)
5256 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5257 assert(exception != (ExceptionInfo *) NULL);
5258 assert(exception->signature == MagickSignature);
5259 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5260 if (canvas_image == (Image *) NULL)
5261 return((Image *) NULL);
5262 if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
5263 {
5264 InheritException(exception,&canvas_image->exception);
5265 canvas_image=DestroyImage(canvas_image);
5266 return((Image *) NULL);
5267 }
5268 canvas_image->matte=MagickTrue;
5269 oval_image=CloneImage(canvas_image,canvas_image->columns,
5270 canvas_image->rows,MagickTrue,exception);
5271 if (oval_image == (Image *) NULL)
5272 {
5273 canvas_image=DestroyImage(canvas_image);
5274 return((Image *) NULL);
5275 }
5276 (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5277 (void) SetImageBackgroundColor(oval_image);
5278 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5279 (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5280 (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
5281 (void) FormatMagickString(ellipse,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00005282 "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
cristy8cd5b312010-01-07 01:10:24 +00005283 image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005284 draw_info->primitive=AcquireString(ellipse);
5285 (void) DrawImage(oval_image,draw_info);
5286 draw_info=DestroyDrawInfo(draw_info);
5287 blur_image=BlurImage(oval_image,radius,sigma,exception);
5288 oval_image=DestroyImage(oval_image);
5289 if (blur_image == (Image *) NULL)
5290 {
5291 canvas_image=DestroyImage(canvas_image);
5292 return((Image *) NULL);
5293 }
5294 blur_image->matte=MagickFalse;
5295 (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5296 blur_image=DestroyImage(blur_image);
5297 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5298 canvas_image=DestroyImage(canvas_image);
5299 return(vignette_image);
5300}
5301
5302/*
5303%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5304% %
5305% %
5306% %
5307% W a v e I m a g e %
5308% %
5309% %
5310% %
5311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5312%
5313% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005314% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005315% by the given parameters.
5316%
5317% The format of the WaveImage method is:
5318%
5319% Image *WaveImage(const Image *image,const double amplitude,
5320% const double wave_length,ExceptionInfo *exception)
5321%
5322% A description of each parameter follows:
5323%
5324% o image: the image.
5325%
5326% o amplitude, wave_length: Define the amplitude and wave length of the
5327% sine wave.
5328%
5329% o exception: return any errors or warnings in this structure.
5330%
5331*/
5332MagickExport Image *WaveImage(const Image *image,const double amplitude,
5333 const double wave_length,ExceptionInfo *exception)
5334{
5335#define WaveImageTag "Wave/Image"
5336
cristyfa112112010-01-04 17:48:07 +00005337 CacheView
5338 *wave_view;
5339
cristy3ed852e2009-09-05 21:47:34 +00005340 Image
5341 *wave_image;
5342
cristy3ed852e2009-09-05 21:47:34 +00005343 MagickBooleanType
5344 status;
5345
cristybb503372010-05-27 20:51:26 +00005346 MagickOffsetType
5347 progress;
5348
cristy3ed852e2009-09-05 21:47:34 +00005349 MagickPixelPacket
5350 zero;
5351
5352 MagickRealType
5353 *sine_map;
5354
cristybb503372010-05-27 20:51:26 +00005355 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005356 i;
5357
5358 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00005359 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00005360
cristybb503372010-05-27 20:51:26 +00005361 ssize_t
5362 y;
5363
cristy3ed852e2009-09-05 21:47:34 +00005364 /*
5365 Initialize wave image attributes.
5366 */
5367 assert(image != (Image *) NULL);
5368 assert(image->signature == MagickSignature);
5369 if (image->debug != MagickFalse)
5370 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5371 assert(exception != (ExceptionInfo *) NULL);
5372 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005373 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005374 fabs(amplitude)),MagickTrue,exception);
5375 if (wave_image == (Image *) NULL)
5376 return((Image *) NULL);
5377 if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
5378 {
5379 InheritException(exception,&wave_image->exception);
5380 wave_image=DestroyImage(wave_image);
5381 return((Image *) NULL);
5382 }
5383 if (wave_image->background_color.opacity != OpaqueOpacity)
5384 wave_image->matte=MagickTrue;
5385 /*
5386 Allocate sine map.
5387 */
5388 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5389 sizeof(*sine_map));
5390 if (sine_map == (MagickRealType *) NULL)
5391 {
5392 wave_image=DestroyImage(wave_image);
5393 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5394 }
cristybb503372010-05-27 20:51:26 +00005395 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy4205a3c2010-09-12 20:19:59 +00005396 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5397 wave_length));
cristy3ed852e2009-09-05 21:47:34 +00005398 /*
5399 Wave image.
5400 */
5401 status=MagickTrue;
5402 progress=0;
5403 GetMagickPixelPacket(wave_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00005404 resample_filter=AcquireResampleFilterThreadSet(image,
5405 BackgroundVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005406 wave_view=AcquireCacheView(wave_image);
cristyb5d5f722009-11-04 03:03:49 +00005407#if defined(MAGICKCORE_OPENMP_SUPPORT)
5408 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005409#endif
cristybb503372010-05-27 20:51:26 +00005410 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005411 {
cristy5c9e6f22010-09-17 17:31:01 +00005412 const int
5413 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00005414
cristy3ed852e2009-09-05 21:47:34 +00005415 MagickPixelPacket
5416 pixel;
5417
5418 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00005419 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00005420
cristybb503372010-05-27 20:51:26 +00005421 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005422 x;
5423
5424 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005425 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005426
5427 if (status == MagickFalse)
5428 continue;
5429 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5430 exception);
5431 if (q == (PixelPacket *) NULL)
5432 {
5433 status=MagickFalse;
5434 continue;
5435 }
5436 indexes=GetCacheViewAuthenticIndexQueue(wave_view);
5437 pixel=zero;
cristybb503372010-05-27 20:51:26 +00005438 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005439 {
5440 (void) ResamplePixelColor(resample_filter[id],(double) x,(double) (y-
5441 sine_map[x]),&pixel);
5442 SetPixelPacket(wave_image,&pixel,q,indexes+x);
5443 q++;
5444 }
5445 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5446 status=MagickFalse;
5447 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5448 {
5449 MagickBooleanType
5450 proceed;
5451
cristyb5d5f722009-11-04 03:03:49 +00005452#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005453 #pragma omp critical (MagickCore_WaveImage)
5454#endif
5455 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5456 if (proceed == MagickFalse)
5457 status=MagickFalse;
5458 }
5459 }
5460 wave_view=DestroyCacheView(wave_view);
5461 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5462 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5463 if (status == MagickFalse)
5464 wave_image=DestroyImage(wave_image);
5465 return(wave_image);
5466}