blob: a3ad3a0847414f48c6179e03b7e5de4897f28777 [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% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 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
101
102struct _FxInfo
103{
104 const Image
105 *images;
106
107 MagickBooleanType
108 matte;
109
110 char
111 *expression;
112
113 FILE
114 *file;
115
116 SplayTreeInfo
117 *colors,
118 *symbols;
119
120 ResampleFilter
121 **resample_filter;
122
123 RandomInfo
124 *random_info;
125
126 ExceptionInfo
127 *exception;
128};
129
130/*
131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
132% %
133% %
134% %
135+ A c q u i r e F x I n f o %
136% %
137% %
138% %
139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140%
141% AcquireFxInfo() allocates the FxInfo structure.
142%
143% The format of the AcquireFxInfo method is:
144%
145% FxInfo *AcquireFxInfo(Image *image,const char *expression)
146% A description of each parameter follows:
147%
148% o image: the image.
149%
150% o expression: the expression.
151%
152*/
153MagickExport FxInfo *AcquireFxInfo(const Image *image,const char *expression)
154{
155 char
156 fx_op[2];
157
158 FxInfo
159 *fx_info;
160
cristybb503372010-05-27 20:51:26 +0000161 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000162 i;
163
cristy90823212009-12-12 20:48:33 +0000164 fx_info=(FxInfo *) AcquireAlignedMemory(1,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +0000165 if (fx_info == (FxInfo *) NULL)
166 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
167 (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
168 fx_info->exception=AcquireExceptionInfo();
169 fx_info->images=image;
170 fx_info->matte=image->matte;
171 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
172 RelinquishMagickMemory);
173 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
174 RelinquishMagickMemory);
175 fx_info->resample_filter=(ResampleFilter **) AcquireQuantumMemory(
176 GetImageListLength(fx_info->images),sizeof(*fx_info->resample_filter));
177 if (fx_info->resample_filter == (ResampleFilter **) NULL)
178 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +0000179 for (i=0; i < (ssize_t) GetImageListLength(fx_info->images); i++)
cristy3ed852e2009-09-05 21:47:34 +0000180 {
181 fx_info->resample_filter[i]=AcquireResampleFilter(GetImageFromList(
182 fx_info->images,i),fx_info->exception);
183 SetResampleFilter(fx_info->resample_filter[i],PointFilter,1.0);
184 }
185 fx_info->random_info=AcquireRandomInfo();
186 fx_info->expression=ConstantString(expression);
187 fx_info->file=stderr;
188 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
189 if ((strstr(fx_info->expression,"e+") != (char *) NULL) ||
190 (strstr(fx_info->expression,"e-") != (char *) NULL))
191 {
192 /*
193 Convert scientific notation.
194 */
195 (void) SubstituteString(&fx_info->expression,"0e+","0*10^");
196 (void) SubstituteString(&fx_info->expression,"1e+","1*10^");
197 (void) SubstituteString(&fx_info->expression,"2e+","2*10^");
198 (void) SubstituteString(&fx_info->expression,"3e+","3*10^");
199 (void) SubstituteString(&fx_info->expression,"4e+","4*10^");
200 (void) SubstituteString(&fx_info->expression,"5e+","5*10^");
201 (void) SubstituteString(&fx_info->expression,"6e+","6*10^");
202 (void) SubstituteString(&fx_info->expression,"7e+","7*10^");
203 (void) SubstituteString(&fx_info->expression,"8e+","8*10^");
204 (void) SubstituteString(&fx_info->expression,"9e+","9*10^");
205 (void) SubstituteString(&fx_info->expression,"0e-","0*10^-");
206 (void) SubstituteString(&fx_info->expression,"1e-","1*10^-");
207 (void) SubstituteString(&fx_info->expression,"2e-","2*10^-");
208 (void) SubstituteString(&fx_info->expression,"3e-","3*10^-");
209 (void) SubstituteString(&fx_info->expression,"4e-","4*10^-");
210 (void) SubstituteString(&fx_info->expression,"5e-","5*10^-");
211 (void) SubstituteString(&fx_info->expression,"6e-","6*10^-");
212 (void) SubstituteString(&fx_info->expression,"7e-","7*10^-");
213 (void) SubstituteString(&fx_info->expression,"8e-","8*10^-");
214 (void) SubstituteString(&fx_info->expression,"9e-","9*10^-");
215 }
216 /*
217 Convert complex to simple operators.
218 */
219 fx_op[1]='\0';
220 *fx_op=(char) LeftShiftOperator;
221 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
222 *fx_op=(char) RightShiftOperator;
223 (void) SubstituteString(&fx_info->expression,">>",fx_op);
224 *fx_op=(char) LessThanEqualOperator;
225 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
226 *fx_op=(char) GreaterThanEqualOperator;
227 (void) SubstituteString(&fx_info->expression,">=",fx_op);
228 *fx_op=(char) EqualOperator;
229 (void) SubstituteString(&fx_info->expression,"==",fx_op);
230 *fx_op=(char) NotEqualOperator;
231 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
232 *fx_op=(char) LogicalAndOperator;
233 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
234 *fx_op=(char) LogicalOrOperator;
235 (void) SubstituteString(&fx_info->expression,"||",fx_op);
236 return(fx_info);
237}
238
239/*
240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241% %
242% %
243% %
244% A d d N o i s e I m a g e %
245% %
246% %
247% %
248%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
249%
250% AddNoiseImage() adds random noise to the image.
251%
252% The format of the AddNoiseImage method is:
253%
254% Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
255% ExceptionInfo *exception)
256% Image *AddNoiseImageChannel(const Image *image,const ChannelType channel,
257% const NoiseType noise_type,ExceptionInfo *exception)
258%
259% A description of each parameter follows:
260%
261% o image: the image.
262%
263% o channel: the channel type.
264%
265% o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
266% Impulse, Laplacian, or Poisson.
267%
268% o exception: return any errors or warnings in this structure.
269%
270*/
271MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
272 ExceptionInfo *exception)
273{
274 Image
275 *noise_image;
276
277 noise_image=AddNoiseImageChannel(image,DefaultChannels,noise_type,exception);
278 return(noise_image);
279}
280
281MagickExport Image *AddNoiseImageChannel(const Image *image,
282 const ChannelType channel,const NoiseType noise_type,ExceptionInfo *exception)
283{
284#define AddNoiseImageTag "AddNoise/Image"
285
cristyfa112112010-01-04 17:48:07 +0000286 CacheView
287 *image_view,
288 *noise_view;
289
cristy3ed852e2009-09-05 21:47:34 +0000290 const char
291 *option;
292
293 Image
294 *noise_image;
295
cristy3ed852e2009-09-05 21:47:34 +0000296 MagickBooleanType
297 status;
298
cristybb503372010-05-27 20:51:26 +0000299 MagickOffsetType
300 progress;
301
cristy3ed852e2009-09-05 21:47:34 +0000302 MagickRealType
303 attenuate;
304
305 RandomInfo
cristyfa112112010-01-04 17:48:07 +0000306 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +0000307
cristybb503372010-05-27 20:51:26 +0000308 ssize_t
309 y;
310
cristy3ed852e2009-09-05 21:47:34 +0000311 /*
312 Initialize noise image attributes.
313 */
314 assert(image != (const Image *) NULL);
315 assert(image->signature == MagickSignature);
316 if (image->debug != MagickFalse)
317 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
318 assert(exception != (ExceptionInfo *) NULL);
319 assert(exception->signature == MagickSignature);
320 noise_image=CloneImage(image,0,0,MagickTrue,exception);
321 if (noise_image == (Image *) NULL)
322 return((Image *) NULL);
323 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
324 {
325 InheritException(exception,&noise_image->exception);
326 noise_image=DestroyImage(noise_image);
327 return((Image *) NULL);
328 }
329 /*
330 Add noise in each row.
331 */
332 attenuate=1.0;
333 option=GetImageArtifact(image,"attenuate");
334 if (option != (char *) NULL)
cristyf2f27272009-12-17 14:48:46 +0000335 attenuate=StringToDouble(option);
cristy3ed852e2009-09-05 21:47:34 +0000336 status=MagickTrue;
337 progress=0;
338 random_info=AcquireRandomInfoThreadSet();
339 image_view=AcquireCacheView(image);
340 noise_view=AcquireCacheView(noise_image);
cristy319a1e72010-02-21 15:13:11 +0000341#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000342 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000343#endif
cristybb503372010-05-27 20:51:26 +0000344 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000345 {
346 MagickBooleanType
347 sync;
348
349 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000350 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000351
352 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000353 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000354
355 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000356 *restrict noise_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000357
cristybb503372010-05-27 20:51:26 +0000358 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000359 id,
360 x;
361
362 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000363 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000364
365 if (status == MagickFalse)
366 continue;
367 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
368 q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
369 exception);
370 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
371 {
372 status=MagickFalse;
373 continue;
374 }
375 indexes=GetCacheViewVirtualIndexQueue(image_view);
376 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
377 id=GetOpenMPThreadId();
cristybb503372010-05-27 20:51:26 +0000378 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000379 {
380 if ((channel & RedChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000381 q->red=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000382 p->red,noise_type,attenuate));
383 if ((channel & GreenChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000384 q->green=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000385 p->green,noise_type,attenuate));
386 if ((channel & BlueChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000387 q->blue=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000388 p->blue,noise_type,attenuate));
389 if ((channel & OpacityChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000390 q->opacity=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000391 p->opacity,noise_type,attenuate));
392 if (((channel & IndexChannel) != 0) &&
393 (image->colorspace == CMYKColorspace))
cristyce70c172010-01-07 17:15:30 +0000394 noise_indexes[x]=(IndexPacket) ClampToQuantum(GenerateDifferentialNoise(
cristy3ed852e2009-09-05 21:47:34 +0000395 random_info[id],indexes[x],noise_type,attenuate));
396 p++;
397 q++;
398 }
399 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
400 if (sync == MagickFalse)
401 status=MagickFalse;
402 if (image->progress_monitor != (MagickProgressMonitor) NULL)
403 {
404 MagickBooleanType
405 proceed;
406
cristyb5d5f722009-11-04 03:03:49 +0000407#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy319a1e72010-02-21 15:13:11 +0000408 #pragma omp critical (MagickCore_AddNoiseImage)
cristy3ed852e2009-09-05 21:47:34 +0000409#endif
410 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
411 image->rows);
412 if (proceed == MagickFalse)
413 status=MagickFalse;
414 }
415 }
416 noise_view=DestroyCacheView(noise_view);
417 image_view=DestroyCacheView(image_view);
418 random_info=DestroyRandomInfoThreadSet(random_info);
419 if (status == MagickFalse)
420 noise_image=DestroyImage(noise_image);
421 return(noise_image);
422}
423
424/*
425%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
426% %
427% %
428% %
429% B l u e S h i f t I m a g e %
430% %
431% %
432% %
433%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
434%
435% BlueShiftImage() mutes the colors of the image to simulate a scene at
436% nighttime in the moonlight.
437%
438% The format of the BlueShiftImage method is:
439%
440% Image *BlueShiftImage(const Image *image,const double factor,
441% ExceptionInfo *exception)
442%
443% A description of each parameter follows:
444%
445% o image: the image.
446%
447% o factor: the shift factor.
448%
449% o exception: return any errors or warnings in this structure.
450%
451*/
452MagickExport Image *BlueShiftImage(const Image *image,const double factor,
453 ExceptionInfo *exception)
454{
455#define BlueShiftImageTag "BlueShift/Image"
456
cristyc4c8d132010-01-07 01:58:38 +0000457 CacheView
458 *image_view,
459 *shift_view;
460
cristy3ed852e2009-09-05 21:47:34 +0000461 Image
462 *shift_image;
463
cristy3ed852e2009-09-05 21:47:34 +0000464 MagickBooleanType
465 status;
466
cristybb503372010-05-27 20:51:26 +0000467 MagickOffsetType
468 progress;
469
470 ssize_t
471 y;
472
cristy3ed852e2009-09-05 21:47:34 +0000473 /*
474 Allocate blue shift image.
475 */
476 assert(image != (const Image *) NULL);
477 assert(image->signature == MagickSignature);
478 if (image->debug != MagickFalse)
479 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
480 assert(exception != (ExceptionInfo *) NULL);
481 assert(exception->signature == MagickSignature);
482 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,
483 exception);
484 if (shift_image == (Image *) NULL)
485 return((Image *) NULL);
486 if (SetImageStorageClass(shift_image,DirectClass) == MagickFalse)
487 {
488 InheritException(exception,&shift_image->exception);
489 shift_image=DestroyImage(shift_image);
490 return((Image *) NULL);
491 }
492 /*
493 Blue-shift DirectClass image.
494 */
495 status=MagickTrue;
496 progress=0;
497 image_view=AcquireCacheView(image);
498 shift_view=AcquireCacheView(shift_image);
cristy319a1e72010-02-21 15:13:11 +0000499#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000500 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000501#endif
cristybb503372010-05-27 20:51:26 +0000502 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000503 {
504 MagickBooleanType
505 sync;
506
507 MagickPixelPacket
508 pixel;
509
510 Quantum
511 quantum;
512
513 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000514 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000515
cristybb503372010-05-27 20:51:26 +0000516 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000517 x;
518
519 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000520 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000521
522 if (status == MagickFalse)
523 continue;
524 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
525 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
526 exception);
527 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
528 {
529 status=MagickFalse;
530 continue;
531 }
cristybb503372010-05-27 20:51:26 +0000532 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000533 {
cristyce70c172010-01-07 17:15:30 +0000534 quantum=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000535 if (p->green < quantum)
cristyce70c172010-01-07 17:15:30 +0000536 quantum=GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000537 if (p->blue < quantum)
cristyce70c172010-01-07 17:15:30 +0000538 quantum=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000539 pixel.red=0.5*(p->red+factor*quantum);
540 pixel.green=0.5*(p->green+factor*quantum);
541 pixel.blue=0.5*(p->blue+factor*quantum);
cristyce70c172010-01-07 17:15:30 +0000542 quantum=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000543 if (p->green > quantum)
cristyce70c172010-01-07 17:15:30 +0000544 quantum=GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000545 if (p->blue > quantum)
cristyce70c172010-01-07 17:15:30 +0000546 quantum=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000547 pixel.red=0.5*(pixel.red+factor*quantum);
548 pixel.green=0.5*(pixel.green+factor*quantum);
549 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristyce70c172010-01-07 17:15:30 +0000550 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
551 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
552 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +0000553 p++;
554 q++;
555 }
556 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
557 if (sync == MagickFalse)
558 status=MagickFalse;
559 if (image->progress_monitor != (MagickProgressMonitor) NULL)
560 {
561 MagickBooleanType
562 proceed;
563
cristy319a1e72010-02-21 15:13:11 +0000564#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000565 #pragma omp critical (MagickCore_BlueShiftImage)
566#endif
567 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
568 image->rows);
569 if (proceed == MagickFalse)
570 status=MagickFalse;
571 }
572 }
573 image_view=DestroyCacheView(image_view);
574 shift_view=DestroyCacheView(shift_view);
575 if (status == MagickFalse)
576 shift_image=DestroyImage(shift_image);
577 return(shift_image);
578}
579
580/*
581%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
582% %
583% %
584% %
585% C h a r c o a l I m a g e %
586% %
587% %
588% %
589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
590%
591% CharcoalImage() creates a new image that is a copy of an existing one with
592% the edge highlighted. It allocates the memory necessary for the new Image
593% structure and returns a pointer to the new image.
594%
595% The format of the CharcoalImage method is:
596%
597% Image *CharcoalImage(const Image *image,const double radius,
598% const double sigma,ExceptionInfo *exception)
599%
600% A description of each parameter follows:
601%
602% o image: the image.
603%
604% o radius: the radius of the pixel neighborhood.
605%
606% o sigma: the standard deviation of the Gaussian, in pixels.
607%
608% o exception: return any errors or warnings in this structure.
609%
610*/
611MagickExport Image *CharcoalImage(const Image *image,const double radius,
612 const double sigma,ExceptionInfo *exception)
613{
614 Image
615 *charcoal_image,
616 *clone_image,
617 *edge_image;
618
619 assert(image != (Image *) NULL);
620 assert(image->signature == MagickSignature);
621 if (image->debug != MagickFalse)
622 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
623 assert(exception != (ExceptionInfo *) NULL);
624 assert(exception->signature == MagickSignature);
625 clone_image=CloneImage(image,0,0,MagickTrue,exception);
626 if (clone_image == (Image *) NULL)
627 return((Image *) NULL);
628 (void) SetImageType(clone_image,GrayscaleType);
629 edge_image=EdgeImage(clone_image,radius,exception);
630 clone_image=DestroyImage(clone_image);
631 if (edge_image == (Image *) NULL)
632 return((Image *) NULL);
633 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
634 edge_image=DestroyImage(edge_image);
635 if (charcoal_image == (Image *) NULL)
636 return((Image *) NULL);
637 (void) NormalizeImage(charcoal_image);
638 (void) NegateImage(charcoal_image,MagickFalse);
639 (void) SetImageType(charcoal_image,GrayscaleType);
640 return(charcoal_image);
641}
642
643/*
644%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
645% %
646% %
647% %
648% C o l o r i z e I m a g e %
649% %
650% %
651% %
652%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
653%
654% ColorizeImage() blends the fill color with each pixel in the image.
655% A percentage blend is specified with opacity. Control the application
656% of different color components by specifying a different percentage for
657% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
658%
659% The format of the ColorizeImage method is:
660%
661% Image *ColorizeImage(const Image *image,const char *opacity,
662% const PixelPacket colorize,ExceptionInfo *exception)
663%
664% A description of each parameter follows:
665%
666% o image: the image.
667%
668% o opacity: A character string indicating the level of opacity as a
669% percentage.
670%
671% o colorize: A color value.
672%
673% o exception: return any errors or warnings in this structure.
674%
675*/
676MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
677 const PixelPacket colorize,ExceptionInfo *exception)
678{
679#define ColorizeImageTag "Colorize/Image"
680
cristyc4c8d132010-01-07 01:58:38 +0000681 CacheView
682 *colorize_view,
683 *image_view;
684
cristy3ed852e2009-09-05 21:47:34 +0000685 GeometryInfo
686 geometry_info;
687
688 Image
689 *colorize_image;
690
cristy3ed852e2009-09-05 21:47:34 +0000691 MagickBooleanType
692 status;
693
cristybb503372010-05-27 20:51:26 +0000694 MagickOffsetType
695 progress;
696
cristy3ed852e2009-09-05 21:47:34 +0000697 MagickPixelPacket
698 pixel;
699
700 MagickStatusType
701 flags;
702
cristybb503372010-05-27 20:51:26 +0000703 ssize_t
704 y;
705
cristy3ed852e2009-09-05 21:47:34 +0000706 /*
707 Allocate colorized image.
708 */
709 assert(image != (const Image *) NULL);
710 assert(image->signature == MagickSignature);
711 if (image->debug != MagickFalse)
712 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
713 assert(exception != (ExceptionInfo *) NULL);
714 assert(exception->signature == MagickSignature);
715 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
716 exception);
717 if (colorize_image == (Image *) NULL)
718 return((Image *) NULL);
719 if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
720 {
721 InheritException(exception,&colorize_image->exception);
722 colorize_image=DestroyImage(colorize_image);
723 return((Image *) NULL);
724 }
725 if (opacity == (const char *) NULL)
726 return(colorize_image);
727 /*
728 Determine RGB values of the pen color.
729 */
730 flags=ParseGeometry(opacity,&geometry_info);
731 pixel.red=geometry_info.rho;
732 pixel.green=geometry_info.rho;
733 pixel.blue=geometry_info.rho;
734 pixel.opacity=(MagickRealType) OpaqueOpacity;
735 if ((flags & SigmaValue) != 0)
736 pixel.green=geometry_info.sigma;
737 if ((flags & XiValue) != 0)
738 pixel.blue=geometry_info.xi;
739 if ((flags & PsiValue) != 0)
740 pixel.opacity=geometry_info.psi;
741 /*
742 Colorize DirectClass image.
743 */
744 status=MagickTrue;
745 progress=0;
746 image_view=AcquireCacheView(image);
747 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000748#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000749 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000750#endif
cristybb503372010-05-27 20:51:26 +0000751 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000752 {
753 MagickBooleanType
754 sync;
755
756 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000757 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000758
cristybb503372010-05-27 20:51:26 +0000759 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000760 x;
761
762 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000763 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000764
765 if (status == MagickFalse)
766 continue;
767 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
768 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
769 exception);
770 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
771 {
772 status=MagickFalse;
773 continue;
774 }
cristybb503372010-05-27 20:51:26 +0000775 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000776 {
777 q->red=(Quantum) ((p->red*(100.0-pixel.red)+
778 colorize.red*pixel.red)/100.0);
779 q->green=(Quantum) ((p->green*(100.0-pixel.green)+
780 colorize.green*pixel.green)/100.0);
781 q->blue=(Quantum) ((p->blue*(100.0-pixel.blue)+
782 colorize.blue*pixel.blue)/100.0);
783 q->opacity=(Quantum) ((p->opacity*(100.0-pixel.opacity)+
784 colorize.opacity*pixel.opacity)/100.0);
785 p++;
786 q++;
787 }
788 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
789 if (sync == MagickFalse)
790 status=MagickFalse;
791 if (image->progress_monitor != (MagickProgressMonitor) NULL)
792 {
793 MagickBooleanType
794 proceed;
795
cristy319a1e72010-02-21 15:13:11 +0000796#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000797 #pragma omp critical (MagickCore_ColorizeImage)
798#endif
799 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
800 if (proceed == MagickFalse)
801 status=MagickFalse;
802 }
803 }
804 image_view=DestroyCacheView(image_view);
805 colorize_view=DestroyCacheView(colorize_view);
806 if (status == MagickFalse)
807 colorize_image=DestroyImage(colorize_image);
808 return(colorize_image);
809}
810
811/*
812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813% %
814% %
815% %
cristye6365592010-04-02 17:31:23 +0000816% C o l o r M a t r i x I m a g e %
817% %
818% %
819% %
820%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821%
822% ColorMatrixImage() applies color transformation to an image. This method
823% permits saturation changes, hue rotation, luminance to alpha, and various
824% other effects. Although variable-sized transformation matrices can be used,
825% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
826% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
827% except offsets are in column 6 rather than 5 (in support of CMYKA images)
828% and offsets are normalized (divide Flash offset by 255).
829%
830% The format of the ColorMatrixImage method is:
831%
832% Image *ColorMatrixImage(const Image *image,
833% const KernelInfo *color_matrix,ExceptionInfo *exception)
834%
835% A description of each parameter follows:
836%
837% o image: the image.
838%
839% o color_matrix: the color matrix.
840%
841% o exception: return any errors or warnings in this structure.
842%
843*/
844MagickExport Image *ColorMatrixImage(const Image *image,
845 const KernelInfo *color_matrix,ExceptionInfo *exception)
846{
847#define ColorMatrixImageTag "ColorMatrix/Image"
848
849 CacheView
850 *color_view,
851 *image_view;
852
853 double
854 ColorMatrix[6][6] =
855 {
856 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
857 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
858 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
859 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
860 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
861 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
862 };
863
864 Image
865 *color_image;
866
cristye6365592010-04-02 17:31:23 +0000867 MagickBooleanType
868 status;
869
cristybb503372010-05-27 20:51:26 +0000870 MagickOffsetType
871 progress;
872
873 register ssize_t
cristye6365592010-04-02 17:31:23 +0000874 i;
875
cristybb503372010-05-27 20:51:26 +0000876 ssize_t
877 u,
878 v,
879 y;
880
cristye6365592010-04-02 17:31:23 +0000881 /*
882 Create color matrix.
883 */
884 assert(image != (Image *) NULL);
885 assert(image->signature == MagickSignature);
886 if (image->debug != MagickFalse)
887 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
888 assert(exception != (ExceptionInfo *) NULL);
889 assert(exception->signature == MagickSignature);
890 i=0;
cristybb503372010-05-27 20:51:26 +0000891 for (v=0; v < (ssize_t) color_matrix->height; v++)
892 for (u=0; u < (ssize_t) color_matrix->width; u++)
cristye6365592010-04-02 17:31:23 +0000893 {
894 if ((v < 6) && (u < 6))
895 ColorMatrix[v][u]=color_matrix->values[i];
896 i++;
897 }
898 /*
899 Initialize color image.
900 */
901 color_image=CloneImage(image,0,0,MagickTrue,exception);
902 if (color_image == (Image *) NULL)
903 return((Image *) NULL);
904 if (SetImageStorageClass(color_image,DirectClass) == MagickFalse)
905 {
906 InheritException(exception,&color_image->exception);
907 color_image=DestroyImage(color_image);
908 return((Image *) NULL);
909 }
910 if (image->debug != MagickFalse)
911 {
912 char
913 format[MaxTextExtent],
914 *message;
915
916 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
917 " ColorMatrix image with color matrix:");
918 message=AcquireString("");
919 for (v=0; v < 6; v++)
920 {
921 *message='\0';
cristye8c25f92010-06-03 00:53:06 +0000922 (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) v);
cristye6365592010-04-02 17:31:23 +0000923 (void) ConcatenateString(&message,format);
924 for (u=0; u < 6; u++)
925 {
926 (void) FormatMagickString(format,MaxTextExtent,"%+f ",
927 ColorMatrix[v][u]);
928 (void) ConcatenateString(&message,format);
929 }
930 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
931 }
932 message=DestroyString(message);
933 }
934 /*
935 ColorMatrix image.
936 */
937 status=MagickTrue;
938 progress=0;
939 image_view=AcquireCacheView(image);
940 color_view=AcquireCacheView(color_image);
941#if defined(MAGICKCORE_OPENMP_SUPPORT)
942 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
943#endif
cristybb503372010-05-27 20:51:26 +0000944 for (y=0; y < (ssize_t) image->rows; y++)
cristye6365592010-04-02 17:31:23 +0000945 {
946 MagickRealType
947 pixel;
948
949 register const IndexPacket
950 *restrict indexes;
951
952 register const PixelPacket
953 *restrict p;
954
cristybb503372010-05-27 20:51:26 +0000955 register ssize_t
cristye6365592010-04-02 17:31:23 +0000956 x;
957
958 register IndexPacket
959 *restrict color_indexes;
960
961 register PixelPacket
962 *restrict q;
963
964 if (status == MagickFalse)
965 continue;
966 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
967 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
968 exception);
969 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
970 {
971 status=MagickFalse;
972 continue;
973 }
974 indexes=GetCacheViewVirtualIndexQueue(image_view);
975 color_indexes=GetCacheViewAuthenticIndexQueue(color_view);
cristybb503372010-05-27 20:51:26 +0000976 for (x=0; x < (ssize_t) image->columns; x++)
cristye6365592010-04-02 17:31:23 +0000977 {
cristybb503372010-05-27 20:51:26 +0000978 register ssize_t
cristye6365592010-04-02 17:31:23 +0000979 v;
980
cristybb503372010-05-27 20:51:26 +0000981 size_t
cristye6365592010-04-02 17:31:23 +0000982 height;
983
984 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
cristybb503372010-05-27 20:51:26 +0000985 for (v=0; v < (ssize_t) height; v++)
cristye6365592010-04-02 17:31:23 +0000986 {
987 pixel=ColorMatrix[v][0]*p->red+ColorMatrix[v][1]*p->green+
988 ColorMatrix[v][2]*p->blue;
989 if (image->matte != MagickFalse)
990 pixel+=ColorMatrix[v][3]*(QuantumRange-p->opacity);
991 if (image->colorspace == CMYKColorspace)
992 pixel+=ColorMatrix[v][4]*indexes[x];
993 pixel+=QuantumRange*ColorMatrix[v][5];
994 switch (v)
995 {
996 case 0: q->red=ClampToQuantum(pixel); break;
997 case 1: q->green=ClampToQuantum(pixel); break;
998 case 2: q->blue=ClampToQuantum(pixel); break;
999 case 3:
1000 {
1001 if (image->matte != MagickFalse)
1002 q->opacity=ClampToQuantum(QuantumRange-pixel);
1003 break;
1004 }
1005 case 4:
1006 {
1007 if (image->colorspace == CMYKColorspace)
1008 color_indexes[x]=ClampToQuantum(pixel);
1009 break;
1010 }
1011 }
1012 }
1013 p++;
1014 q++;
1015 }
1016 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1017 status=MagickFalse;
1018 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1019 {
1020 MagickBooleanType
1021 proceed;
1022
1023#if defined(MAGICKCORE_OPENMP_SUPPORT)
1024 #pragma omp critical (MagickCore_ColorMatrixImage)
1025#endif
1026 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1027 image->rows);
1028 if (proceed == MagickFalse)
1029 status=MagickFalse;
1030 }
1031 }
1032 color_view=DestroyCacheView(color_view);
1033 image_view=DestroyCacheView(image_view);
1034 if (status == MagickFalse)
1035 color_image=DestroyImage(color_image);
1036 return(color_image);
1037}
1038
1039/*
1040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1041% %
1042% %
1043% %
cristy3ed852e2009-09-05 21:47:34 +00001044+ D e s t r o y F x I n f o %
1045% %
1046% %
1047% %
1048%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1049%
1050% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1051%
1052% The format of the DestroyFxInfo method is:
1053%
1054% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1055%
1056% A description of each parameter follows:
1057%
1058% o fx_info: the fx info.
1059%
1060*/
1061MagickExport FxInfo *DestroyFxInfo(FxInfo *fx_info)
1062{
cristybb503372010-05-27 20:51:26 +00001063 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001064 i;
1065
1066 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1067 fx_info->expression=DestroyString(fx_info->expression);
1068 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1069 fx_info->colors=DestroySplayTree(fx_info->colors);
cristybb503372010-05-27 20:51:26 +00001070 for (i=0; i < (ssize_t) GetImageListLength(fx_info->images); i++)
cristy3ed852e2009-09-05 21:47:34 +00001071 fx_info->resample_filter[i]=DestroyResampleFilter(
1072 fx_info->resample_filter[i]);
1073 fx_info->resample_filter=(ResampleFilter **) RelinquishMagickMemory(
1074 fx_info->resample_filter);
1075 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1076 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1077 return(fx_info);
1078}
1079
1080/*
1081%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1082% %
1083% %
1084% %
cristy3ed852e2009-09-05 21:47:34 +00001085+ 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 %
1086% %
1087% %
1088% %
1089%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1090%
1091% FxEvaluateChannelExpression() evaluates an expression and returns the
1092% results.
1093%
1094% The format of the FxEvaluateExpression method is:
1095%
1096% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
cristybb503372010-05-27 20:51:26 +00001097% const ChannelType channel,const ssize_t x,const ssize_t y,
cristy3ed852e2009-09-05 21:47:34 +00001098% MagickRealType *alpha,Exceptioninfo *exception)
1099% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1100% MagickRealType *alpha,Exceptioninfo *exception)
1101%
1102% A description of each parameter follows:
1103%
1104% o fx_info: the fx info.
1105%
1106% o channel: the channel.
1107%
1108% o x,y: the pixel position.
1109%
1110% o alpha: the result.
1111%
1112% o exception: return any errors or warnings in this structure.
1113%
1114*/
1115
cristy351842f2010-03-07 15:27:38 +00001116static inline double MagickMax(const double x,const double y)
1117{
1118 if (x > y)
1119 return(x);
1120 return(y);
1121}
1122
1123static inline double MagickMin(const double x,const double y)
1124{
1125 if (x < y)
1126 return(x);
1127 return(y);
1128}
1129
cristy3ed852e2009-09-05 21:47:34 +00001130static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
1131 ChannelType channel,const char *symbol,ExceptionInfo *exception)
1132{
1133 char
1134 key[MaxTextExtent],
1135 statistic[MaxTextExtent];
1136
1137 const char
1138 *value;
1139
1140 register const char
1141 *p;
1142
1143 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1144 if (*p == '.')
1145 switch (*++p) /* e.g. depth.r */
1146 {
1147 case 'r': channel=RedChannel; break;
1148 case 'g': channel=GreenChannel; break;
1149 case 'b': channel=BlueChannel; break;
1150 case 'c': channel=CyanChannel; break;
1151 case 'm': channel=MagentaChannel; break;
1152 case 'y': channel=YellowChannel; break;
1153 case 'k': channel=BlackChannel; break;
1154 default: break;
1155 }
cristye8c25f92010-06-03 00:53:06 +00001156 (void) FormatMagickString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
1157 (double) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001158 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1159 if (value != (const char *) NULL)
cristyf2f27272009-12-17 14:48:46 +00001160 return(QuantumScale*StringToDouble(value));
cristy3ed852e2009-09-05 21:47:34 +00001161 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1162 if (LocaleNCompare(symbol,"depth",5) == 0)
1163 {
cristybb503372010-05-27 20:51:26 +00001164 size_t
cristy3ed852e2009-09-05 21:47:34 +00001165 depth;
1166
1167 depth=GetImageChannelDepth(image,channel,exception);
cristye8c25f92010-06-03 00:53:06 +00001168 (void) FormatMagickString(statistic,MaxTextExtent,"%.20g",(double)
1169 depth);
cristy3ed852e2009-09-05 21:47:34 +00001170 }
1171 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1172 {
1173 double
1174 kurtosis,
1175 skewness;
1176
1177 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1178 exception);
cristye7f51092010-01-17 00:39:37 +00001179 (void) FormatMagickString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001180 }
1181 if (LocaleNCompare(symbol,"maxima",6) == 0)
1182 {
1183 double
1184 maxima,
1185 minima;
1186
1187 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristye7f51092010-01-17 00:39:37 +00001188 (void) FormatMagickString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001189 }
1190 if (LocaleNCompare(symbol,"mean",4) == 0)
1191 {
1192 double
1193 mean,
1194 standard_deviation;
1195
1196 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1197 exception);
cristye7f51092010-01-17 00:39:37 +00001198 (void) FormatMagickString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001199 }
1200 if (LocaleNCompare(symbol,"minima",6) == 0)
1201 {
1202 double
1203 maxima,
1204 minima;
1205
1206 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristye7f51092010-01-17 00:39:37 +00001207 (void) FormatMagickString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001208 }
1209 if (LocaleNCompare(symbol,"skewness",8) == 0)
1210 {
1211 double
1212 kurtosis,
1213 skewness;
1214
1215 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1216 exception);
cristye7f51092010-01-17 00:39:37 +00001217 (void) FormatMagickString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001218 }
1219 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1220 {
1221 double
1222 mean,
1223 standard_deviation;
1224
1225 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1226 exception);
cristye7f51092010-01-17 00:39:37 +00001227 (void) FormatMagickString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001228 standard_deviation);
1229 }
1230 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1231 ConstantString(statistic));
cristyf2f27272009-12-17 14:48:46 +00001232 return(QuantumScale*StringToDouble(statistic));
cristy3ed852e2009-09-05 21:47:34 +00001233}
1234
1235static MagickRealType
cristye85007d2010-06-06 22:51:36 +00001236 FxEvaluateSubexpression(FxInfo *,const ChannelType,const ssize_t,
1237 const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +00001238
1239static inline MagickRealType FxMax(FxInfo *fx_info,const ChannelType channel,
cristye85007d2010-06-06 22:51:36 +00001240 const ssize_t x,const ssize_t y,const char *expression,
1241 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001242{
1243 MagickRealType
1244 alpha,
1245 beta;
1246
1247 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1248 return((MagickRealType) MagickMax((double) alpha,(double) beta));
1249}
1250
1251static inline MagickRealType FxMin(FxInfo *fx_info,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) MagickMin((double) alpha,(double) beta));
1261}
1262
1263static inline const char *FxSubexpression(const char *expression,
1264 ExceptionInfo *exception)
1265{
1266 const char
1267 *subexpression;
1268
cristybb503372010-05-27 20:51:26 +00001269 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001270 level;
1271
1272 level=0;
1273 subexpression=expression;
1274 while ((*subexpression != '\0') &&
1275 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1276 {
1277 if (strchr("(",(int) *subexpression) != (char *) NULL)
1278 level++;
1279 else
1280 if (strchr(")",(int) *subexpression) != (char *) NULL)
1281 level--;
1282 subexpression++;
1283 }
1284 if (*subexpression == '\0')
1285 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1286 "UnbalancedParenthesis","`%s'",expression);
1287 return(subexpression);
1288}
1289
1290static MagickRealType FxGetSymbol(FxInfo *fx_info,const ChannelType channel,
cristye85007d2010-06-06 22:51:36 +00001291 const ssize_t x,const ssize_t y,const char *expression,
1292 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001293{
1294 char
1295 *q,
1296 subexpression[MaxTextExtent],
1297 symbol[MaxTextExtent];
1298
1299 const char
1300 *p,
1301 *value;
1302
1303 Image
1304 *image;
1305
1306 MagickPixelPacket
1307 pixel;
1308
1309 MagickRealType
1310 alpha,
1311 beta;
1312
1313 PointInfo
1314 point;
1315
cristybb503372010-05-27 20:51:26 +00001316 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001317 i;
1318
1319 size_t
1320 length;
1321
cristybb503372010-05-27 20:51:26 +00001322 size_t
cristy3ed852e2009-09-05 21:47:34 +00001323 level;
1324
1325 p=expression;
1326 i=GetImageIndexInList(fx_info->images);
1327 level=0;
1328 point.x=(double) x;
1329 point.y=(double) y;
1330 if (isalpha((int) *(p+1)) == 0)
1331 {
1332 if (strchr("suv",(int) *p) != (char *) NULL)
1333 {
1334 switch (*p)
1335 {
1336 case 's':
1337 default:
1338 {
1339 i=GetImageIndexInList(fx_info->images);
1340 break;
1341 }
1342 case 'u': i=0; break;
1343 case 'v': i=1; break;
1344 }
1345 p++;
1346 if (*p == '[')
1347 {
1348 level++;
1349 q=subexpression;
1350 for (p++; *p != '\0'; )
1351 {
1352 if (*p == '[')
1353 level++;
1354 else
1355 if (*p == ']')
1356 {
1357 level--;
1358 if (level == 0)
1359 break;
1360 }
1361 *q++=(*p++);
1362 }
1363 *q='\0';
1364 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1365 &beta,exception);
cristybb503372010-05-27 20:51:26 +00001366 i=(ssize_t) (alpha+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001367 p++;
1368 }
1369 if (*p == '.')
1370 p++;
1371 }
1372 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1373 {
1374 p++;
1375 if (*p == '{')
1376 {
1377 level++;
1378 q=subexpression;
1379 for (p++; *p != '\0'; )
1380 {
1381 if (*p == '{')
1382 level++;
1383 else
1384 if (*p == '}')
1385 {
1386 level--;
1387 if (level == 0)
1388 break;
1389 }
1390 *q++=(*p++);
1391 }
1392 *q='\0';
1393 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1394 &beta,exception);
1395 point.x=alpha;
1396 point.y=beta;
1397 p++;
1398 }
1399 else
1400 if (*p == '[')
1401 {
1402 level++;
1403 q=subexpression;
1404 for (p++; *p != '\0'; )
1405 {
1406 if (*p == '[')
1407 level++;
1408 else
1409 if (*p == ']')
1410 {
1411 level--;
1412 if (level == 0)
1413 break;
1414 }
1415 *q++=(*p++);
1416 }
1417 *q='\0';
1418 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1419 &beta,exception);
1420 point.x+=alpha;
1421 point.y+=beta;
1422 p++;
1423 }
1424 if (*p == '.')
1425 p++;
1426 }
1427 }
1428 length=GetImageListLength(fx_info->images);
1429 while (i < 0)
cristybb503372010-05-27 20:51:26 +00001430 i+=(ssize_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001431 i%=length;
1432 image=GetImageFromList(fx_info->images,i);
1433 if (image == (Image *) NULL)
1434 {
1435 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1436 "NoSuchImage","`%s'",expression);
1437 return(0.0);
1438 }
1439 (void) ResamplePixelColor(fx_info->resample_filter[i],point.x,point.y,&pixel);
1440 if ((strlen(p) > 2) &&
1441 (LocaleCompare(p,"intensity") != 0) &&
1442 (LocaleCompare(p,"luminance") != 0) &&
1443 (LocaleCompare(p,"hue") != 0) &&
1444 (LocaleCompare(p,"saturation") != 0) &&
1445 (LocaleCompare(p,"lightness") != 0))
1446 {
1447 char
1448 name[MaxTextExtent];
1449
1450 (void) CopyMagickString(name,p,MaxTextExtent);
1451 for (q=name+(strlen(name)-1); q > name; q--)
1452 {
1453 if (*q == ')')
1454 break;
1455 if (*q == '.')
1456 {
1457 *q='\0';
1458 break;
1459 }
1460 }
1461 if ((strlen(name) > 2) &&
1462 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1463 {
1464 MagickPixelPacket
1465 *color;
1466
1467 color=(MagickPixelPacket *) GetValueFromSplayTree(fx_info->colors,
1468 name);
1469 if (color != (MagickPixelPacket *) NULL)
1470 {
1471 pixel=(*color);
1472 p+=strlen(name);
1473 }
1474 else
1475 if (QueryMagickColor(name,&pixel,fx_info->exception) != MagickFalse)
1476 {
1477 (void) AddValueToSplayTree(fx_info->colors,ConstantString(name),
1478 CloneMagickPixelPacket(&pixel));
1479 p+=strlen(name);
1480 }
1481 }
1482 }
1483 (void) CopyMagickString(symbol,p,MaxTextExtent);
1484 StripString(symbol);
1485 if (*symbol == '\0')
1486 {
1487 switch (channel)
1488 {
1489 case RedChannel: return(QuantumScale*pixel.red);
1490 case GreenChannel: return(QuantumScale*pixel.green);
1491 case BlueChannel: return(QuantumScale*pixel.blue);
1492 case OpacityChannel:
1493 {
1494 if (pixel.matte == MagickFalse)
1495 {
1496 fx_info->matte=MagickFalse;
1497 return(1.0);
1498 }
cristy46f08202010-01-10 04:04:21 +00001499 return((MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel)));
cristy3ed852e2009-09-05 21:47:34 +00001500 }
1501 case IndexChannel:
1502 {
1503 if (image->colorspace != CMYKColorspace)
1504 {
1505 (void) ThrowMagickException(exception,GetMagickModule(),
1506 OptionError,"ColorSeparatedImageRequired","`%s'",
1507 image->filename);
1508 return(0.0);
1509 }
1510 return(QuantumScale*pixel.index);
1511 }
1512 default:
1513 break;
1514 }
1515 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1516 "UnableToParseExpression","`%s'",p);
1517 return(0.0);
1518 }
1519 switch (*symbol)
1520 {
1521 case 'A':
1522 case 'a':
1523 {
1524 if (LocaleCompare(symbol,"a") == 0)
cristy46f08202010-01-10 04:04:21 +00001525 return((MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel)));
cristy3ed852e2009-09-05 21:47:34 +00001526 break;
1527 }
1528 case 'B':
1529 case 'b':
1530 {
1531 if (LocaleCompare(symbol,"b") == 0)
1532 return(QuantumScale*pixel.blue);
1533 break;
1534 }
1535 case 'C':
1536 case 'c':
1537 {
1538 if (LocaleNCompare(symbol,"channel",7) == 0)
1539 {
1540 GeometryInfo
1541 channel_info;
1542
1543 MagickStatusType
1544 flags;
1545
1546 flags=ParseGeometry(symbol+7,&channel_info);
1547 if (image->colorspace == CMYKColorspace)
1548 switch (channel)
1549 {
1550 case CyanChannel:
1551 {
1552 if ((flags & RhoValue) == 0)
1553 return(0.0);
1554 return(channel_info.rho);
1555 }
1556 case MagentaChannel:
1557 {
1558 if ((flags & SigmaValue) == 0)
1559 return(0.0);
1560 return(channel_info.sigma);
1561 }
1562 case YellowChannel:
1563 {
1564 if ((flags & XiValue) == 0)
1565 return(0.0);
1566 return(channel_info.xi);
1567 }
1568 case BlackChannel:
1569 {
1570 if ((flags & PsiValue) == 0)
1571 return(0.0);
1572 return(channel_info.psi);
1573 }
1574 case OpacityChannel:
1575 {
1576 if ((flags & ChiValue) == 0)
1577 return(0.0);
1578 return(channel_info.chi);
1579 }
1580 default:
1581 return(0.0);
1582 }
1583 switch (channel)
1584 {
1585 case RedChannel:
1586 {
1587 if ((flags & RhoValue) == 0)
1588 return(0.0);
1589 return(channel_info.rho);
1590 }
1591 case GreenChannel:
1592 {
1593 if ((flags & SigmaValue) == 0)
1594 return(0.0);
1595 return(channel_info.sigma);
1596 }
1597 case BlueChannel:
1598 {
1599 if ((flags & XiValue) == 0)
1600 return(0.0);
1601 return(channel_info.xi);
1602 }
1603 case OpacityChannel:
1604 {
1605 if ((flags & PsiValue) == 0)
1606 return(0.0);
1607 return(channel_info.psi);
1608 }
1609 case IndexChannel:
1610 {
1611 if ((flags & ChiValue) == 0)
1612 return(0.0);
1613 return(channel_info.chi);
1614 }
1615 default:
1616 return(0.0);
1617 }
1618 return(0.0);
1619 }
1620 if (LocaleCompare(symbol,"c") == 0)
1621 return(QuantumScale*pixel.red);
1622 break;
1623 }
1624 case 'D':
1625 case 'd':
1626 {
1627 if (LocaleNCompare(symbol,"depth",5) == 0)
1628 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1629 break;
1630 }
1631 case 'G':
1632 case 'g':
1633 {
1634 if (LocaleCompare(symbol,"g") == 0)
1635 return(QuantumScale*pixel.green);
1636 break;
1637 }
1638 case 'K':
1639 case 'k':
1640 {
1641 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1642 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1643 if (LocaleCompare(symbol,"k") == 0)
1644 {
1645 if (image->colorspace != CMYKColorspace)
1646 {
1647 (void) ThrowMagickException(exception,GetMagickModule(),
1648 OptionError,"ColorSeparatedImageRequired","`%s'",
1649 image->filename);
1650 return(0.0);
1651 }
1652 return(QuantumScale*pixel.index);
1653 }
1654 break;
1655 }
1656 case 'H':
1657 case 'h':
1658 {
1659 if (LocaleCompare(symbol,"h") == 0)
1660 return((MagickRealType) image->rows);
1661 if (LocaleCompare(symbol,"hue") == 0)
1662 {
1663 double
1664 hue,
1665 lightness,
1666 saturation;
1667
cristyce70c172010-01-07 17:15:30 +00001668 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1669 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001670 return(hue);
1671 }
1672 break;
1673 }
1674 case 'I':
1675 case 'i':
1676 {
1677 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1678 (LocaleCompare(symbol,"image.minima") == 0) ||
1679 (LocaleCompare(symbol,"image.maxima") == 0) ||
1680 (LocaleCompare(symbol,"image.mean") == 0) ||
1681 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1682 (LocaleCompare(symbol,"image.skewness") == 0) ||
1683 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1684 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1685 if (LocaleCompare(symbol,"image.resolution.x") == 0)
1686 return(image->x_resolution);
1687 if (LocaleCompare(symbol,"image.resolution.y") == 0)
1688 return(image->y_resolution);
1689 if (LocaleCompare(symbol,"intensity") == 0)
1690 return(QuantumScale*MagickPixelIntensityToQuantum(&pixel));
1691 if (LocaleCompare(symbol,"i") == 0)
1692 return((MagickRealType) x);
1693 break;
1694 }
1695 case 'J':
1696 case 'j':
1697 {
1698 if (LocaleCompare(symbol,"j") == 0)
1699 return((MagickRealType) y);
1700 break;
1701 }
1702 case 'L':
1703 case 'l':
1704 {
1705 if (LocaleCompare(symbol,"lightness") == 0)
1706 {
1707 double
1708 hue,
1709 lightness,
1710 saturation;
1711
cristyce70c172010-01-07 17:15:30 +00001712 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1713 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001714 return(lightness);
1715 }
1716 if (LocaleCompare(symbol,"luminance") == 0)
1717 {
1718 double
1719 luminence;
1720
1721 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1722 return(QuantumScale*luminence);
1723 }
1724 break;
1725 }
1726 case 'M':
1727 case 'm':
1728 {
1729 if (LocaleNCompare(symbol,"maxima",6) == 0)
1730 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1731 if (LocaleNCompare(symbol,"mean",4) == 0)
1732 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1733 if (LocaleNCompare(symbol,"minima",6) == 0)
1734 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1735 if (LocaleCompare(symbol,"m") == 0)
1736 return(QuantumScale*pixel.blue);
1737 break;
1738 }
1739 case 'N':
1740 case 'n':
1741 {
1742 if (LocaleCompare(symbol,"n") == 0)
1743 return((MagickRealType) GetImageListLength(fx_info->images));
1744 break;
1745 }
1746 case 'O':
1747 case 'o':
1748 {
1749 if (LocaleCompare(symbol,"o") == 0)
1750 return(QuantumScale*pixel.opacity);
1751 break;
1752 }
1753 case 'P':
1754 case 'p':
1755 {
1756 if (LocaleCompare(symbol,"page.height") == 0)
1757 return((MagickRealType) image->page.height);
1758 if (LocaleCompare(symbol,"page.width") == 0)
1759 return((MagickRealType) image->page.width);
1760 if (LocaleCompare(symbol,"page.x") == 0)
1761 return((MagickRealType) image->page.x);
1762 if (LocaleCompare(symbol,"page.y") == 0)
1763 return((MagickRealType) image->page.y);
1764 break;
1765 }
1766 case 'R':
1767 case 'r':
1768 {
1769 if (LocaleCompare(symbol,"resolution.x") == 0)
1770 return(image->x_resolution);
1771 if (LocaleCompare(symbol,"resolution.y") == 0)
1772 return(image->y_resolution);
1773 if (LocaleCompare(symbol,"r") == 0)
1774 return(QuantumScale*pixel.red);
1775 break;
1776 }
1777 case 'S':
1778 case 's':
1779 {
1780 if (LocaleCompare(symbol,"saturation") == 0)
1781 {
1782 double
1783 hue,
1784 lightness,
1785 saturation;
1786
cristyce70c172010-01-07 17:15:30 +00001787 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1788 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001789 return(saturation);
1790 }
1791 if (LocaleNCompare(symbol,"skewness",8) == 0)
1792 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1793 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1794 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1795 break;
1796 }
1797 case 'T':
1798 case 't':
1799 {
1800 if (LocaleCompare(symbol,"t") == 0)
1801 return((MagickRealType) fx_info->images->scene);
1802 break;
1803 }
1804 case 'W':
1805 case 'w':
1806 {
1807 if (LocaleCompare(symbol,"w") == 0)
1808 return((MagickRealType) image->columns);
1809 break;
1810 }
1811 case 'Y':
1812 case 'y':
1813 {
1814 if (LocaleCompare(symbol,"y") == 0)
1815 return(QuantumScale*pixel.green);
1816 break;
1817 }
1818 case 'Z':
1819 case 'z':
1820 {
1821 if (LocaleCompare(symbol,"z") == 0)
1822 {
1823 MagickRealType
1824 depth;
1825
1826 depth=(MagickRealType) GetImageChannelDepth(image,channel,
1827 fx_info->exception);
1828 return(depth);
1829 }
1830 break;
1831 }
1832 default:
1833 break;
1834 }
1835 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1836 if (value != (const char *) NULL)
cristyf2f27272009-12-17 14:48:46 +00001837 return((MagickRealType) StringToDouble(value));
cristy3ed852e2009-09-05 21:47:34 +00001838 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1839 "UnableToParseExpression","`%s'",symbol);
1840 return(0.0);
1841}
1842
1843static const char *FxOperatorPrecedence(const char *expression,
1844 ExceptionInfo *exception)
1845{
1846 typedef enum
1847 {
1848 UndefinedPrecedence,
1849 NullPrecedence,
1850 BitwiseComplementPrecedence,
1851 ExponentPrecedence,
1852 MultiplyPrecedence,
1853 AdditionPrecedence,
1854 ShiftPrecedence,
1855 RelationalPrecedence,
1856 EquivalencyPrecedence,
1857 BitwiseAndPrecedence,
1858 BitwiseOrPrecedence,
1859 LogicalAndPrecedence,
1860 LogicalOrPrecedence,
1861 TernaryPrecedence,
1862 AssignmentPrecedence,
1863 CommaPrecedence,
1864 SeparatorPrecedence
1865 } FxPrecedence;
1866
1867 FxPrecedence
1868 precedence,
1869 target;
1870
1871 register const char
1872 *subexpression;
1873
1874 register int
1875 c;
1876
cristybb503372010-05-27 20:51:26 +00001877 size_t
cristy3ed852e2009-09-05 21:47:34 +00001878 level;
1879
1880 c=0;
1881 level=0;
1882 subexpression=(const char *) NULL;
1883 target=NullPrecedence;
1884 while (*expression != '\0')
1885 {
1886 precedence=UndefinedPrecedence;
1887 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1888 {
1889 expression++;
1890 continue;
1891 }
cristy488fa882010-03-01 22:34:24 +00001892 switch (*expression)
1893 {
1894 case 'A':
1895 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001896 {
cristy488fa882010-03-01 22:34:24 +00001897 if (LocaleNCompare(expression,"atan2",5) == 0)
1898 {
1899 expression+=5;
1900 break;
1901 }
1902 break;
cristy3ed852e2009-09-05 21:47:34 +00001903 }
cristy488fa882010-03-01 22:34:24 +00001904 case 'J':
1905 case 'j':
1906 {
1907 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1908 (LocaleNCompare(expression,"j1",2) == 0))
1909 {
1910 expression+=2;
1911 break;
1912 }
1913 break;
1914 }
1915 default:
1916 break;
1917 }
cristy3ed852e2009-09-05 21:47:34 +00001918 if ((c == (int) '{') || (c == (int) '['))
1919 level++;
1920 else
1921 if ((c == (int) '}') || (c == (int) ']'))
1922 level--;
1923 if (level == 0)
1924 switch ((unsigned char) *expression)
1925 {
1926 case '~':
1927 case '!':
1928 {
1929 precedence=BitwiseComplementPrecedence;
1930 break;
1931 }
1932 case '^':
1933 {
1934 precedence=ExponentPrecedence;
1935 break;
1936 }
1937 default:
1938 {
1939 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1940 (strchr(")",c) != (char *) NULL))) &&
1941 (((islower((int) ((char) *expression)) != 0) ||
1942 (strchr("(",(int) *expression) != (char *) NULL)) ||
1943 ((isdigit((int) ((char) c)) == 0) &&
1944 (isdigit((int) ((char) *expression)) != 0))) &&
1945 (strchr("xy",(int) *expression) == (char *) NULL))
1946 precedence=MultiplyPrecedence;
1947 break;
1948 }
1949 case '*':
1950 case '/':
1951 case '%':
1952 {
1953 precedence=MultiplyPrecedence;
1954 break;
1955 }
1956 case '+':
1957 case '-':
1958 {
1959 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1960 (isalpha(c) != 0))
1961 precedence=AdditionPrecedence;
1962 break;
1963 }
1964 case LeftShiftOperator:
1965 case RightShiftOperator:
1966 {
1967 precedence=ShiftPrecedence;
1968 break;
1969 }
1970 case '<':
1971 case LessThanEqualOperator:
1972 case GreaterThanEqualOperator:
1973 case '>':
1974 {
1975 precedence=RelationalPrecedence;
1976 break;
1977 }
1978 case EqualOperator:
1979 case NotEqualOperator:
1980 {
1981 precedence=EquivalencyPrecedence;
1982 break;
1983 }
1984 case '&':
1985 {
1986 precedence=BitwiseAndPrecedence;
1987 break;
1988 }
1989 case '|':
1990 {
1991 precedence=BitwiseOrPrecedence;
1992 break;
1993 }
1994 case LogicalAndOperator:
1995 {
1996 precedence=LogicalAndPrecedence;
1997 break;
1998 }
1999 case LogicalOrOperator:
2000 {
2001 precedence=LogicalOrPrecedence;
2002 break;
2003 }
2004 case ':':
2005 case '?':
2006 {
2007 precedence=TernaryPrecedence;
2008 break;
2009 }
2010 case '=':
2011 {
2012 precedence=AssignmentPrecedence;
2013 break;
2014 }
2015 case ',':
2016 {
2017 precedence=CommaPrecedence;
2018 break;
2019 }
2020 case ';':
2021 {
2022 precedence=SeparatorPrecedence;
2023 break;
2024 }
2025 }
2026 if ((precedence == BitwiseComplementPrecedence) ||
2027 (precedence == TernaryPrecedence) ||
2028 (precedence == AssignmentPrecedence))
2029 {
2030 if (precedence > target)
2031 {
2032 /*
2033 Right-to-left associativity.
2034 */
2035 target=precedence;
2036 subexpression=expression;
2037 }
2038 }
2039 else
2040 if (precedence >= target)
2041 {
2042 /*
2043 Left-to-right associativity.
2044 */
2045 target=precedence;
2046 subexpression=expression;
2047 }
2048 if (strchr("(",(int) *expression) != (char *) NULL)
2049 expression=FxSubexpression(expression,exception);
2050 c=(int) (*expression++);
2051 }
2052 return(subexpression);
2053}
2054
2055static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
cristye85007d2010-06-06 22:51:36 +00002056 const ChannelType channel,const ssize_t x,const ssize_t y,
2057 const char *expression,MagickRealType *beta,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002058{
2059 char
2060 *q,
2061 subexpression[MaxTextExtent];
2062
2063 MagickRealType
2064 alpha,
2065 gamma;
2066
2067 register const char
2068 *p;
2069
2070 *beta=0.0;
2071 if (exception->severity != UndefinedException)
2072 return(0.0);
2073 while (isspace((int) *expression) != 0)
2074 expression++;
2075 if (*expression == '\0')
2076 {
2077 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2078 "MissingExpression","`%s'",expression);
2079 return(0.0);
2080 }
cristy66322f02010-05-17 11:40:48 +00002081 *subexpression='\0';
cristy3ed852e2009-09-05 21:47:34 +00002082 p=FxOperatorPrecedence(expression,exception);
2083 if (p != (const char *) NULL)
2084 {
2085 (void) CopyMagickString(subexpression,expression,(size_t)
2086 (p-expression+1));
2087 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2088 exception);
2089 switch ((unsigned char) *p)
2090 {
2091 case '~':
2092 {
2093 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002094 *beta=(MagickRealType) (~(size_t) *beta);
cristy3ed852e2009-09-05 21:47:34 +00002095 return(*beta);
2096 }
2097 case '!':
2098 {
2099 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2100 return(*beta == 0.0 ? 1.0 : 0.0);
2101 }
2102 case '^':
2103 {
2104 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2105 channel,x,y,++p,beta,exception));
2106 return(*beta);
2107 }
2108 case '*':
2109 {
2110 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2111 return(alpha*(*beta));
2112 }
2113 case '/':
2114 {
2115 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2116 if (*beta == 0.0)
2117 {
2118 if (exception->severity == UndefinedException)
2119 (void) ThrowMagickException(exception,GetMagickModule(),
2120 OptionError,"DivideByZero","`%s'",expression);
2121 return(0.0);
2122 }
2123 return(alpha/(*beta));
2124 }
2125 case '%':
2126 {
2127 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2128 *beta=fabs(floor(((double) *beta)+0.5));
2129 if (*beta == 0.0)
2130 {
2131 (void) ThrowMagickException(exception,GetMagickModule(),
2132 OptionError,"DivideByZero","`%s'",expression);
2133 return(0.0);
2134 }
2135 return(fmod((double) alpha,(double) *beta));
2136 }
2137 case '+':
2138 {
2139 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2140 return(alpha+(*beta));
2141 }
2142 case '-':
2143 {
2144 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2145 return(alpha-(*beta));
2146 }
2147 case LeftShiftOperator:
2148 {
2149 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002150 *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002151 (gamma+0.5));
2152 return(*beta);
2153 }
2154 case RightShiftOperator:
2155 {
2156 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002157 *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002158 (gamma+0.5));
2159 return(*beta);
2160 }
2161 case '<':
2162 {
2163 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2164 return(alpha < *beta ? 1.0 : 0.0);
2165 }
2166 case LessThanEqualOperator:
2167 {
2168 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2169 return(alpha <= *beta ? 1.0 : 0.0);
2170 }
2171 case '>':
2172 {
2173 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2174 return(alpha > *beta ? 1.0 : 0.0);
2175 }
2176 case GreaterThanEqualOperator:
2177 {
2178 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2179 return(alpha >= *beta ? 1.0 : 0.0);
2180 }
2181 case EqualOperator:
2182 {
2183 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2184 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2185 }
2186 case NotEqualOperator:
2187 {
2188 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2189 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2190 }
2191 case '&':
2192 {
2193 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002194 *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002195 (gamma+0.5));
2196 return(*beta);
2197 }
2198 case '|':
2199 {
2200 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristybb503372010-05-27 20:51:26 +00002201 *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t)
cristy3ed852e2009-09-05 21:47:34 +00002202 (gamma+0.5));
2203 return(*beta);
2204 }
2205 case LogicalAndOperator:
2206 {
2207 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2208 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2209 return(*beta);
2210 }
2211 case LogicalOrOperator:
2212 {
2213 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2214 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2215 return(*beta);
2216 }
2217 case '?':
2218 {
2219 MagickRealType
2220 gamma;
2221
2222 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2223 q=subexpression;
2224 p=StringToken(":",&q);
2225 if (q == (char *) NULL)
2226 {
2227 (void) ThrowMagickException(exception,GetMagickModule(),
2228 OptionError,"UnableToParseExpression","`%s'",subexpression);
2229 return(0.0);
2230 }
2231 if (fabs((double) alpha) > MagickEpsilon)
2232 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2233 else
2234 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2235 return(gamma);
2236 }
2237 case '=':
2238 {
2239 char
2240 numeric[MaxTextExtent];
2241
2242 q=subexpression;
2243 while (isalpha((int) ((unsigned char) *q)) != 0)
2244 q++;
2245 if (*q != '\0')
2246 {
2247 (void) ThrowMagickException(exception,GetMagickModule(),
2248 OptionError,"UnableToParseExpression","`%s'",subexpression);
2249 return(0.0);
2250 }
2251 ClearMagickException(exception);
2252 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristye7f51092010-01-17 00:39:37 +00002253 (void) FormatMagickString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002254 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002255 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2256 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2257 subexpression),ConstantString(numeric));
2258 return(*beta);
2259 }
2260 case ',':
2261 {
2262 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2263 return(alpha);
2264 }
2265 case ';':
2266 {
2267 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2268 return(*beta);
2269 }
2270 default:
2271 {
2272 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2273 exception);
2274 return(gamma);
2275 }
2276 }
2277 }
2278 if (strchr("(",(int) *expression) != (char *) NULL)
2279 {
2280 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2281 subexpression[strlen(subexpression)-1]='\0';
2282 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2283 exception);
2284 return(gamma);
2285 }
2286 switch (*expression)
2287 {
2288 case '+':
2289 {
2290 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2291 exception);
2292 return(1.0*gamma);
2293 }
2294 case '-':
2295 {
2296 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2297 exception);
2298 return(-1.0*gamma);
2299 }
2300 case '~':
2301 {
2302 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2303 exception);
cristybb503372010-05-27 20:51:26 +00002304 return((MagickRealType) (~(size_t) (gamma+0.5)));
cristy3ed852e2009-09-05 21:47:34 +00002305 }
2306 case 'A':
2307 case 'a':
2308 {
2309 if (LocaleNCompare(expression,"abs",3) == 0)
2310 {
2311 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2312 exception);
2313 return((MagickRealType) fabs((double) alpha));
2314 }
2315 if (LocaleNCompare(expression,"acos",4) == 0)
2316 {
2317 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2318 exception);
2319 return((MagickRealType) acos((double) alpha));
2320 }
cristy43c22f42010-03-30 12:34:07 +00002321#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002322 if (LocaleNCompare(expression,"airy",4) == 0)
2323 {
2324 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2325 exception);
2326 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002327 return(1.0);
2328 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002329 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002330 }
cristy43c22f42010-03-30 12:34:07 +00002331#endif
cristy3ed852e2009-09-05 21:47:34 +00002332 if (LocaleNCompare(expression,"asin",4) == 0)
2333 {
2334 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2335 exception);
2336 return((MagickRealType) asin((double) alpha));
2337 }
2338 if (LocaleNCompare(expression,"alt",3) == 0)
2339 {
2340 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2341 exception);
cristybb503372010-05-27 20:51:26 +00002342 return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00002343 }
2344 if (LocaleNCompare(expression,"atan2",5) == 0)
2345 {
2346 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2347 exception);
2348 return((MagickRealType) atan2((double) alpha,(double) *beta));
2349 }
2350 if (LocaleNCompare(expression,"atan",4) == 0)
2351 {
2352 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2353 exception);
2354 return((MagickRealType) atan((double) alpha));
2355 }
2356 if (LocaleCompare(expression,"a") == 0)
2357 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2358 break;
2359 }
2360 case 'B':
2361 case 'b':
2362 {
2363 if (LocaleCompare(expression,"b") == 0)
2364 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2365 break;
2366 }
2367 case 'C':
2368 case 'c':
2369 {
2370 if (LocaleNCompare(expression,"ceil",4) == 0)
2371 {
2372 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2373 exception);
2374 return((MagickRealType) ceil((double) alpha));
2375 }
2376 if (LocaleNCompare(expression,"cosh",4) == 0)
2377 {
2378 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2379 exception);
2380 return((MagickRealType) cosh((double) alpha));
2381 }
2382 if (LocaleNCompare(expression,"cos",3) == 0)
2383 {
2384 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2385 exception);
2386 return((MagickRealType) cos((double) alpha));
2387 }
2388 if (LocaleCompare(expression,"c") == 0)
2389 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2390 break;
2391 }
2392 case 'D':
2393 case 'd':
2394 {
2395 if (LocaleNCompare(expression,"debug",5) == 0)
2396 {
2397 const char
2398 *type;
2399
2400 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2401 exception);
2402 if (fx_info->images->colorspace == CMYKColorspace)
2403 switch (channel)
2404 {
2405 case CyanChannel: type="cyan"; break;
2406 case MagentaChannel: type="magenta"; break;
2407 case YellowChannel: type="yellow"; break;
2408 case OpacityChannel: type="opacity"; break;
2409 case BlackChannel: type="black"; break;
2410 default: type="unknown"; break;
2411 }
2412 else
2413 switch (channel)
2414 {
2415 case RedChannel: type="red"; break;
2416 case GreenChannel: type="green"; break;
2417 case BlueChannel: type="blue"; break;
2418 case OpacityChannel: type="opacity"; break;
2419 default: type="unknown"; break;
2420 }
2421 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2422 if (strlen(subexpression) > 1)
2423 subexpression[strlen(subexpression)-1]='\0';
2424 if (fx_info->file != (FILE *) NULL)
cristye8c25f92010-06-03 00:53:06 +00002425 (void) fprintf(fx_info->file,"%s[%.20g,%.20g].%s: %s=%.*g\n",
2426 fx_info->images->filename,(double) x,(double) y,type,
2427 subexpression,GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002428 return(0.0);
2429 }
2430 break;
2431 }
2432 case 'E':
2433 case 'e':
2434 {
2435 if (LocaleCompare(expression,"epsilon") == 0)
2436 return((MagickRealType) MagickEpsilon);
2437 if (LocaleNCompare(expression,"exp",3) == 0)
2438 {
2439 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2440 exception);
2441 return((MagickRealType) exp((double) alpha));
2442 }
2443 if (LocaleCompare(expression,"e") == 0)
2444 return((MagickRealType) 2.7182818284590452354);
2445 break;
2446 }
2447 case 'F':
2448 case 'f':
2449 {
2450 if (LocaleNCompare(expression,"floor",5) == 0)
2451 {
2452 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2453 exception);
2454 return((MagickRealType) floor((double) alpha));
2455 }
2456 break;
2457 }
2458 case 'G':
2459 case 'g':
2460 {
2461 if (LocaleCompare(expression,"g") == 0)
2462 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2463 break;
2464 }
2465 case 'H':
2466 case 'h':
2467 {
2468 if (LocaleCompare(expression,"h") == 0)
2469 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2470 if (LocaleCompare(expression,"hue") == 0)
2471 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2472 if (LocaleNCompare(expression,"hypot",5) == 0)
2473 {
2474 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2475 exception);
2476 return((MagickRealType) hypot((double) alpha,(double) *beta));
2477 }
2478 break;
2479 }
2480 case 'K':
2481 case 'k':
2482 {
2483 if (LocaleCompare(expression,"k") == 0)
2484 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2485 break;
2486 }
2487 case 'I':
2488 case 'i':
2489 {
2490 if (LocaleCompare(expression,"intensity") == 0)
2491 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2492 if (LocaleNCompare(expression,"int",3) == 0)
2493 {
2494 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2495 exception);
2496 return((MagickRealType) floor(alpha+0.5));
2497 }
2498 if (LocaleCompare(expression,"i") == 0)
2499 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2500 break;
2501 }
2502 case 'J':
2503 case 'j':
2504 {
2505 if (LocaleCompare(expression,"j") == 0)
2506 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002507#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002508 if (LocaleNCompare(expression,"j0",2) == 0)
2509 {
2510 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2511 exception);
2512 return((MagickRealType) j0((double) alpha));
2513 }
cristy161b9262010-03-20 19:34:32 +00002514#endif
2515#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002516 if (LocaleNCompare(expression,"j1",2) == 0)
2517 {
2518 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2519 exception);
2520 return((MagickRealType) j1((double) alpha));
2521 }
cristy161b9262010-03-20 19:34:32 +00002522#endif
cristyaa018fa2010-04-08 23:03:54 +00002523#if defined(MAGICKCORE_HAVE_J1)
cristya6a09e72010-03-02 14:51:02 +00002524 if (LocaleNCompare(expression,"jinc",4) == 0)
2525 {
2526 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2527 exception);
cristy0946a822010-03-12 17:14:58 +00002528 if (alpha == 0.0)
2529 return(1.0);
cristy69928f92010-03-12 13:27:09 +00002530 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/
cristyfce2f7b2010-03-12 00:29:49 +00002531 (MagickPI*alpha));
2532 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002533 }
cristyaa018fa2010-04-08 23:03:54 +00002534#endif
cristy3ed852e2009-09-05 21:47:34 +00002535 break;
2536 }
2537 case 'L':
2538 case 'l':
2539 {
2540 if (LocaleNCompare(expression,"ln",2) == 0)
2541 {
2542 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2543 exception);
2544 return((MagickRealType) log((double) alpha));
2545 }
2546 if (LocaleNCompare(expression,"logtwo",4) == 0)
2547 {
2548 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2549 exception);
2550 return((MagickRealType) log10((double) alpha))/log10(2.0);
2551 }
2552 if (LocaleNCompare(expression,"log",3) == 0)
2553 {
2554 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2555 exception);
2556 return((MagickRealType) log10((double) alpha));
2557 }
2558 if (LocaleCompare(expression,"lightness") == 0)
2559 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2560 break;
2561 }
2562 case 'M':
2563 case 'm':
2564 {
2565 if (LocaleCompare(expression,"MaxRGB") == 0)
2566 return((MagickRealType) QuantumRange);
2567 if (LocaleNCompare(expression,"maxima",6) == 0)
2568 break;
2569 if (LocaleNCompare(expression,"max",3) == 0)
2570 return(FxMax(fx_info,channel,x,y,expression+3,exception));
2571 if (LocaleNCompare(expression,"minima",6) == 0)
2572 break;
2573 if (LocaleNCompare(expression,"min",3) == 0)
2574 return(FxMin(fx_info,channel,x,y,expression+3,exception));
2575 if (LocaleNCompare(expression,"mod",3) == 0)
2576 {
2577 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2578 exception);
2579 return((MagickRealType) fmod((double) alpha,(double) *beta));
2580 }
2581 if (LocaleCompare(expression,"m") == 0)
2582 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2583 break;
2584 }
2585 case 'N':
2586 case 'n':
2587 {
2588 if (LocaleCompare(expression,"n") == 0)
2589 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2590 break;
2591 }
2592 case 'O':
2593 case 'o':
2594 {
2595 if (LocaleCompare(expression,"Opaque") == 0)
2596 return(1.0);
2597 if (LocaleCompare(expression,"o") == 0)
2598 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2599 break;
2600 }
2601 case 'P':
2602 case 'p':
2603 {
2604 if (LocaleCompare(expression,"pi") == 0)
2605 return((MagickRealType) MagickPI);
2606 if (LocaleNCompare(expression,"pow",3) == 0)
2607 {
2608 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2609 exception);
2610 return((MagickRealType) pow((double) alpha,(double) *beta));
2611 }
2612 if (LocaleCompare(expression,"p") == 0)
2613 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2614 break;
2615 }
2616 case 'Q':
2617 case 'q':
2618 {
2619 if (LocaleCompare(expression,"QuantumRange") == 0)
2620 return((MagickRealType) QuantumRange);
2621 if (LocaleCompare(expression,"QuantumScale") == 0)
2622 return((MagickRealType) QuantumScale);
2623 break;
2624 }
2625 case 'R':
2626 case 'r':
2627 {
2628 if (LocaleNCompare(expression,"rand",4) == 0)
2629 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2630 if (LocaleNCompare(expression,"round",5) == 0)
2631 {
2632 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2633 exception);
2634 if (alpha >= 0.0)
2635 return((MagickRealType) floor((double) alpha+0.5));
2636 return((MagickRealType) ceil((double) alpha-0.5));
2637 }
2638 if (LocaleCompare(expression,"r") == 0)
2639 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2640 break;
2641 }
2642 case 'S':
2643 case 's':
2644 {
2645 if (LocaleCompare(expression,"saturation") == 0)
2646 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2647 if (LocaleNCompare(expression,"sign",4) == 0)
2648 {
2649 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2650 exception);
2651 return(alpha < 0.0 ? -1.0 : 1.0);
2652 }
cristya6a09e72010-03-02 14:51:02 +00002653 if (LocaleNCompare(expression,"sinc",4) == 0)
2654 {
2655 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2656 exception);
2657 if (alpha == 0)
2658 return(1.0);
2659 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2660 (MagickPI*alpha));
2661 return(gamma);
2662 }
cristy3ed852e2009-09-05 21:47:34 +00002663 if (LocaleNCompare(expression,"sinh",4) == 0)
2664 {
2665 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2666 exception);
2667 return((MagickRealType) sinh((double) alpha));
2668 }
2669 if (LocaleNCompare(expression,"sin",3) == 0)
2670 {
2671 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2672 exception);
2673 return((MagickRealType) sin((double) alpha));
2674 }
2675 if (LocaleNCompare(expression,"sqrt",4) == 0)
2676 {
2677 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2678 exception);
2679 return((MagickRealType) sqrt((double) alpha));
2680 }
2681 if (LocaleCompare(expression,"s") == 0)
2682 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2683 break;
2684 }
2685 case 'T':
2686 case 't':
2687 {
2688 if (LocaleNCompare(expression,"tanh",4) == 0)
2689 {
2690 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2691 exception);
2692 return((MagickRealType) tanh((double) alpha));
2693 }
2694 if (LocaleNCompare(expression,"tan",3) == 0)
2695 {
2696 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2697 exception);
2698 return((MagickRealType) tan((double) alpha));
2699 }
2700 if (LocaleCompare(expression,"Transparent") == 0)
2701 return(0.0);
2702 if (LocaleCompare(expression,"t") == 0)
2703 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2704 break;
2705 }
2706 case 'U':
2707 case 'u':
2708 {
2709 if (LocaleCompare(expression,"u") == 0)
2710 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2711 break;
2712 }
2713 case 'V':
2714 case 'v':
2715 {
2716 if (LocaleCompare(expression,"v") == 0)
2717 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2718 break;
2719 }
2720 case 'W':
2721 case 'w':
2722 {
2723 if (LocaleCompare(expression,"w") == 0)
2724 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2725 break;
2726 }
2727 case 'Y':
2728 case 'y':
2729 {
2730 if (LocaleCompare(expression,"y") == 0)
2731 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2732 break;
2733 }
2734 case 'Z':
2735 case 'z':
2736 {
2737 if (LocaleCompare(expression,"z") == 0)
2738 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2739 break;
2740 }
2741 default:
2742 break;
2743 }
2744 q=(char *) expression;
2745 alpha=strtod(expression,&q);
2746 if (q == expression)
2747 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2748 return(alpha);
2749}
2750
2751MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2752 MagickRealType *alpha,ExceptionInfo *exception)
2753{
2754 MagickBooleanType
2755 status;
2756
2757 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2758 return(status);
2759}
2760
2761MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2762 MagickRealType *alpha,ExceptionInfo *exception)
2763{
2764 FILE
2765 *file;
2766
2767 MagickBooleanType
2768 status;
2769
2770 file=fx_info->file;
2771 fx_info->file=(FILE *) NULL;
2772 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2773 fx_info->file=file;
2774 return(status);
2775}
2776
2777MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
cristye85007d2010-06-06 22:51:36 +00002778 const ChannelType channel,const ssize_t x,const ssize_t y,
2779 MagickRealType *alpha,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002780{
2781 MagickRealType
2782 beta;
2783
2784 beta=0.0;
2785 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2786 exception);
2787 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2788}
2789
2790/*
2791%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2792% %
2793% %
2794% %
2795% F x I m a g e %
2796% %
2797% %
2798% %
2799%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2800%
2801% FxImage() applies a mathematical expression to the specified image.
2802%
2803% The format of the FxImage method is:
2804%
2805% Image *FxImage(const Image *image,const char *expression,
2806% ExceptionInfo *exception)
2807% Image *FxImageChannel(const Image *image,const ChannelType channel,
2808% const char *expression,ExceptionInfo *exception)
2809%
2810% A description of each parameter follows:
2811%
2812% o image: the image.
2813%
2814% o channel: the channel.
2815%
2816% o expression: A mathematical expression.
2817%
2818% o exception: return any errors or warnings in this structure.
2819%
2820*/
2821
2822static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2823{
cristybb503372010-05-27 20:51:26 +00002824 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002825 i;
2826
2827 assert(fx_info != (FxInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002828 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002829 if (fx_info[i] != (FxInfo *) NULL)
2830 fx_info[i]=DestroyFxInfo(fx_info[i]);
2831 fx_info=(FxInfo **) RelinquishAlignedMemory(fx_info);
2832 return(fx_info);
2833}
2834
2835static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2836 ExceptionInfo *exception)
2837{
2838 char
2839 *fx_expression;
2840
2841 FxInfo
2842 **fx_info;
2843
2844 MagickRealType
2845 alpha;
2846
cristybb503372010-05-27 20:51:26 +00002847 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002848 i;
2849
cristybb503372010-05-27 20:51:26 +00002850 size_t
cristy3ed852e2009-09-05 21:47:34 +00002851 number_threads;
2852
2853 number_threads=GetOpenMPMaximumThreads();
2854 fx_info=(FxInfo **) AcquireAlignedMemory(number_threads,sizeof(*fx_info));
2855 if (fx_info == (FxInfo **) NULL)
2856 return((FxInfo **) NULL);
2857 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2858 if (*expression != '@')
2859 fx_expression=ConstantString(expression);
2860 else
2861 fx_expression=FileToString(expression+1,~0,exception);
cristybb503372010-05-27 20:51:26 +00002862 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002863 {
2864 fx_info[i]=AcquireFxInfo(image,fx_expression);
2865 if (fx_info[i] == (FxInfo *) NULL)
2866 return(DestroyFxThreadSet(fx_info));
2867 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2868 }
2869 fx_expression=DestroyString(fx_expression);
2870 return(fx_info);
2871}
2872
2873MagickExport Image *FxImage(const Image *image,const char *expression,
2874 ExceptionInfo *exception)
2875{
2876 Image
2877 *fx_image;
2878
2879 fx_image=FxImageChannel(image,GrayChannel,expression,exception);
2880 return(fx_image);
2881}
2882
2883MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
2884 const char *expression,ExceptionInfo *exception)
2885{
2886#define FxImageTag "Fx/Image"
2887
cristyfa112112010-01-04 17:48:07 +00002888 CacheView
2889 *fx_view;
2890
cristy3ed852e2009-09-05 21:47:34 +00002891 FxInfo
cristyfa112112010-01-04 17:48:07 +00002892 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00002893
2894 Image
2895 *fx_image;
2896
cristy3ed852e2009-09-05 21:47:34 +00002897 MagickBooleanType
2898 status;
2899
cristybb503372010-05-27 20:51:26 +00002900 MagickOffsetType
2901 progress;
2902
cristy3ed852e2009-09-05 21:47:34 +00002903 MagickRealType
2904 alpha;
2905
cristybb503372010-05-27 20:51:26 +00002906 ssize_t
2907 y;
2908
cristy3ed852e2009-09-05 21:47:34 +00002909 assert(image != (Image *) NULL);
2910 assert(image->signature == MagickSignature);
2911 if (image->debug != MagickFalse)
2912 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2913 fx_image=CloneImage(image,0,0,MagickTrue,exception);
2914 if (fx_image == (Image *) NULL)
2915 return((Image *) NULL);
2916 if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
2917 {
2918 InheritException(exception,&fx_image->exception);
2919 fx_image=DestroyImage(fx_image);
2920 return((Image *) NULL);
2921 }
2922 fx_info=AcquireFxThreadSet(image,expression,exception);
2923 if (fx_info == (FxInfo **) NULL)
2924 {
2925 fx_image=DestroyImage(fx_image);
2926 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2927 }
2928 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
2929 if (status == MagickFalse)
2930 {
2931 fx_image=DestroyImage(fx_image);
2932 fx_info=DestroyFxThreadSet(fx_info);
2933 return((Image *) NULL);
2934 }
2935 /*
2936 Fx image.
2937 */
2938 status=MagickTrue;
2939 progress=0;
2940 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00002941#if defined(MAGICKCORE_OPENMP_SUPPORT)
2942 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002943#endif
cristybb503372010-05-27 20:51:26 +00002944 for (y=0; y < (ssize_t) fx_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002945 {
2946 MagickRealType
2947 alpha;
2948
2949 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002950 *restrict fx_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002951
cristybb503372010-05-27 20:51:26 +00002952 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002953 id,
2954 x;
2955
2956 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002957 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002958
2959 if (status == MagickFalse)
2960 continue;
2961 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
2962 if (q == (PixelPacket *) NULL)
2963 {
2964 status=MagickFalse;
2965 continue;
2966 }
2967 fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
2968 id=GetOpenMPThreadId();
2969 alpha=0.0;
cristybb503372010-05-27 20:51:26 +00002970 for (x=0; x < (ssize_t) fx_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002971 {
2972 if ((channel & RedChannel) != 0)
2973 {
2974 (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
2975 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00002976 q->red=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00002977 }
2978 if ((channel & GreenChannel) != 0)
2979 {
2980 (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
2981 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00002982 q->green=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00002983 }
2984 if ((channel & BlueChannel) != 0)
2985 {
2986 (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
2987 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00002988 q->blue=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00002989 }
2990 if ((channel & OpacityChannel) != 0)
2991 {
2992 (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
2993 &alpha,exception);
2994 if (image->matte == MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002995 q->opacity=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00002996 else
cristyce70c172010-01-07 17:15:30 +00002997 q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
cristy3ed852e2009-09-05 21:47:34 +00002998 QuantumRange*alpha));
2999 }
3000 if (((channel & IndexChannel) != 0) &&
3001 (fx_image->colorspace == CMYKColorspace))
3002 {
3003 (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
3004 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00003005 fx_indexes[x]=(IndexPacket) ClampToQuantum((MagickRealType)
cristy3ed852e2009-09-05 21:47:34 +00003006 QuantumRange*alpha);
3007 }
3008 q++;
3009 }
3010 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3011 status=MagickFalse;
3012 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3013 {
3014 MagickBooleanType
3015 proceed;
3016
cristyb5d5f722009-11-04 03:03:49 +00003017#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003018 #pragma omp critical (MagickCore_FxImageChannel)
3019#endif
3020 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3021 if (proceed == MagickFalse)
3022 status=MagickFalse;
3023 }
3024 }
3025 fx_image->matte=fx_info[0]->matte;
3026 fx_view=DestroyCacheView(fx_view);
3027 fx_info=DestroyFxThreadSet(fx_info);
3028 if (status == MagickFalse)
3029 fx_image=DestroyImage(fx_image);
3030 return(fx_image);
3031}
3032
3033/*
3034%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3035% %
3036% %
3037% %
3038% I m p l o d e I m a g e %
3039% %
3040% %
3041% %
3042%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3043%
3044% ImplodeImage() creates a new image that is a copy of an existing
3045% one with the image pixels "implode" by the specified percentage. It
3046% allocates the memory necessary for the new Image structure and returns a
3047% pointer to the new image.
3048%
3049% The format of the ImplodeImage method is:
3050%
3051% Image *ImplodeImage(const Image *image,const double amount,
3052% ExceptionInfo *exception)
3053%
3054% A description of each parameter follows:
3055%
3056% o implode_image: Method ImplodeImage returns a pointer to the image
3057% after it is implode. A null image is returned if there is a memory
3058% shortage.
3059%
3060% o image: the image.
3061%
3062% o amount: Define the extent of the implosion.
3063%
3064% o exception: return any errors or warnings in this structure.
3065%
3066*/
3067MagickExport Image *ImplodeImage(const Image *image,const double amount,
3068 ExceptionInfo *exception)
3069{
3070#define ImplodeImageTag "Implode/Image"
3071
cristyfa112112010-01-04 17:48:07 +00003072 CacheView
3073 *image_view,
3074 *implode_view;
3075
cristy3ed852e2009-09-05 21:47:34 +00003076 Image
3077 *implode_image;
3078
cristy3ed852e2009-09-05 21:47:34 +00003079 MagickBooleanType
3080 status;
3081
cristybb503372010-05-27 20:51:26 +00003082 MagickOffsetType
3083 progress;
3084
cristy3ed852e2009-09-05 21:47:34 +00003085 MagickPixelPacket
3086 zero;
3087
3088 MagickRealType
3089 radius;
3090
3091 PointInfo
3092 center,
3093 scale;
3094
3095 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00003096 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00003097
cristybb503372010-05-27 20:51:26 +00003098 ssize_t
3099 y;
3100
cristy3ed852e2009-09-05 21:47:34 +00003101 /*
3102 Initialize implode image attributes.
3103 */
3104 assert(image != (Image *) NULL);
3105 assert(image->signature == MagickSignature);
3106 if (image->debug != MagickFalse)
3107 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3108 assert(exception != (ExceptionInfo *) NULL);
3109 assert(exception->signature == MagickSignature);
3110 implode_image=CloneImage(image,0,0,MagickTrue,exception);
3111 if (implode_image == (Image *) NULL)
3112 return((Image *) NULL);
3113 if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
3114 {
3115 InheritException(exception,&implode_image->exception);
3116 implode_image=DestroyImage(implode_image);
3117 return((Image *) NULL);
3118 }
3119 if (implode_image->background_color.opacity != OpaqueOpacity)
3120 implode_image->matte=MagickTrue;
3121 /*
3122 Compute scaling factor.
3123 */
3124 scale.x=1.0;
3125 scale.y=1.0;
3126 center.x=0.5*image->columns;
3127 center.y=0.5*image->rows;
3128 radius=center.x;
3129 if (image->columns > image->rows)
3130 scale.y=(double) image->columns/(double) image->rows;
3131 else
3132 if (image->columns < image->rows)
3133 {
3134 scale.x=(double) image->rows/(double) image->columns;
3135 radius=center.y;
3136 }
3137 /*
3138 Implode image.
3139 */
3140 status=MagickTrue;
3141 progress=0;
3142 GetMagickPixelPacket(implode_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00003143 resample_filter=AcquireResampleFilterThreadSet(image,
3144 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003145 image_view=AcquireCacheView(image);
3146 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003147#if defined(MAGICKCORE_OPENMP_SUPPORT)
3148 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003149#endif
cristybb503372010-05-27 20:51:26 +00003150 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003151 {
3152 MagickPixelPacket
3153 pixel;
3154
3155 MagickRealType
3156 distance;
3157
3158 PointInfo
3159 delta;
3160
3161 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003162 *restrict implode_indexes;
cristy3ed852e2009-09-05 21:47:34 +00003163
cristybb503372010-05-27 20:51:26 +00003164 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003165 id,
3166 x;
3167
3168 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003169 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003170
3171 if (status == MagickFalse)
3172 continue;
3173 q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3174 exception);
3175 if (q == (PixelPacket *) NULL)
3176 {
3177 status=MagickFalse;
3178 continue;
3179 }
3180 implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
3181 delta.y=scale.y*(double) (y-center.y);
3182 pixel=zero;
3183 id=GetOpenMPThreadId();
cristybb503372010-05-27 20:51:26 +00003184 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003185 {
3186 /*
3187 Determine if the pixel is within an ellipse.
3188 */
3189 delta.x=scale.x*(double) (x-center.x);
3190 distance=delta.x*delta.x+delta.y*delta.y;
3191 if (distance < (radius*radius))
3192 {
3193 double
3194 factor;
3195
3196 /*
3197 Implode the pixel.
3198 */
3199 factor=1.0;
3200 if (distance > 0.0)
3201 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3202 radius/2)),-amount);
3203 (void) ResamplePixelColor(resample_filter[id],(double)
3204 (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3205 scale.y+center.y),&pixel);
3206 SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
3207 }
3208 q++;
3209 }
3210 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3211 status=MagickFalse;
3212 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3213 {
3214 MagickBooleanType
3215 proceed;
3216
cristyb5d5f722009-11-04 03:03:49 +00003217#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003218 #pragma omp critical (MagickCore_ImplodeImage)
3219#endif
3220 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3221 if (proceed == MagickFalse)
3222 status=MagickFalse;
3223 }
3224 }
3225 implode_view=DestroyCacheView(implode_view);
3226 image_view=DestroyCacheView(image_view);
3227 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
3228 if (status == MagickFalse)
3229 implode_image=DestroyImage(implode_image);
3230 return(implode_image);
3231}
3232
3233/*
3234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3235% %
3236% %
3237% %
3238% M o r p h I m a g e s %
3239% %
3240% %
3241% %
3242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3243%
3244% The MorphImages() method requires a minimum of two images. The first
3245% image is transformed into the second by a number of intervening images
3246% as specified by frames.
3247%
3248% The format of the MorphImage method is:
3249%
cristybb503372010-05-27 20:51:26 +00003250% Image *MorphImages(const Image *image,const size_t number_frames,
cristy3ed852e2009-09-05 21:47:34 +00003251% ExceptionInfo *exception)
3252%
3253% A description of each parameter follows:
3254%
3255% o image: the image.
3256%
3257% o number_frames: Define the number of in-between image to generate.
3258% The more in-between frames, the smoother the morph.
3259%
3260% o exception: return any errors or warnings in this structure.
3261%
3262*/
3263MagickExport Image *MorphImages(const Image *image,
cristybb503372010-05-27 20:51:26 +00003264 const size_t number_frames,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003265{
3266#define MorphImageTag "Morph/Image"
3267
3268 Image
3269 *morph_image,
3270 *morph_images;
3271
cristybb503372010-05-27 20:51:26 +00003272 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003273 y;
3274
3275 MagickOffsetType
3276 scene;
3277
3278 MagickRealType
3279 alpha,
3280 beta;
3281
3282 register const Image
3283 *next;
3284
cristybb503372010-05-27 20:51:26 +00003285 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003286 i;
3287
3288 MagickBooleanType
3289 status;
3290
3291 /*
3292 Clone first frame in sequence.
3293 */
3294 assert(image != (Image *) NULL);
3295 assert(image->signature == MagickSignature);
3296 if (image->debug != MagickFalse)
3297 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3298 assert(exception != (ExceptionInfo *) NULL);
3299 assert(exception->signature == MagickSignature);
3300 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3301 if (morph_images == (Image *) NULL)
3302 return((Image *) NULL);
3303 if (GetNextImageInList(image) == (Image *) NULL)
3304 {
3305 /*
3306 Morph single image.
3307 */
cristybb503372010-05-27 20:51:26 +00003308 for (i=1; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003309 {
3310 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3311 if (morph_image == (Image *) NULL)
3312 {
3313 morph_images=DestroyImageList(morph_images);
3314 return((Image *) NULL);
3315 }
3316 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003317 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003318 {
cristy8b27a6d2010-02-14 03:31:15 +00003319 MagickBooleanType
3320 proceed;
3321
cristybb503372010-05-27 20:51:26 +00003322 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3323 number_frames);
cristy8b27a6d2010-02-14 03:31:15 +00003324 if (proceed == MagickFalse)
3325 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003326 }
3327 }
3328 return(GetFirstImageInList(morph_images));
3329 }
3330 /*
3331 Morph image sequence.
3332 */
3333 status=MagickTrue;
3334 scene=0;
3335 next=image;
3336 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3337 {
cristybb503372010-05-27 20:51:26 +00003338 for (i=0; i < (ssize_t) number_frames; i++)
cristy3ed852e2009-09-05 21:47:34 +00003339 {
3340 CacheView
3341 *image_view,
3342 *morph_view;
3343
3344 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3345 alpha=1.0-beta;
cristybb503372010-05-27 20:51:26 +00003346 morph_image=ZoomImage(next,(size_t) (alpha*next->columns+beta*
3347 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
cristy3ed852e2009-09-05 21:47:34 +00003348 next->rows+beta*GetNextImageInList(next)->rows+0.5),exception);
3349 if (morph_image == (Image *) NULL)
3350 {
3351 morph_images=DestroyImageList(morph_images);
3352 return((Image *) NULL);
3353 }
3354 if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
3355 {
3356 InheritException(exception,&morph_image->exception);
3357 morph_image=DestroyImage(morph_image);
3358 return((Image *) NULL);
3359 }
3360 AppendImageToList(&morph_images,morph_image);
3361 morph_images=GetLastImageInList(morph_images);
3362 morph_image=ZoomImage(GetNextImageInList(next),morph_images->columns,
3363 morph_images->rows,exception);
3364 if (morph_image == (Image *) NULL)
3365 {
3366 morph_images=DestroyImageList(morph_images);
3367 return((Image *) NULL);
3368 }
3369 image_view=AcquireCacheView(morph_image);
3370 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003371#if defined(MAGICKCORE_OPENMP_SUPPORT)
3372 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003373#endif
cristybb503372010-05-27 20:51:26 +00003374 for (y=0; y < (ssize_t) morph_images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003375 {
3376 MagickBooleanType
3377 sync;
3378
3379 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003380 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003381
cristybb503372010-05-27 20:51:26 +00003382 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003383 x;
3384
3385 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003386 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003387
3388 if (status == MagickFalse)
3389 continue;
3390 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3391 exception);
3392 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3393 exception);
3394 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3395 {
3396 status=MagickFalse;
3397 continue;
3398 }
cristybb503372010-05-27 20:51:26 +00003399 for (x=0; x < (ssize_t) morph_images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003400 {
cristyce70c172010-01-07 17:15:30 +00003401 q->red=ClampToQuantum(alpha*q->red+beta*GetRedPixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00003402 q->green=ClampToQuantum(alpha*q->green+beta*
3403 GetGreenPixelComponent(p));
cristyce70c172010-01-07 17:15:30 +00003404 q->blue=ClampToQuantum(alpha*q->blue+beta*GetBluePixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00003405 q->opacity=ClampToQuantum(alpha*q->opacity+beta*
3406 GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00003407 p++;
3408 q++;
3409 }
3410 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3411 if (sync == MagickFalse)
3412 status=MagickFalse;
3413 }
3414 morph_view=DestroyCacheView(morph_view);
3415 image_view=DestroyCacheView(image_view);
3416 morph_image=DestroyImage(morph_image);
3417 }
cristybb503372010-05-27 20:51:26 +00003418 if (i < (ssize_t) number_frames)
cristy3ed852e2009-09-05 21:47:34 +00003419 break;
3420 /*
3421 Clone last frame in sequence.
3422 */
3423 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3424 if (morph_image == (Image *) NULL)
3425 {
3426 morph_images=DestroyImageList(morph_images);
3427 return((Image *) NULL);
3428 }
3429 AppendImageToList(&morph_images,morph_image);
3430 morph_images=GetLastImageInList(morph_images);
3431 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3432 {
3433 MagickBooleanType
3434 proceed;
3435
cristyb5d5f722009-11-04 03:03:49 +00003436#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003437 #pragma omp critical (MagickCore_MorphImages)
3438#endif
3439 proceed=SetImageProgress(image,MorphImageTag,scene,
3440 GetImageListLength(image));
3441 if (proceed == MagickFalse)
3442 status=MagickFalse;
3443 }
3444 scene++;
3445 }
3446 if (GetNextImageInList(next) != (Image *) NULL)
3447 {
3448 morph_images=DestroyImageList(morph_images);
3449 return((Image *) NULL);
3450 }
3451 return(GetFirstImageInList(morph_images));
3452}
3453
3454/*
3455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3456% %
3457% %
3458% %
3459% P l a s m a I m a g e %
3460% %
3461% %
3462% %
3463%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3464%
3465% PlasmaImage() initializes an image with plasma fractal values. The image
3466% must be initialized with a base color and the random number generator
3467% seeded before this method is called.
3468%
3469% The format of the PlasmaImage method is:
3470%
3471% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
cristybb503372010-05-27 20:51:26 +00003472% size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003473%
3474% A description of each parameter follows:
3475%
3476% o image: the image.
3477%
3478% o segment: Define the region to apply plasma fractals values.
3479%
3480% o attenuate: Define the plasmattenuation factor.
3481%
3482% o depth: Limit the plasma recursion depth.
3483%
3484*/
3485
3486static inline Quantum PlasmaPixel(RandomInfo *random_info,
3487 const MagickRealType pixel,const MagickRealType noise)
3488{
3489 Quantum
3490 plasma;
3491
cristyce70c172010-01-07 17:15:30 +00003492 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003493 noise/2.0);
3494 return(plasma);
3495}
3496
3497MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
cristybb503372010-05-27 20:51:26 +00003498 RandomInfo *random_info,const SegmentInfo *segment,size_t attenuate,
3499 size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003500{
3501 ExceptionInfo
3502 *exception;
3503
cristybb503372010-05-27 20:51:26 +00003504 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003505 x,
3506 x_mid,
3507 y,
3508 y_mid;
3509
3510 MagickRealType
3511 plasma;
3512
3513 PixelPacket
3514 u,
3515 v;
3516
3517 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3518 return(MagickTrue);
3519 if (depth != 0)
3520 {
3521 SegmentInfo
3522 local_info;
3523
3524 /*
3525 Divide the area into quadrants and recurse.
3526 */
3527 depth--;
3528 attenuate++;
cristybb503372010-05-27 20:51:26 +00003529 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3530 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003531 local_info=(*segment);
3532 local_info.x2=(double) x_mid;
3533 local_info.y2=(double) y_mid;
3534 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3535 local_info=(*segment);
3536 local_info.y1=(double) y_mid;
3537 local_info.x2=(double) x_mid;
3538 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3539 local_info=(*segment);
3540 local_info.x1=(double) x_mid;
3541 local_info.y2=(double) y_mid;
3542 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3543 local_info=(*segment);
3544 local_info.x1=(double) x_mid;
3545 local_info.y1=(double) y_mid;
3546 return(PlasmaImageProxy(image,random_info,&local_info,attenuate,depth));
3547 }
3548 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
3549 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00003550 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3551 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003552 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3553 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3554 return(MagickFalse);
3555 /*
3556 Average pixels and apply plasma.
3557 */
3558 exception=(&image->exception);
3559 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3560 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3561 {
3562 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003563 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003564
3565 /*
3566 Left pixel.
3567 */
cristybb503372010-05-27 20:51:26 +00003568 x=(ssize_t) ceil(segment->x1-0.5);
3569 (void) GetOneVirtualPixel(image,x,(ssize_t) ceil(segment->y1-0.5),&u,
cristy06609ee2010-03-17 20:21:27 +00003570 exception);
cristybb503372010-05-27 20:51:26 +00003571 (void) GetOneVirtualPixel(image,x,(ssize_t) ceil(segment->y2-0.5),&v,
cristy06609ee2010-03-17 20:21:27 +00003572 exception);
cristy3ed852e2009-09-05 21:47:34 +00003573 q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
3574 if (q == (PixelPacket *) NULL)
3575 return(MagickTrue);
3576 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3577 plasma);
3578 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3579 plasma);
3580 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3581 plasma);
3582 (void) SyncAuthenticPixels(image,exception);
3583 if (segment->x1 != segment->x2)
3584 {
3585 /*
3586 Right pixel.
3587 */
cristybb503372010-05-27 20:51:26 +00003588 x=(ssize_t) ceil(segment->x2-0.5);
3589 (void) GetOneVirtualPixel(image,x,(ssize_t) ceil(segment->y1-0.5),&u,
cristy3ed852e2009-09-05 21:47:34 +00003590 exception);
cristybb503372010-05-27 20:51:26 +00003591 (void) GetOneVirtualPixel(image,x,(ssize_t) ceil(segment->y2-0.5),&v,
cristy3ed852e2009-09-05 21:47:34 +00003592 exception);
3593 q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
3594 if (q == (PixelPacket *) NULL)
3595 return(MagickTrue);
3596 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3597 plasma);
3598 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3599 2.0,plasma);
3600 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3601 plasma);
3602 (void) SyncAuthenticPixels(image,exception);
3603 }
3604 }
3605 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3606 {
3607 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3608 {
3609 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003610 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003611
3612 /*
3613 Bottom pixel.
3614 */
cristybb503372010-05-27 20:51:26 +00003615 y=(ssize_t) ceil(segment->y2-0.5);
3616 (void) GetOneVirtualPixel(image,(ssize_t) ceil(segment->x1-0.5),y,&u,
cristy3ed852e2009-09-05 21:47:34 +00003617 exception);
cristybb503372010-05-27 20:51:26 +00003618 (void) GetOneVirtualPixel(image,(ssize_t) ceil(segment->x2-0.5),y,&v,
cristy3ed852e2009-09-05 21:47:34 +00003619 exception);
3620 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3621 if (q == (PixelPacket *) NULL)
3622 return(MagickTrue);
3623 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3624 plasma);
3625 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3626 2.0,plasma);
3627 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3628 plasma);
3629 (void) SyncAuthenticPixels(image,exception);
3630 }
3631 if (segment->y1 != segment->y2)
3632 {
3633 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003634 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003635
3636 /*
3637 Top pixel.
3638 */
cristybb503372010-05-27 20:51:26 +00003639 y=(ssize_t) ceil(segment->y1-0.5);
3640 (void) GetOneVirtualPixel(image,(ssize_t) ceil(segment->x1-0.5),y,&u,
cristy3ed852e2009-09-05 21:47:34 +00003641 exception);
cristybb503372010-05-27 20:51:26 +00003642 (void) GetOneVirtualPixel(image,(ssize_t) ceil(segment->x2-0.5),y,&v,
cristy3ed852e2009-09-05 21:47:34 +00003643 exception);
3644 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3645 if (q == (PixelPacket *) NULL)
3646 return(MagickTrue);
3647 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3648 plasma);
3649 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3650 2.0,plasma);
3651 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3652 plasma);
3653 (void) SyncAuthenticPixels(image,exception);
3654 }
3655 }
3656 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3657 {
3658 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003659 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003660
3661 /*
3662 Middle pixel.
3663 */
cristybb503372010-05-27 20:51:26 +00003664 x=(ssize_t) ceil(segment->x1-0.5);
3665 y=(ssize_t) ceil(segment->y1-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003666 (void) GetOneVirtualPixel(image,x,y,&u,exception);
cristybb503372010-05-27 20:51:26 +00003667 x=(ssize_t) ceil(segment->x2-0.5);
3668 y=(ssize_t) ceil(segment->y2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003669 (void) GetOneVirtualPixel(image,x,y,&v,exception);
3670 q=QueueAuthenticPixels(image,x_mid,y_mid,1,1,exception);
3671 if (q == (PixelPacket *) NULL)
3672 return(MagickTrue);
3673 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3674 plasma);
3675 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3676 plasma);
3677 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3678 plasma);
3679 (void) SyncAuthenticPixels(image,exception);
3680 }
3681 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3682 return(MagickTrue);
3683 return(MagickFalse);
3684}
3685
3686MagickExport MagickBooleanType PlasmaImage(Image *image,
cristybb503372010-05-27 20:51:26 +00003687 const SegmentInfo *segment,size_t attenuate,size_t depth)
cristy3ed852e2009-09-05 21:47:34 +00003688{
3689 MagickBooleanType
3690 status;
3691
3692 RandomInfo
3693 *random_info;
3694
3695 if (image->debug != MagickFalse)
3696 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3697 assert(image != (Image *) NULL);
3698 assert(image->signature == MagickSignature);
3699 if (image->debug != MagickFalse)
3700 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3701 random_info=AcquireRandomInfo();
3702 status=PlasmaImageProxy(image,random_info,segment,attenuate,depth);
3703 random_info=DestroyRandomInfo(random_info);
3704 return(status);
3705}
3706
3707/*
3708%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3709% %
3710% %
3711% %
3712% P o l a r o i d I m a g e %
3713% %
3714% %
3715% %
3716%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3717%
3718% PolaroidImage() simulates a Polaroid picture.
3719%
3720% The format of the AnnotateImage method is:
3721%
3722% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3723% const double angle,ExceptionInfo exception)
3724%
3725% A description of each parameter follows:
3726%
3727% o image: the image.
3728%
3729% o draw_info: the draw info.
3730%
cristycee97112010-05-28 00:44:52 +00003731% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00003732%
3733% o exception: return any errors or warnings in this structure.
3734%
3735*/
3736MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3737 const double angle,ExceptionInfo *exception)
3738{
3739 const char
3740 *value;
3741
cristybb503372010-05-27 20:51:26 +00003742 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003743 quantum;
3744
3745 Image
3746 *bend_image,
3747 *caption_image,
3748 *flop_image,
3749 *picture_image,
3750 *polaroid_image,
3751 *rotate_image,
3752 *trim_image;
3753
cristybb503372010-05-27 20:51:26 +00003754 size_t
cristy3ed852e2009-09-05 21:47:34 +00003755 height;
3756
3757 /*
3758 Simulate a Polaroid picture.
3759 */
3760 assert(image != (Image *) NULL);
3761 assert(image->signature == MagickSignature);
3762 if (image->debug != MagickFalse)
3763 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3764 assert(exception != (ExceptionInfo *) NULL);
3765 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00003766 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
cristy3ed852e2009-09-05 21:47:34 +00003767 image->rows)/25.0,10.0);
3768 height=image->rows+2*quantum;
3769 caption_image=(Image *) NULL;
3770 value=GetImageProperty(image,"Caption");
3771 if (value != (const char *) NULL)
3772 {
3773 char
3774 *caption,
3775 geometry[MaxTextExtent];
3776
3777 DrawInfo
3778 *annotate_info;
3779
cristybb503372010-05-27 20:51:26 +00003780 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003781 count;
3782
3783 MagickBooleanType
3784 status;
3785
3786 TypeMetric
3787 metrics;
3788
3789 /*
3790 Generate caption image.
3791 */
3792 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3793 if (caption_image == (Image *) NULL)
3794 return((Image *) NULL);
3795 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
3796 caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
3797 value);
3798 (void) CloneString(&annotate_info->text,caption);
3799 count=FormatMagickCaption(caption_image,annotate_info,&metrics,&caption);
cristybb503372010-05-27 20:51:26 +00003800 status=SetImageExtent(caption_image,image->columns,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00003801 ((count+1)*(metrics.ascent-metrics.descent)+0.5));
3802 if (status == MagickFalse)
3803 caption_image=DestroyImage(caption_image);
3804 else
3805 {
3806 caption_image->background_color=image->border_color;
3807 (void) SetImageBackgroundColor(caption_image);
3808 (void) CloneString(&annotate_info->text,caption);
cristye7f51092010-01-17 00:39:37 +00003809 (void) FormatMagickString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00003810 metrics.ascent);
3811 if (annotate_info->gravity == UndefinedGravity)
3812 (void) CloneString(&annotate_info->geometry,AcquireString(
3813 geometry));
3814 (void) AnnotateImage(caption_image,annotate_info);
3815 height+=caption_image->rows;
3816 }
3817 annotate_info=DestroyDrawInfo(annotate_info);
3818 caption=DestroyString(caption);
3819 }
3820 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3821 exception);
3822 if (picture_image == (Image *) NULL)
3823 {
3824 if (caption_image != (Image *) NULL)
3825 caption_image=DestroyImage(caption_image);
3826 return((Image *) NULL);
3827 }
3828 picture_image->background_color=image->border_color;
3829 (void) SetImageBackgroundColor(picture_image);
3830 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
3831 if (caption_image != (Image *) NULL)
3832 {
3833 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
cristybb503372010-05-27 20:51:26 +00003834 quantum,(ssize_t) (image->rows+3*quantum/2));
cristy3ed852e2009-09-05 21:47:34 +00003835 caption_image=DestroyImage(caption_image);
3836 }
3837 (void) QueryColorDatabase("none",&picture_image->background_color,exception);
3838 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
3839 rotate_image=RotateImage(picture_image,90.0,exception);
3840 picture_image=DestroyImage(picture_image);
3841 if (rotate_image == (Image *) NULL)
3842 return((Image *) NULL);
3843 picture_image=rotate_image;
3844 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
3845 picture_image->columns,exception);
3846 picture_image=DestroyImage(picture_image);
3847 if (bend_image == (Image *) NULL)
3848 return((Image *) NULL);
3849 InheritException(&bend_image->exception,exception);
3850 picture_image=bend_image;
3851 rotate_image=RotateImage(picture_image,-90.0,exception);
3852 picture_image=DestroyImage(picture_image);
3853 if (rotate_image == (Image *) NULL)
3854 return((Image *) NULL);
3855 picture_image=rotate_image;
3856 picture_image->background_color=image->background_color;
3857 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
3858 exception);
3859 if (polaroid_image == (Image *) NULL)
3860 {
3861 picture_image=DestroyImage(picture_image);
3862 return(picture_image);
3863 }
3864 flop_image=FlopImage(polaroid_image,exception);
3865 polaroid_image=DestroyImage(polaroid_image);
3866 if (flop_image == (Image *) NULL)
3867 {
3868 picture_image=DestroyImage(picture_image);
3869 return(picture_image);
3870 }
3871 polaroid_image=flop_image;
3872 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
cristybb503372010-05-27 20:51:26 +00003873 (ssize_t) (-0.01*picture_image->columns/2.0),0L);
cristy3ed852e2009-09-05 21:47:34 +00003874 picture_image=DestroyImage(picture_image);
3875 (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
3876 rotate_image=RotateImage(polaroid_image,angle,exception);
3877 polaroid_image=DestroyImage(polaroid_image);
3878 if (rotate_image == (Image *) NULL)
3879 return((Image *) NULL);
3880 polaroid_image=rotate_image;
3881 trim_image=TrimImage(polaroid_image,exception);
3882 polaroid_image=DestroyImage(polaroid_image);
3883 if (trim_image == (Image *) NULL)
3884 return((Image *) NULL);
3885 polaroid_image=trim_image;
3886 return(polaroid_image);
3887}
3888
3889/*
3890%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3891% %
3892% %
3893% %
cristy3ed852e2009-09-05 21:47:34 +00003894% S e p i a T o n e I m a g e %
3895% %
3896% %
3897% %
3898%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3899%
3900% MagickSepiaToneImage() applies a special effect to the image, similar to the
3901% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
3902% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
3903% threshold of 80% is a good starting point for a reasonable tone.
3904%
3905% The format of the SepiaToneImage method is:
3906%
3907% Image *SepiaToneImage(const Image *image,const double threshold,
3908% ExceptionInfo *exception)
3909%
3910% A description of each parameter follows:
3911%
3912% o image: the image.
3913%
3914% o threshold: the tone threshold.
3915%
3916% o exception: return any errors or warnings in this structure.
3917%
3918*/
3919MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
3920 ExceptionInfo *exception)
3921{
3922#define SepiaToneImageTag "SepiaTone/Image"
3923
cristyc4c8d132010-01-07 01:58:38 +00003924 CacheView
3925 *image_view,
3926 *sepia_view;
3927
cristy3ed852e2009-09-05 21:47:34 +00003928 Image
3929 *sepia_image;
3930
cristy3ed852e2009-09-05 21:47:34 +00003931 MagickBooleanType
3932 status;
3933
cristybb503372010-05-27 20:51:26 +00003934 MagickOffsetType
3935 progress;
3936
3937 ssize_t
3938 y;
3939
cristy3ed852e2009-09-05 21:47:34 +00003940 /*
3941 Initialize sepia-toned image attributes.
3942 */
3943 assert(image != (const Image *) NULL);
3944 assert(image->signature == MagickSignature);
3945 if (image->debug != MagickFalse)
3946 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3947 assert(exception != (ExceptionInfo *) NULL);
3948 assert(exception->signature == MagickSignature);
3949 sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3950 if (sepia_image == (Image *) NULL)
3951 return((Image *) NULL);
3952 if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
3953 {
3954 InheritException(exception,&sepia_image->exception);
3955 sepia_image=DestroyImage(sepia_image);
3956 return((Image *) NULL);
3957 }
3958 /*
3959 Tone each row of the image.
3960 */
3961 status=MagickTrue;
3962 progress=0;
3963 image_view=AcquireCacheView(image);
3964 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00003965#if defined(MAGICKCORE_OPENMP_SUPPORT)
3966 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003967#endif
cristybb503372010-05-27 20:51:26 +00003968 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003969 {
3970 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003971 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003972
cristybb503372010-05-27 20:51:26 +00003973 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003974 x;
3975
3976 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003977 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003978
3979 if (status == MagickFalse)
3980 continue;
3981 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3982 q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
3983 exception);
3984 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3985 {
3986 status=MagickFalse;
3987 continue;
3988 }
cristybb503372010-05-27 20:51:26 +00003989 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003990 {
3991 MagickRealType
3992 intensity,
3993 tone;
3994
3995 intensity=(MagickRealType) PixelIntensityToQuantum(p);
3996 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
3997 (MagickRealType) QuantumRange-threshold;
cristyce70c172010-01-07 17:15:30 +00003998 q->red=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00003999 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4000 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristyce70c172010-01-07 17:15:30 +00004001 q->green=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004002 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristyce70c172010-01-07 17:15:30 +00004003 q->blue=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004004 tone=threshold/7.0;
4005 if ((MagickRealType) q->green < tone)
cristyce70c172010-01-07 17:15:30 +00004006 q->green=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004007 if ((MagickRealType) q->blue < tone)
cristyce70c172010-01-07 17:15:30 +00004008 q->blue=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004009 p++;
4010 q++;
4011 }
4012 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4013 status=MagickFalse;
4014 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4015 {
4016 MagickBooleanType
4017 proceed;
4018
cristyb5d5f722009-11-04 03:03:49 +00004019#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004020 #pragma omp critical (MagickCore_SepiaToneImage)
4021#endif
4022 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4023 image->rows);
4024 if (proceed == MagickFalse)
4025 status=MagickFalse;
4026 }
4027 }
4028 sepia_view=DestroyCacheView(sepia_view);
4029 image_view=DestroyCacheView(image_view);
4030 (void) NormalizeImage(sepia_image);
4031 (void) ContrastImage(sepia_image,MagickTrue);
4032 if (status == MagickFalse)
4033 sepia_image=DestroyImage(sepia_image);
4034 return(sepia_image);
4035}
4036
4037/*
4038%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4039% %
4040% %
4041% %
4042% S h a d o w I m a g e %
4043% %
4044% %
4045% %
4046%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4047%
4048% ShadowImage() simulates a shadow from the specified image and returns it.
4049%
4050% The format of the ShadowImage method is:
4051%
4052% Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004053% const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004054% ExceptionInfo *exception)
4055%
4056% A description of each parameter follows:
4057%
4058% o image: the image.
4059%
4060% o opacity: percentage transparency.
4061%
4062% o sigma: the standard deviation of the Gaussian, in pixels.
4063%
4064% o x_offset: the shadow x-offset.
4065%
4066% o y_offset: the shadow y-offset.
4067%
4068% o exception: return any errors or warnings in this structure.
4069%
4070*/
4071MagickExport Image *ShadowImage(const Image *image,const double opacity,
cristybb503372010-05-27 20:51:26 +00004072 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004073 ExceptionInfo *exception)
4074{
4075#define ShadowImageTag "Shadow/Image"
4076
cristyc4c8d132010-01-07 01:58:38 +00004077 CacheView
4078 *image_view;
4079
cristy3ed852e2009-09-05 21:47:34 +00004080 Image
4081 *border_image,
4082 *clone_image,
4083 *shadow_image;
4084
cristy3ed852e2009-09-05 21:47:34 +00004085 MagickBooleanType
4086 status;
4087
cristybb503372010-05-27 20:51:26 +00004088 MagickOffsetType
4089 progress;
4090
cristy3ed852e2009-09-05 21:47:34 +00004091 RectangleInfo
4092 border_info;
4093
cristybb503372010-05-27 20:51:26 +00004094 ssize_t
4095 y;
4096
cristy3ed852e2009-09-05 21:47:34 +00004097 assert(image != (Image *) NULL);
4098 assert(image->signature == MagickSignature);
4099 if (image->debug != MagickFalse)
4100 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4101 assert(exception != (ExceptionInfo *) NULL);
4102 assert(exception->signature == MagickSignature);
4103 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4104 if (clone_image == (Image *) NULL)
4105 return((Image *) NULL);
4106 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4107 clone_image->compose=OverCompositeOp;
cristybb503372010-05-27 20:51:26 +00004108 border_info.width=(size_t) floor(2.0*sigma+0.5);
4109 border_info.height=(size_t) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004110 border_info.x=0;
4111 border_info.y=0;
4112 (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4113 border_image=BorderImage(clone_image,&border_info,exception);
4114 clone_image=DestroyImage(clone_image);
4115 if (border_image == (Image *) NULL)
4116 return((Image *) NULL);
4117 if (border_image->matte == MagickFalse)
4118 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4119 /*
4120 Shadow image.
4121 */
4122 status=MagickTrue;
4123 progress=0;
4124 image_view=AcquireCacheView(border_image);
cristyb5d5f722009-11-04 03:03:49 +00004125#if defined(MAGICKCORE_OPENMP_SUPPORT)
4126 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004127#endif
cristybb503372010-05-27 20:51:26 +00004128 for (y=0; y < (ssize_t) border_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004129 {
cristybb503372010-05-27 20:51:26 +00004130 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004131 x;
4132
4133 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004134 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004135
4136 if (status == MagickFalse)
4137 continue;
4138 q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4139 exception);
4140 if (q == (PixelPacket *) NULL)
4141 {
4142 status=MagickFalse;
4143 continue;
4144 }
cristybb503372010-05-27 20:51:26 +00004145 for (x=0; x < (ssize_t) border_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004146 {
4147 q->red=border_image->background_color.red;
4148 q->green=border_image->background_color.green;
4149 q->blue=border_image->background_color.blue;
4150 if (border_image->matte == MagickFalse)
4151 q->opacity=border_image->background_color.opacity;
4152 else
cristy46f08202010-01-10 04:04:21 +00004153 q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
4154 GetAlphaPixelComponent(q)*opacity/100.0));
cristy3ed852e2009-09-05 21:47:34 +00004155 q++;
4156 }
4157 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4158 status=MagickFalse;
4159 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4160 {
4161 MagickBooleanType
4162 proceed;
4163
cristyb5d5f722009-11-04 03:03:49 +00004164#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004165 #pragma omp critical (MagickCore_ShadowImage)
4166#endif
4167 proceed=SetImageProgress(image,ShadowImageTag,progress++,
4168 border_image->rows);
4169 if (proceed == MagickFalse)
4170 status=MagickFalse;
4171 }
4172 }
4173 image_view=DestroyCacheView(image_view);
4174 shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
4175 border_image=DestroyImage(border_image);
4176 if (shadow_image == (Image *) NULL)
4177 return((Image *) NULL);
4178 if (shadow_image->page.width == 0)
4179 shadow_image->page.width=shadow_image->columns;
4180 if (shadow_image->page.height == 0)
4181 shadow_image->page.height=shadow_image->rows;
cristybb503372010-05-27 20:51:26 +00004182 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4183 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4184 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4185 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
cristy3ed852e2009-09-05 21:47:34 +00004186 return(shadow_image);
4187}
4188
4189/*
4190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4191% %
4192% %
4193% %
4194% S k e t c h I m a g e %
4195% %
4196% %
4197% %
4198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4199%
4200% SketchImage() simulates a pencil sketch. We convolve the image with a
4201% Gaussian operator of the given radius and standard deviation (sigma). For
4202% reasonable results, radius should be larger than sigma. Use a radius of 0
4203% and SketchImage() selects a suitable radius for you. Angle gives the angle
4204% of the sketch.
4205%
4206% The format of the SketchImage method is:
4207%
4208% Image *SketchImage(const Image *image,const double radius,
4209% const double sigma,const double angle,ExceptionInfo *exception)
4210%
4211% A description of each parameter follows:
4212%
4213% o image: the image.
4214%
4215% o radius: the radius of the Gaussian, in pixels, not counting
4216% the center pixel.
4217%
4218% o sigma: the standard deviation of the Gaussian, in pixels.
4219%
cristycee97112010-05-28 00:44:52 +00004220% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00004221%
4222% o exception: return any errors or warnings in this structure.
4223%
4224*/
4225MagickExport Image *SketchImage(const Image *image,const double radius,
4226 const double sigma,const double angle,ExceptionInfo *exception)
4227{
cristyfa112112010-01-04 17:48:07 +00004228 CacheView
4229 *random_view;
4230
cristy3ed852e2009-09-05 21:47:34 +00004231 Image
4232 *blend_image,
4233 *blur_image,
4234 *dodge_image,
4235 *random_image,
4236 *sketch_image;
4237
cristybb503372010-05-27 20:51:26 +00004238 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004239 y;
4240
4241 MagickBooleanType
4242 status;
4243
4244 MagickPixelPacket
4245 zero;
4246
4247 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004248 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004249
4250 /*
4251 Sketch image.
4252 */
4253 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4254 MagickTrue,exception);
4255 if (random_image == (Image *) NULL)
4256 return((Image *) NULL);
4257 status=MagickTrue;
4258 GetMagickPixelPacket(random_image,&zero);
cristy1b784432009-12-19 02:20:40 +00004259 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004260 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004261#if defined(MAGICKCORE_OPENMP_SUPPORT)
4262 #pragma omp parallel for schedule(dynamic,4) shared(status)
4263#endif
cristybb503372010-05-27 20:51:26 +00004264 for (y=0; y < (ssize_t) random_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004265 {
4266 MagickPixelPacket
4267 pixel;
4268
4269 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004270 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00004271
cristybb503372010-05-27 20:51:26 +00004272 register ssize_t
cristy1b784432009-12-19 02:20:40 +00004273 id,
cristy3ed852e2009-09-05 21:47:34 +00004274 x;
4275
4276 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004277 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004278
cristy1b784432009-12-19 02:20:40 +00004279 if (status == MagickFalse)
4280 continue;
cristy3ed852e2009-09-05 21:47:34 +00004281 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4282 exception);
4283 if (q == (PixelPacket *) NULL)
4284 {
4285 status=MagickFalse;
4286 continue;
4287 }
4288 indexes=GetCacheViewAuthenticIndexQueue(random_view);
4289 pixel=zero;
cristy1b784432009-12-19 02:20:40 +00004290 id=GetOpenMPThreadId();
cristybb503372010-05-27 20:51:26 +00004291 for (x=0; x < (ssize_t) random_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004292 {
4293 pixel.red=(MagickRealType) (QuantumRange*
cristy1b784432009-12-19 02:20:40 +00004294 GetPseudoRandomValue(random_info[id]));
cristy3ed852e2009-09-05 21:47:34 +00004295 pixel.green=pixel.red;
4296 pixel.blue=pixel.red;
4297 if (image->colorspace == CMYKColorspace)
4298 pixel.index=pixel.red;
4299 SetPixelPacket(random_image,&pixel,q,indexes+x);
4300 q++;
4301 }
4302 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4303 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004304 }
4305 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004306 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004307 if (status == MagickFalse)
4308 {
4309 random_image=DestroyImage(random_image);
4310 return(random_image);
4311 }
4312 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4313 random_image=DestroyImage(random_image);
4314 if (blur_image == (Image *) NULL)
4315 return((Image *) NULL);
4316 dodge_image=EdgeImage(blur_image,radius,exception);
4317 blur_image=DestroyImage(blur_image);
4318 if (dodge_image == (Image *) NULL)
4319 return((Image *) NULL);
4320 (void) NormalizeImage(dodge_image);
4321 (void) NegateImage(dodge_image,MagickFalse);
4322 (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4323 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4324 if (sketch_image == (Image *) NULL)
4325 {
4326 dodge_image=DestroyImage(dodge_image);
4327 return((Image *) NULL);
4328 }
4329 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4330 dodge_image=DestroyImage(dodge_image);
4331 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4332 if (blend_image == (Image *) NULL)
4333 {
4334 sketch_image=DestroyImage(sketch_image);
4335 return((Image *) NULL);
4336 }
4337 (void) SetImageArtifact(blend_image,"compose:args","20x80");
4338 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4339 blend_image=DestroyImage(blend_image);
4340 return(sketch_image);
4341}
4342
4343/*
4344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4345% %
4346% %
4347% %
4348% S o l a r i z e I m a g e %
4349% %
4350% %
4351% %
4352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4353%
4354% SolarizeImage() applies a special effect to the image, similar to the effect
4355% achieved in a photo darkroom by selectively exposing areas of photo
4356% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4357% measure of the extent of the solarization.
4358%
4359% The format of the SolarizeImage method is:
4360%
4361% MagickBooleanType SolarizeImage(Image *image,const double threshold)
4362%
4363% A description of each parameter follows:
4364%
4365% o image: the image.
4366%
4367% o threshold: Define the extent of the solarization.
4368%
4369*/
4370MagickExport MagickBooleanType SolarizeImage(Image *image,
4371 const double threshold)
4372{
4373#define SolarizeImageTag "Solarize/Image"
4374
cristyc4c8d132010-01-07 01:58:38 +00004375 CacheView
4376 *image_view;
4377
cristy3ed852e2009-09-05 21:47:34 +00004378 ExceptionInfo
4379 *exception;
4380
cristy3ed852e2009-09-05 21:47:34 +00004381 MagickBooleanType
4382 status;
4383
cristybb503372010-05-27 20:51:26 +00004384 MagickOffsetType
4385 progress;
4386
4387 ssize_t
4388 y;
4389
cristy3ed852e2009-09-05 21:47:34 +00004390 assert(image != (Image *) NULL);
4391 assert(image->signature == MagickSignature);
4392 if (image->debug != MagickFalse)
4393 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4394 if (image->storage_class == PseudoClass)
4395 {
cristybb503372010-05-27 20:51:26 +00004396 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004397 i;
4398
4399 /*
4400 Solarize colormap.
4401 */
cristybb503372010-05-27 20:51:26 +00004402 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00004403 {
4404 if ((MagickRealType) image->colormap[i].red > threshold)
4405 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4406 if ((MagickRealType) image->colormap[i].green > threshold)
4407 image->colormap[i].green=(Quantum) QuantumRange-
4408 image->colormap[i].green;
4409 if ((MagickRealType) image->colormap[i].blue > threshold)
4410 image->colormap[i].blue=(Quantum) QuantumRange-
4411 image->colormap[i].blue;
4412 }
4413 }
4414 /*
4415 Solarize image.
4416 */
4417 status=MagickTrue;
4418 progress=0;
4419 exception=(&image->exception);
4420 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004421#if defined(MAGICKCORE_OPENMP_SUPPORT)
4422 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004423#endif
cristybb503372010-05-27 20:51:26 +00004424 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004425 {
cristybb503372010-05-27 20:51:26 +00004426 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004427 x;
4428
4429 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004430 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004431
4432 if (status == MagickFalse)
4433 continue;
4434 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4435 exception);
4436 if (q == (PixelPacket *) NULL)
4437 {
4438 status=MagickFalse;
4439 continue;
4440 }
cristybb503372010-05-27 20:51:26 +00004441 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004442 {
4443 if ((MagickRealType) q->red > threshold)
4444 q->red=(Quantum) QuantumRange-q->red;
4445 if ((MagickRealType) q->green > threshold)
4446 q->green=(Quantum) QuantumRange-q->green;
4447 if ((MagickRealType) q->blue > threshold)
4448 q->blue=(Quantum) QuantumRange-q->blue;
4449 q++;
4450 }
4451 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4452 status=MagickFalse;
4453 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4454 {
4455 MagickBooleanType
4456 proceed;
4457
cristyb5d5f722009-11-04 03:03:49 +00004458#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004459 #pragma omp critical (MagickCore_SolarizeImage)
4460#endif
4461 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4462 if (proceed == MagickFalse)
4463 status=MagickFalse;
4464 }
4465 }
4466 image_view=DestroyCacheView(image_view);
4467 return(status);
4468}
4469
4470/*
4471%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4472% %
4473% %
4474% %
4475% S t e g a n o I m a g e %
4476% %
4477% %
4478% %
4479%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4480%
4481% SteganoImage() hides a digital watermark within the image. Recover
4482% the hidden watermark later to prove that the authenticity of an image.
4483% Offset defines the start position within the image to hide the watermark.
4484%
4485% The format of the SteganoImage method is:
4486%
4487% Image *SteganoImage(const Image *image,Image *watermark,
4488% ExceptionInfo *exception)
4489%
4490% A description of each parameter follows:
4491%
4492% o image: the image.
4493%
4494% o watermark: the watermark image.
4495%
4496% o exception: return any errors or warnings in this structure.
4497%
4498*/
4499MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4500 ExceptionInfo *exception)
4501{
cristybb503372010-05-27 20:51:26 +00004502#define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) \
cristy3ed852e2009-09-05 21:47:34 +00004503 (i)) & 0x01) != 0)
cristyeaedf062010-05-29 22:36:02 +00004504#define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) != 0 ? (size_t) (alpha) \
4505 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
cristy3ed852e2009-09-05 21:47:34 +00004506#define SteganoImageTag "Stegano/Image"
4507
4508 Image
4509 *stegano_image;
4510
4511 int
4512 c;
4513
cristybb503372010-05-27 20:51:26 +00004514 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004515 i,
4516 j,
4517 k,
4518 y;
4519
4520 MagickBooleanType
4521 status;
4522
4523 PixelPacket
4524 pixel;
4525
cristybb503372010-05-27 20:51:26 +00004526 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004527 x;
4528
4529 register PixelPacket
4530 *q;
4531
cristybb503372010-05-27 20:51:26 +00004532 size_t
cristyeaedf062010-05-29 22:36:02 +00004533 depth,
4534 one;
cristy3ed852e2009-09-05 21:47:34 +00004535
4536 /*
4537 Initialize steganographic image attributes.
4538 */
4539 assert(image != (const Image *) NULL);
4540 assert(image->signature == MagickSignature);
4541 if (image->debug != MagickFalse)
4542 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4543 assert(watermark != (const Image *) NULL);
4544 assert(watermark->signature == MagickSignature);
4545 assert(exception != (ExceptionInfo *) NULL);
4546 assert(exception->signature == MagickSignature);
cristyeaedf062010-05-29 22:36:02 +00004547 one=1UL;
cristy3ed852e2009-09-05 21:47:34 +00004548 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4549 if (stegano_image == (Image *) NULL)
4550 return((Image *) NULL);
4551 if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
4552 {
4553 InheritException(exception,&stegano_image->exception);
4554 stegano_image=DestroyImage(stegano_image);
4555 return((Image *) NULL);
4556 }
4557 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4558 /*
4559 Hide watermark in low-order bits of image.
4560 */
4561 c=0;
4562 i=0;
4563 j=0;
4564 depth=stegano_image->depth;
4565 k=image->offset;
cristybb503372010-05-27 20:51:26 +00004566 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
cristy3ed852e2009-09-05 21:47:34 +00004567 {
cristybb503372010-05-27 20:51:26 +00004568 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
cristy3ed852e2009-09-05 21:47:34 +00004569 {
cristybb503372010-05-27 20:51:26 +00004570 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
cristy3ed852e2009-09-05 21:47:34 +00004571 {
4572 (void) GetOneVirtualPixel(watermark,x,y,&pixel,exception);
cristybb503372010-05-27 20:51:26 +00004573 if ((k/(ssize_t) stegano_image->columns) >= (ssize_t) stegano_image->rows)
cristy3ed852e2009-09-05 21:47:34 +00004574 break;
cristybb503372010-05-27 20:51:26 +00004575 q=GetAuthenticPixels(stegano_image,k % (ssize_t) stegano_image->columns,
4576 k/(ssize_t) stegano_image->columns,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004577 if (q == (PixelPacket *) NULL)
4578 break;
4579 switch (c)
4580 {
4581 case 0:
4582 {
4583 SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4584 break;
4585 }
4586 case 1:
4587 {
4588 SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4589 break;
4590 }
4591 case 2:
4592 {
4593 SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4594 break;
4595 }
4596 }
4597 if (SyncAuthenticPixels(stegano_image,exception) == MagickFalse)
4598 break;
4599 c++;
4600 if (c == 3)
4601 c=0;
4602 k++;
cristybb503372010-05-27 20:51:26 +00004603 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00004604 k=0;
4605 if (k == image->offset)
4606 j++;
4607 }
4608 }
cristy8b27a6d2010-02-14 03:31:15 +00004609 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004610 {
cristy8b27a6d2010-02-14 03:31:15 +00004611 MagickBooleanType
4612 proceed;
4613
4614 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4615 (depth-i),depth);
4616 if (proceed == MagickFalse)
4617 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004618 }
4619 }
4620 if (stegano_image->storage_class == PseudoClass)
4621 (void) SyncImage(stegano_image);
4622 return(stegano_image);
4623}
4624
4625/*
4626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4627% %
4628% %
4629% %
4630% S t e r e o A n a g l y p h I m a g e %
4631% %
4632% %
4633% %
4634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4635%
4636% StereoAnaglyphImage() combines two images and produces a single image that
4637% is the composite of a left and right image of a stereo pair. Special
4638% red-green stereo glasses are required to view this effect.
4639%
4640% The format of the StereoAnaglyphImage method is:
4641%
4642% Image *StereoImage(const Image *left_image,const Image *right_image,
4643% ExceptionInfo *exception)
4644% Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004645% const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004646% ExceptionInfo *exception)
4647%
4648% A description of each parameter follows:
4649%
4650% o left_image: the left image.
4651%
4652% o right_image: the right image.
4653%
4654% o exception: return any errors or warnings in this structure.
4655%
4656% o x_offset: amount, in pixels, by which the left image is offset to the
4657% right of the right image.
4658%
4659% o y_offset: amount, in pixels, by which the left image is offset to the
4660% bottom of the right image.
4661%
4662%
4663*/
4664MagickExport Image *StereoImage(const Image *left_image,
4665 const Image *right_image,ExceptionInfo *exception)
4666{
4667 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4668}
4669
4670MagickExport Image *StereoAnaglyphImage(const Image *left_image,
cristybb503372010-05-27 20:51:26 +00004671 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +00004672 ExceptionInfo *exception)
4673{
4674#define StereoImageTag "Stereo/Image"
4675
4676 const Image
4677 *image;
4678
4679 Image
4680 *stereo_image;
4681
cristybb503372010-05-27 20:51:26 +00004682 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004683 y;
4684
4685 MagickBooleanType
4686 status;
4687
4688 assert(left_image != (const Image *) NULL);
4689 assert(left_image->signature == MagickSignature);
4690 if (left_image->debug != MagickFalse)
4691 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4692 left_image->filename);
4693 assert(right_image != (const Image *) NULL);
4694 assert(right_image->signature == MagickSignature);
4695 assert(exception != (ExceptionInfo *) NULL);
4696 assert(exception->signature == MagickSignature);
4697 assert(right_image != (const Image *) NULL);
4698 image=left_image;
4699 if ((left_image->columns != right_image->columns) ||
4700 (left_image->rows != right_image->rows))
4701 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4702 /*
4703 Initialize stereo image attributes.
4704 */
4705 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4706 MagickTrue,exception);
4707 if (stereo_image == (Image *) NULL)
4708 return((Image *) NULL);
4709 if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
4710 {
4711 InheritException(exception,&stereo_image->exception);
4712 stereo_image=DestroyImage(stereo_image);
4713 return((Image *) NULL);
4714 }
4715 /*
4716 Copy left image to red channel and right image to blue channel.
4717 */
cristybb503372010-05-27 20:51:26 +00004718 for (y=0; y < (ssize_t) stereo_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004719 {
4720 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004721 *restrict p,
4722 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004723
cristybb503372010-05-27 20:51:26 +00004724 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004725 x;
4726
4727 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004728 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004729
4730 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4731 exception);
4732 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4733 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
4734 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
4735 (r == (PixelPacket *) NULL))
4736 break;
cristybb503372010-05-27 20:51:26 +00004737 for (x=0; x < (ssize_t) stereo_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004738 {
cristyce70c172010-01-07 17:15:30 +00004739 r->red=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00004740 r->green=q->green;
4741 r->blue=q->blue;
4742 r->opacity=(Quantum) ((p->opacity+q->opacity)/2);
4743 p++;
4744 q++;
4745 r++;
4746 }
4747 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4748 break;
cristy8b27a6d2010-02-14 03:31:15 +00004749 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004750 {
cristy8b27a6d2010-02-14 03:31:15 +00004751 MagickBooleanType
4752 proceed;
4753
cristybb503372010-05-27 20:51:26 +00004754 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4755 stereo_image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00004756 if (proceed == MagickFalse)
4757 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004758 }
4759 }
4760 return(stereo_image);
4761}
4762
4763/*
4764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4765% %
4766% %
4767% %
4768% S w i r l I m a g e %
4769% %
4770% %
4771% %
4772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4773%
4774% SwirlImage() swirls the pixels about the center of the image, where
4775% degrees indicates the sweep of the arc through which each pixel is moved.
4776% You get a more dramatic effect as the degrees move from 1 to 360.
4777%
4778% The format of the SwirlImage method is:
4779%
4780% Image *SwirlImage(const Image *image,double degrees,
4781% ExceptionInfo *exception)
4782%
4783% A description of each parameter follows:
4784%
4785% o image: the image.
4786%
4787% o degrees: Define the tightness of the swirling effect.
4788%
4789% o exception: return any errors or warnings in this structure.
4790%
4791*/
4792MagickExport Image *SwirlImage(const Image *image,double degrees,
4793 ExceptionInfo *exception)
4794{
4795#define SwirlImageTag "Swirl/Image"
4796
cristyfa112112010-01-04 17:48:07 +00004797 CacheView
4798 *image_view,
4799 *swirl_view;
4800
cristy3ed852e2009-09-05 21:47:34 +00004801 Image
4802 *swirl_image;
4803
cristy3ed852e2009-09-05 21:47:34 +00004804 MagickBooleanType
4805 status;
4806
cristybb503372010-05-27 20:51:26 +00004807 MagickOffsetType
4808 progress;
4809
cristy3ed852e2009-09-05 21:47:34 +00004810 MagickPixelPacket
4811 zero;
4812
4813 MagickRealType
4814 radius;
4815
4816 PointInfo
4817 center,
4818 scale;
4819
4820 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00004821 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00004822
cristybb503372010-05-27 20:51:26 +00004823 ssize_t
4824 y;
4825
cristy3ed852e2009-09-05 21:47:34 +00004826 /*
4827 Initialize swirl image attributes.
4828 */
4829 assert(image != (const Image *) NULL);
4830 assert(image->signature == MagickSignature);
4831 if (image->debug != MagickFalse)
4832 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4833 assert(exception != (ExceptionInfo *) NULL);
4834 assert(exception->signature == MagickSignature);
4835 swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4836 if (swirl_image == (Image *) NULL)
4837 return((Image *) NULL);
4838 if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
4839 {
4840 InheritException(exception,&swirl_image->exception);
4841 swirl_image=DestroyImage(swirl_image);
4842 return((Image *) NULL);
4843 }
4844 if (swirl_image->background_color.opacity != OpaqueOpacity)
4845 swirl_image->matte=MagickTrue;
4846 /*
4847 Compute scaling factor.
4848 */
4849 center.x=(double) image->columns/2.0;
4850 center.y=(double) image->rows/2.0;
4851 radius=MagickMax(center.x,center.y);
4852 scale.x=1.0;
4853 scale.y=1.0;
4854 if (image->columns > image->rows)
4855 scale.y=(double) image->columns/(double) image->rows;
4856 else
4857 if (image->columns < image->rows)
4858 scale.x=(double) image->rows/(double) image->columns;
4859 degrees=(double) DegreesToRadians(degrees);
4860 /*
4861 Swirl image.
4862 */
4863 status=MagickTrue;
4864 progress=0;
4865 GetMagickPixelPacket(swirl_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00004866 resample_filter=AcquireResampleFilterThreadSet(image,
4867 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004868 image_view=AcquireCacheView(image);
4869 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00004870#if defined(MAGICKCORE_OPENMP_SUPPORT)
4871 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004872#endif
cristybb503372010-05-27 20:51:26 +00004873 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004874 {
4875 MagickPixelPacket
4876 pixel;
4877
4878 MagickRealType
4879 distance;
4880
4881 PointInfo
4882 delta;
4883
4884 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004885 *restrict swirl_indexes;
cristy3ed852e2009-09-05 21:47:34 +00004886
cristybb503372010-05-27 20:51:26 +00004887 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004888 id,
4889 x;
4890
4891 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004892 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004893
4894 if (status == MagickFalse)
4895 continue;
4896 q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
4897 exception);
4898 if (q == (PixelPacket *) NULL)
4899 {
4900 status=MagickFalse;
4901 continue;
4902 }
4903 swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
4904 delta.y=scale.y*(double) (y-center.y);
4905 pixel=zero;
4906 id=GetOpenMPThreadId();
cristybb503372010-05-27 20:51:26 +00004907 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004908 {
4909 /*
4910 Determine if the pixel is within an ellipse.
4911 */
4912 delta.x=scale.x*(double) (x-center.x);
4913 distance=delta.x*delta.x+delta.y*delta.y;
4914 if (distance < (radius*radius))
4915 {
4916 MagickRealType
4917 cosine,
4918 factor,
4919 sine;
4920
4921 /*
4922 Swirl the pixel.
4923 */
4924 factor=1.0-sqrt((double) distance)/radius;
4925 sine=sin((double) (degrees*factor*factor));
4926 cosine=cos((double) (degrees*factor*factor));
4927 (void) ResamplePixelColor(resample_filter[id],(double) ((cosine*
4928 delta.x-sine*delta.y)/scale.x+center.x),(double) ((sine*delta.x+
4929 cosine*delta.y)/scale.y+center.y),&pixel);
4930 SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
4931 }
4932 q++;
4933 }
4934 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
4935 status=MagickFalse;
4936 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4937 {
4938 MagickBooleanType
4939 proceed;
4940
cristyb5d5f722009-11-04 03:03:49 +00004941#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004942 #pragma omp critical (MagickCore_SwirlImage)
4943#endif
4944 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
4945 if (proceed == MagickFalse)
4946 status=MagickFalse;
4947 }
4948 }
4949 swirl_view=DestroyCacheView(swirl_view);
4950 image_view=DestroyCacheView(image_view);
4951 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
4952 if (status == MagickFalse)
4953 swirl_image=DestroyImage(swirl_image);
4954 return(swirl_image);
4955}
4956
4957/*
4958%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4959% %
4960% %
4961% %
4962% T i n t I m a g e %
4963% %
4964% %
4965% %
4966%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4967%
4968% TintImage() applies a color vector to each pixel in the image. The length
4969% of the vector is 0 for black and white and at its maximum for the midtones.
4970% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
4971%
4972% The format of the TintImage method is:
4973%
4974% Image *TintImage(const Image *image,const char *opacity,
4975% const PixelPacket tint,ExceptionInfo *exception)
4976%
4977% A description of each parameter follows:
4978%
4979% o image: the image.
4980%
4981% o opacity: A color value used for tinting.
4982%
4983% o tint: A color value used for tinting.
4984%
4985% o exception: return any errors or warnings in this structure.
4986%
4987*/
4988MagickExport Image *TintImage(const Image *image,const char *opacity,
4989 const PixelPacket tint,ExceptionInfo *exception)
4990{
4991#define TintImageTag "Tint/Image"
4992
cristyc4c8d132010-01-07 01:58:38 +00004993 CacheView
4994 *image_view,
4995 *tint_view;
4996
cristy3ed852e2009-09-05 21:47:34 +00004997 GeometryInfo
4998 geometry_info;
4999
5000 Image
5001 *tint_image;
5002
cristy3ed852e2009-09-05 21:47:34 +00005003 MagickBooleanType
5004 status;
5005
cristybb503372010-05-27 20:51:26 +00005006 MagickOffsetType
5007 progress;
cristy3ed852e2009-09-05 21:47:34 +00005008
5009 MagickPixelPacket
5010 color_vector,
5011 pixel;
5012
cristybb503372010-05-27 20:51:26 +00005013 MagickStatusType
5014 flags;
5015
5016 ssize_t
5017 y;
5018
cristy3ed852e2009-09-05 21:47:34 +00005019 /*
5020 Allocate tint image.
5021 */
5022 assert(image != (const Image *) NULL);
5023 assert(image->signature == MagickSignature);
5024 if (image->debug != MagickFalse)
5025 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5026 assert(exception != (ExceptionInfo *) NULL);
5027 assert(exception->signature == MagickSignature);
5028 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5029 if (tint_image == (Image *) NULL)
5030 return((Image *) NULL);
5031 if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5032 {
5033 InheritException(exception,&tint_image->exception);
5034 tint_image=DestroyImage(tint_image);
5035 return((Image *) NULL);
5036 }
5037 if (opacity == (const char *) NULL)
5038 return(tint_image);
5039 /*
5040 Determine RGB values of the color.
5041 */
5042 flags=ParseGeometry(opacity,&geometry_info);
5043 pixel.red=geometry_info.rho;
5044 if ((flags & SigmaValue) != 0)
5045 pixel.green=geometry_info.sigma;
5046 else
5047 pixel.green=pixel.red;
5048 if ((flags & XiValue) != 0)
5049 pixel.blue=geometry_info.xi;
5050 else
5051 pixel.blue=pixel.red;
5052 if ((flags & PsiValue) != 0)
5053 pixel.opacity=geometry_info.psi;
5054 else
5055 pixel.opacity=(MagickRealType) OpaqueOpacity;
5056 color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5057 PixelIntensity(&tint));
5058 color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5059 PixelIntensity(&tint));
5060 color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5061 PixelIntensity(&tint));
5062 /*
5063 Tint image.
5064 */
5065 status=MagickTrue;
5066 progress=0;
5067 image_view=AcquireCacheView(image);
5068 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005069#if defined(MAGICKCORE_OPENMP_SUPPORT)
5070 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005071#endif
cristybb503372010-05-27 20:51:26 +00005072 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005073 {
5074 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005075 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005076
cristybb503372010-05-27 20:51:26 +00005077 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005078 x;
5079
5080 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005081 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005082
5083 if (status == MagickFalse)
5084 continue;
5085 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5086 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5087 exception);
5088 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5089 {
5090 status=MagickFalse;
5091 continue;
5092 }
cristybb503372010-05-27 20:51:26 +00005093 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005094 {
5095 MagickPixelPacket
5096 pixel;
5097
5098 MagickRealType
5099 weight;
5100
5101 weight=QuantumScale*p->red-0.5;
5102 pixel.red=(MagickRealType) p->red+color_vector.red*(1.0-(4.0*
5103 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005104 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00005105 weight=QuantumScale*p->green-0.5;
5106 pixel.green=(MagickRealType) p->green+color_vector.green*(1.0-(4.0*
5107 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005108 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00005109 weight=QuantumScale*p->blue-0.5;
5110 pixel.blue=(MagickRealType) p->blue+color_vector.blue*(1.0-(4.0*
5111 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005112 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5113 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00005114 p++;
5115 q++;
5116 }
5117 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5118 status=MagickFalse;
5119 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5120 {
5121 MagickBooleanType
5122 proceed;
5123
cristyb5d5f722009-11-04 03:03:49 +00005124#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005125 #pragma omp critical (MagickCore_TintImage)
5126#endif
5127 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5128 if (proceed == MagickFalse)
5129 status=MagickFalse;
5130 }
5131 }
5132 tint_view=DestroyCacheView(tint_view);
5133 image_view=DestroyCacheView(image_view);
5134 if (status == MagickFalse)
5135 tint_image=DestroyImage(tint_image);
5136 return(tint_image);
5137}
5138
5139/*
5140%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5141% %
5142% %
5143% %
5144% V i g n e t t e I m a g e %
5145% %
5146% %
5147% %
5148%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5149%
5150% VignetteImage() softens the edges of the image in vignette style.
5151%
5152% The format of the VignetteImage method is:
5153%
5154% Image *VignetteImage(const Image *image,const double radius,
cristybb503372010-05-27 20:51:26 +00005155% const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005156%
5157% A description of each parameter follows:
5158%
5159% o image: the image.
5160%
5161% o radius: the radius of the pixel neighborhood.
5162%
5163% o sigma: the standard deviation of the Gaussian, in pixels.
5164%
5165% o x, y: Define the x and y ellipse offset.
5166%
5167% o exception: return any errors or warnings in this structure.
5168%
5169*/
5170MagickExport Image *VignetteImage(const Image *image,const double radius,
cristybb503372010-05-27 20:51:26 +00005171 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00005172{
5173 char
5174 ellipse[MaxTextExtent];
5175
5176 DrawInfo
5177 *draw_info;
5178
5179 Image
5180 *canvas_image,
5181 *blur_image,
5182 *oval_image,
5183 *vignette_image;
5184
5185 assert(image != (Image *) NULL);
5186 assert(image->signature == MagickSignature);
5187 if (image->debug != MagickFalse)
5188 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5189 assert(exception != (ExceptionInfo *) NULL);
5190 assert(exception->signature == MagickSignature);
5191 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5192 if (canvas_image == (Image *) NULL)
5193 return((Image *) NULL);
5194 if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
5195 {
5196 InheritException(exception,&canvas_image->exception);
5197 canvas_image=DestroyImage(canvas_image);
5198 return((Image *) NULL);
5199 }
5200 canvas_image->matte=MagickTrue;
5201 oval_image=CloneImage(canvas_image,canvas_image->columns,
5202 canvas_image->rows,MagickTrue,exception);
5203 if (oval_image == (Image *) NULL)
5204 {
5205 canvas_image=DestroyImage(canvas_image);
5206 return((Image *) NULL);
5207 }
5208 (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5209 (void) SetImageBackgroundColor(oval_image);
5210 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5211 (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5212 (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
5213 (void) FormatMagickString(ellipse,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00005214 "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
cristy8cd5b312010-01-07 01:10:24 +00005215 image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005216 draw_info->primitive=AcquireString(ellipse);
5217 (void) DrawImage(oval_image,draw_info);
5218 draw_info=DestroyDrawInfo(draw_info);
5219 blur_image=BlurImage(oval_image,radius,sigma,exception);
5220 oval_image=DestroyImage(oval_image);
5221 if (blur_image == (Image *) NULL)
5222 {
5223 canvas_image=DestroyImage(canvas_image);
5224 return((Image *) NULL);
5225 }
5226 blur_image->matte=MagickFalse;
5227 (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5228 blur_image=DestroyImage(blur_image);
5229 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5230 canvas_image=DestroyImage(canvas_image);
5231 return(vignette_image);
5232}
5233
5234/*
5235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5236% %
5237% %
5238% %
5239% W a v e I m a g e %
5240% %
5241% %
5242% %
5243%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5244%
5245% WaveImage() creates a "ripple" effect in the image by shifting the pixels
cristycee97112010-05-28 00:44:52 +00005246% vertically along a sine wave whose amplitude and wavelength is specified
cristy3ed852e2009-09-05 21:47:34 +00005247% by the given parameters.
5248%
5249% The format of the WaveImage method is:
5250%
5251% Image *WaveImage(const Image *image,const double amplitude,
5252% const double wave_length,ExceptionInfo *exception)
5253%
5254% A description of each parameter follows:
5255%
5256% o image: the image.
5257%
5258% o amplitude, wave_length: Define the amplitude and wave length of the
5259% sine wave.
5260%
5261% o exception: return any errors or warnings in this structure.
5262%
5263*/
5264MagickExport Image *WaveImage(const Image *image,const double amplitude,
5265 const double wave_length,ExceptionInfo *exception)
5266{
5267#define WaveImageTag "Wave/Image"
5268
cristyfa112112010-01-04 17:48:07 +00005269 CacheView
5270 *wave_view;
5271
cristy3ed852e2009-09-05 21:47:34 +00005272 Image
5273 *wave_image;
5274
cristy3ed852e2009-09-05 21:47:34 +00005275 MagickBooleanType
5276 status;
5277
cristybb503372010-05-27 20:51:26 +00005278 MagickOffsetType
5279 progress;
5280
cristy3ed852e2009-09-05 21:47:34 +00005281 MagickPixelPacket
5282 zero;
5283
5284 MagickRealType
5285 *sine_map;
5286
cristybb503372010-05-27 20:51:26 +00005287 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005288 i;
5289
5290 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00005291 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00005292
cristybb503372010-05-27 20:51:26 +00005293 ssize_t
5294 y;
5295
cristy3ed852e2009-09-05 21:47:34 +00005296 /*
5297 Initialize wave image attributes.
5298 */
5299 assert(image != (Image *) NULL);
5300 assert(image->signature == MagickSignature);
5301 if (image->debug != MagickFalse)
5302 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5303 assert(exception != (ExceptionInfo *) NULL);
5304 assert(exception->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +00005305 wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
cristy3ed852e2009-09-05 21:47:34 +00005306 fabs(amplitude)),MagickTrue,exception);
5307 if (wave_image == (Image *) NULL)
5308 return((Image *) NULL);
5309 if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
5310 {
5311 InheritException(exception,&wave_image->exception);
5312 wave_image=DestroyImage(wave_image);
5313 return((Image *) NULL);
5314 }
5315 if (wave_image->background_color.opacity != OpaqueOpacity)
5316 wave_image->matte=MagickTrue;
5317 /*
5318 Allocate sine map.
5319 */
5320 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5321 sizeof(*sine_map));
5322 if (sine_map == (MagickRealType *) NULL)
5323 {
5324 wave_image=DestroyImage(wave_image);
5325 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5326 }
cristybb503372010-05-27 20:51:26 +00005327 for (i=0; i < (ssize_t) wave_image->columns; i++)
cristy3ed852e2009-09-05 21:47:34 +00005328 sine_map[i]=fabs(amplitude)+amplitude*sin((2*MagickPI*i)/wave_length);
5329 /*
5330 Wave image.
5331 */
5332 status=MagickTrue;
5333 progress=0;
5334 GetMagickPixelPacket(wave_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00005335 resample_filter=AcquireResampleFilterThreadSet(image,
5336 BackgroundVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005337 wave_view=AcquireCacheView(wave_image);
cristyb5d5f722009-11-04 03:03:49 +00005338#if defined(MAGICKCORE_OPENMP_SUPPORT)
5339 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005340#endif
cristybb503372010-05-27 20:51:26 +00005341 for (y=0; y < (ssize_t) wave_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005342 {
5343 MagickPixelPacket
5344 pixel;
5345
5346 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00005347 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00005348
cristybb503372010-05-27 20:51:26 +00005349 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005350 id,
5351 x;
5352
5353 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005354 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005355
5356 if (status == MagickFalse)
5357 continue;
5358 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5359 exception);
5360 if (q == (PixelPacket *) NULL)
5361 {
5362 status=MagickFalse;
5363 continue;
5364 }
5365 indexes=GetCacheViewAuthenticIndexQueue(wave_view);
5366 pixel=zero;
5367 id=GetOpenMPThreadId();
cristybb503372010-05-27 20:51:26 +00005368 for (x=0; x < (ssize_t) wave_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005369 {
5370 (void) ResamplePixelColor(resample_filter[id],(double) x,(double) (y-
5371 sine_map[x]),&pixel);
5372 SetPixelPacket(wave_image,&pixel,q,indexes+x);
5373 q++;
5374 }
5375 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5376 status=MagickFalse;
5377 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5378 {
5379 MagickBooleanType
5380 proceed;
5381
cristyb5d5f722009-11-04 03:03:49 +00005382#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005383 #pragma omp critical (MagickCore_WaveImage)
5384#endif
5385 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5386 if (proceed == MagickFalse)
5387 status=MagickFalse;
5388 }
5389 }
5390 wave_view=DestroyCacheView(wave_view);
5391 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5392 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5393 if (status == MagickFalse)
5394 wave_image=DestroyImage(wave_image);
5395 return(wave_image);
5396}