blob: 4ac16c7adf0ce27f96ea5dad1e8cebf55cf68d38 [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
161 register long
162 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");
179 for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
180 {
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
296 long
297 progress,
298 y;
299
300 MagickBooleanType
301 status;
302
303 MagickRealType
304 attenuate;
305
306 RandomInfo
cristyfa112112010-01-04 17:48:07 +0000307 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +0000308
309 /*
310 Initialize noise image attributes.
311 */
312 assert(image != (const Image *) NULL);
313 assert(image->signature == MagickSignature);
314 if (image->debug != MagickFalse)
315 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
316 assert(exception != (ExceptionInfo *) NULL);
317 assert(exception->signature == MagickSignature);
318 noise_image=CloneImage(image,0,0,MagickTrue,exception);
319 if (noise_image == (Image *) NULL)
320 return((Image *) NULL);
321 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
322 {
323 InheritException(exception,&noise_image->exception);
324 noise_image=DestroyImage(noise_image);
325 return((Image *) NULL);
326 }
327 /*
328 Add noise in each row.
329 */
330 attenuate=1.0;
331 option=GetImageArtifact(image,"attenuate");
332 if (option != (char *) NULL)
cristyf2f27272009-12-17 14:48:46 +0000333 attenuate=StringToDouble(option);
cristy3ed852e2009-09-05 21:47:34 +0000334 status=MagickTrue;
335 progress=0;
336 random_info=AcquireRandomInfoThreadSet();
337 image_view=AcquireCacheView(image);
338 noise_view=AcquireCacheView(noise_image);
cristy319a1e72010-02-21 15:13:11 +0000339#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000340 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000341#endif
342 for (y=0; y < (long) image->rows; y++)
343 {
344 MagickBooleanType
345 sync;
346
347 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000348 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000349
350 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000351 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000352
353 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000354 *restrict noise_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000355
356 register long
357 id,
358 x;
359
360 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000361 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000362
363 if (status == MagickFalse)
364 continue;
365 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
366 q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
367 exception);
368 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
369 {
370 status=MagickFalse;
371 continue;
372 }
373 indexes=GetCacheViewVirtualIndexQueue(image_view);
374 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
375 id=GetOpenMPThreadId();
376 for (x=0; x < (long) image->columns; x++)
377 {
378 if ((channel & RedChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000379 q->red=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000380 p->red,noise_type,attenuate));
381 if ((channel & GreenChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000382 q->green=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000383 p->green,noise_type,attenuate));
384 if ((channel & BlueChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000385 q->blue=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000386 p->blue,noise_type,attenuate));
387 if ((channel & OpacityChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000388 q->opacity=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000389 p->opacity,noise_type,attenuate));
390 if (((channel & IndexChannel) != 0) &&
391 (image->colorspace == CMYKColorspace))
cristyce70c172010-01-07 17:15:30 +0000392 noise_indexes[x]=(IndexPacket) ClampToQuantum(GenerateDifferentialNoise(
cristy3ed852e2009-09-05 21:47:34 +0000393 random_info[id],indexes[x],noise_type,attenuate));
394 p++;
395 q++;
396 }
397 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
398 if (sync == MagickFalse)
399 status=MagickFalse;
400 if (image->progress_monitor != (MagickProgressMonitor) NULL)
401 {
402 MagickBooleanType
403 proceed;
404
cristyb5d5f722009-11-04 03:03:49 +0000405#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy319a1e72010-02-21 15:13:11 +0000406 #pragma omp critical (MagickCore_AddNoiseImage)
cristy3ed852e2009-09-05 21:47:34 +0000407#endif
408 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
409 image->rows);
410 if (proceed == MagickFalse)
411 status=MagickFalse;
412 }
413 }
414 noise_view=DestroyCacheView(noise_view);
415 image_view=DestroyCacheView(image_view);
416 random_info=DestroyRandomInfoThreadSet(random_info);
417 if (status == MagickFalse)
418 noise_image=DestroyImage(noise_image);
419 return(noise_image);
420}
421
422/*
423%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
424% %
425% %
426% %
427% B l u e S h i f t I m a g e %
428% %
429% %
430% %
431%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
432%
433% BlueShiftImage() mutes the colors of the image to simulate a scene at
434% nighttime in the moonlight.
435%
436% The format of the BlueShiftImage method is:
437%
438% Image *BlueShiftImage(const Image *image,const double factor,
439% ExceptionInfo *exception)
440%
441% A description of each parameter follows:
442%
443% o image: the image.
444%
445% o factor: the shift factor.
446%
447% o exception: return any errors or warnings in this structure.
448%
449*/
450MagickExport Image *BlueShiftImage(const Image *image,const double factor,
451 ExceptionInfo *exception)
452{
453#define BlueShiftImageTag "BlueShift/Image"
454
cristyc4c8d132010-01-07 01:58:38 +0000455 CacheView
456 *image_view,
457 *shift_view;
458
cristy3ed852e2009-09-05 21:47:34 +0000459 Image
460 *shift_image;
461
462 long
463 progress,
464 y;
465
466 MagickBooleanType
467 status;
468
cristy3ed852e2009-09-05 21:47:34 +0000469 /*
470 Allocate blue shift image.
471 */
472 assert(image != (const Image *) NULL);
473 assert(image->signature == MagickSignature);
474 if (image->debug != MagickFalse)
475 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
476 assert(exception != (ExceptionInfo *) NULL);
477 assert(exception->signature == MagickSignature);
478 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,
479 exception);
480 if (shift_image == (Image *) NULL)
481 return((Image *) NULL);
482 if (SetImageStorageClass(shift_image,DirectClass) == MagickFalse)
483 {
484 InheritException(exception,&shift_image->exception);
485 shift_image=DestroyImage(shift_image);
486 return((Image *) NULL);
487 }
488 /*
489 Blue-shift DirectClass image.
490 */
491 status=MagickTrue;
492 progress=0;
493 image_view=AcquireCacheView(image);
494 shift_view=AcquireCacheView(shift_image);
cristy319a1e72010-02-21 15:13:11 +0000495#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000496 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000497#endif
498 for (y=0; y < (long) image->rows; y++)
499 {
500 MagickBooleanType
501 sync;
502
503 MagickPixelPacket
504 pixel;
505
506 Quantum
507 quantum;
508
509 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000510 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000511
512 register long
513 x;
514
515 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000516 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000517
518 if (status == MagickFalse)
519 continue;
520 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
521 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
522 exception);
523 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
524 {
525 status=MagickFalse;
526 continue;
527 }
528 for (x=0; x < (long) image->columns; x++)
529 {
cristyce70c172010-01-07 17:15:30 +0000530 quantum=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000531 if (p->green < quantum)
cristyce70c172010-01-07 17:15:30 +0000532 quantum=GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000533 if (p->blue < quantum)
cristyce70c172010-01-07 17:15:30 +0000534 quantum=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000535 pixel.red=0.5*(p->red+factor*quantum);
536 pixel.green=0.5*(p->green+factor*quantum);
537 pixel.blue=0.5*(p->blue+factor*quantum);
cristyce70c172010-01-07 17:15:30 +0000538 quantum=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000539 if (p->green > quantum)
cristyce70c172010-01-07 17:15:30 +0000540 quantum=GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000541 if (p->blue > quantum)
cristyce70c172010-01-07 17:15:30 +0000542 quantum=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000543 pixel.red=0.5*(pixel.red+factor*quantum);
544 pixel.green=0.5*(pixel.green+factor*quantum);
545 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristyce70c172010-01-07 17:15:30 +0000546 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
547 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
548 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +0000549 p++;
550 q++;
551 }
552 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
553 if (sync == MagickFalse)
554 status=MagickFalse;
555 if (image->progress_monitor != (MagickProgressMonitor) NULL)
556 {
557 MagickBooleanType
558 proceed;
559
cristy319a1e72010-02-21 15:13:11 +0000560#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000561 #pragma omp critical (MagickCore_BlueShiftImage)
562#endif
563 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
564 image->rows);
565 if (proceed == MagickFalse)
566 status=MagickFalse;
567 }
568 }
569 image_view=DestroyCacheView(image_view);
570 shift_view=DestroyCacheView(shift_view);
571 if (status == MagickFalse)
572 shift_image=DestroyImage(shift_image);
573 return(shift_image);
574}
575
576/*
577%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
578% %
579% %
580% %
581% C h a r c o a l I m a g e %
582% %
583% %
584% %
585%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
586%
587% CharcoalImage() creates a new image that is a copy of an existing one with
588% the edge highlighted. It allocates the memory necessary for the new Image
589% structure and returns a pointer to the new image.
590%
591% The format of the CharcoalImage method is:
592%
593% Image *CharcoalImage(const Image *image,const double radius,
594% const double sigma,ExceptionInfo *exception)
595%
596% A description of each parameter follows:
597%
598% o image: the image.
599%
600% o radius: the radius of the pixel neighborhood.
601%
602% o sigma: the standard deviation of the Gaussian, in pixels.
603%
604% o exception: return any errors or warnings in this structure.
605%
606*/
607MagickExport Image *CharcoalImage(const Image *image,const double radius,
608 const double sigma,ExceptionInfo *exception)
609{
610 Image
611 *charcoal_image,
612 *clone_image,
613 *edge_image;
614
615 assert(image != (Image *) NULL);
616 assert(image->signature == MagickSignature);
617 if (image->debug != MagickFalse)
618 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
619 assert(exception != (ExceptionInfo *) NULL);
620 assert(exception->signature == MagickSignature);
621 clone_image=CloneImage(image,0,0,MagickTrue,exception);
622 if (clone_image == (Image *) NULL)
623 return((Image *) NULL);
624 (void) SetImageType(clone_image,GrayscaleType);
625 edge_image=EdgeImage(clone_image,radius,exception);
626 clone_image=DestroyImage(clone_image);
627 if (edge_image == (Image *) NULL)
628 return((Image *) NULL);
629 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
630 edge_image=DestroyImage(edge_image);
631 if (charcoal_image == (Image *) NULL)
632 return((Image *) NULL);
633 (void) NormalizeImage(charcoal_image);
634 (void) NegateImage(charcoal_image,MagickFalse);
635 (void) SetImageType(charcoal_image,GrayscaleType);
636 return(charcoal_image);
637}
638
639/*
640%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
641% %
642% %
643% %
644% C o l o r i z e I m a g e %
645% %
646% %
647% %
648%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
649%
650% ColorizeImage() blends the fill color with each pixel in the image.
651% A percentage blend is specified with opacity. Control the application
652% of different color components by specifying a different percentage for
653% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
654%
655% The format of the ColorizeImage method is:
656%
657% Image *ColorizeImage(const Image *image,const char *opacity,
658% const PixelPacket colorize,ExceptionInfo *exception)
659%
660% A description of each parameter follows:
661%
662% o image: the image.
663%
664% o opacity: A character string indicating the level of opacity as a
665% percentage.
666%
667% o colorize: A color value.
668%
669% o exception: return any errors or warnings in this structure.
670%
671*/
672MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
673 const PixelPacket colorize,ExceptionInfo *exception)
674{
675#define ColorizeImageTag "Colorize/Image"
676
cristyc4c8d132010-01-07 01:58:38 +0000677 CacheView
678 *colorize_view,
679 *image_view;
680
cristy3ed852e2009-09-05 21:47:34 +0000681 GeometryInfo
682 geometry_info;
683
684 Image
685 *colorize_image;
686
687 long
688 progress,
689 y;
690
691 MagickBooleanType
692 status;
693
694 MagickPixelPacket
695 pixel;
696
697 MagickStatusType
698 flags;
699
cristy3ed852e2009-09-05 21:47:34 +0000700 /*
701 Allocate colorized image.
702 */
703 assert(image != (const Image *) NULL);
704 assert(image->signature == MagickSignature);
705 if (image->debug != MagickFalse)
706 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
707 assert(exception != (ExceptionInfo *) NULL);
708 assert(exception->signature == MagickSignature);
709 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
710 exception);
711 if (colorize_image == (Image *) NULL)
712 return((Image *) NULL);
713 if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
714 {
715 InheritException(exception,&colorize_image->exception);
716 colorize_image=DestroyImage(colorize_image);
717 return((Image *) NULL);
718 }
719 if (opacity == (const char *) NULL)
720 return(colorize_image);
721 /*
722 Determine RGB values of the pen color.
723 */
724 flags=ParseGeometry(opacity,&geometry_info);
725 pixel.red=geometry_info.rho;
726 pixel.green=geometry_info.rho;
727 pixel.blue=geometry_info.rho;
728 pixel.opacity=(MagickRealType) OpaqueOpacity;
729 if ((flags & SigmaValue) != 0)
730 pixel.green=geometry_info.sigma;
731 if ((flags & XiValue) != 0)
732 pixel.blue=geometry_info.xi;
733 if ((flags & PsiValue) != 0)
734 pixel.opacity=geometry_info.psi;
735 /*
736 Colorize DirectClass image.
737 */
738 status=MagickTrue;
739 progress=0;
740 image_view=AcquireCacheView(image);
741 colorize_view=AcquireCacheView(colorize_image);
cristy319a1e72010-02-21 15:13:11 +0000742#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb5d5f722009-11-04 03:03:49 +0000743 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000744#endif
745 for (y=0; y < (long) image->rows; y++)
746 {
747 MagickBooleanType
748 sync;
749
750 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000751 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000752
753 register long
754 x;
755
756 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000757 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000758
759 if (status == MagickFalse)
760 continue;
761 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
762 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
763 exception);
764 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
765 {
766 status=MagickFalse;
767 continue;
768 }
769 for (x=0; x < (long) image->columns; x++)
770 {
771 q->red=(Quantum) ((p->red*(100.0-pixel.red)+
772 colorize.red*pixel.red)/100.0);
773 q->green=(Quantum) ((p->green*(100.0-pixel.green)+
774 colorize.green*pixel.green)/100.0);
775 q->blue=(Quantum) ((p->blue*(100.0-pixel.blue)+
776 colorize.blue*pixel.blue)/100.0);
777 q->opacity=(Quantum) ((p->opacity*(100.0-pixel.opacity)+
778 colorize.opacity*pixel.opacity)/100.0);
779 p++;
780 q++;
781 }
782 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
783 if (sync == MagickFalse)
784 status=MagickFalse;
785 if (image->progress_monitor != (MagickProgressMonitor) NULL)
786 {
787 MagickBooleanType
788 proceed;
789
cristy319a1e72010-02-21 15:13:11 +0000790#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000791 #pragma omp critical (MagickCore_ColorizeImage)
792#endif
793 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
794 if (proceed == MagickFalse)
795 status=MagickFalse;
796 }
797 }
798 image_view=DestroyCacheView(image_view);
799 colorize_view=DestroyCacheView(colorize_view);
800 if (status == MagickFalse)
801 colorize_image=DestroyImage(colorize_image);
802 return(colorize_image);
803}
804
805/*
806%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
807% %
808% %
809% %
cristye6365592010-04-02 17:31:23 +0000810% C o l o r M a t r i x I m a g e %
811% %
812% %
813% %
814%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
815%
816% ColorMatrixImage() applies color transformation to an image. This method
817% permits saturation changes, hue rotation, luminance to alpha, and various
818% other effects. Although variable-sized transformation matrices can be used,
819% typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
820% (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
821% except offsets are in column 6 rather than 5 (in support of CMYKA images)
822% and offsets are normalized (divide Flash offset by 255).
823%
824% The format of the ColorMatrixImage method is:
825%
826% Image *ColorMatrixImage(const Image *image,
827% const KernelInfo *color_matrix,ExceptionInfo *exception)
828%
829% A description of each parameter follows:
830%
831% o image: the image.
832%
833% o color_matrix: the color matrix.
834%
835% o exception: return any errors or warnings in this structure.
836%
837*/
838MagickExport Image *ColorMatrixImage(const Image *image,
839 const KernelInfo *color_matrix,ExceptionInfo *exception)
840{
841#define ColorMatrixImageTag "ColorMatrix/Image"
842
843 CacheView
844 *color_view,
845 *image_view;
846
847 double
848 ColorMatrix[6][6] =
849 {
850 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
851 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
852 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
853 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
854 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
855 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
856 };
857
858 Image
859 *color_image;
860
861 long
862 progress,
863 u,
864 v,
865 y;
866
867 MagickBooleanType
868 status;
869
870 register long
871 i;
872
873 /*
874 Create color matrix.
875 */
876 assert(image != (Image *) NULL);
877 assert(image->signature == MagickSignature);
878 if (image->debug != MagickFalse)
879 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
880 assert(exception != (ExceptionInfo *) NULL);
881 assert(exception->signature == MagickSignature);
882 i=0;
883 for (v=0; v < (long) color_matrix->height; v++)
884 for (u=0; u < (long) color_matrix->width; u++)
885 {
886 if ((v < 6) && (u < 6))
887 ColorMatrix[v][u]=color_matrix->values[i];
888 i++;
889 }
890 /*
891 Initialize color image.
892 */
893 color_image=CloneImage(image,0,0,MagickTrue,exception);
894 if (color_image == (Image *) NULL)
895 return((Image *) NULL);
896 if (SetImageStorageClass(color_image,DirectClass) == MagickFalse)
897 {
898 InheritException(exception,&color_image->exception);
899 color_image=DestroyImage(color_image);
900 return((Image *) NULL);
901 }
902 if (image->debug != MagickFalse)
903 {
904 char
905 format[MaxTextExtent],
906 *message;
907
908 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
909 " ColorMatrix image with color matrix:");
910 message=AcquireString("");
911 for (v=0; v < 6; v++)
912 {
913 *message='\0';
914 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
915 (void) ConcatenateString(&message,format);
916 for (u=0; u < 6; u++)
917 {
918 (void) FormatMagickString(format,MaxTextExtent,"%+f ",
919 ColorMatrix[v][u]);
920 (void) ConcatenateString(&message,format);
921 }
922 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
923 }
924 message=DestroyString(message);
925 }
926 /*
927 ColorMatrix image.
928 */
929 status=MagickTrue;
930 progress=0;
931 image_view=AcquireCacheView(image);
932 color_view=AcquireCacheView(color_image);
933#if defined(MAGICKCORE_OPENMP_SUPPORT)
934 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
935#endif
936 for (y=0; y < (long) image->rows; y++)
937 {
938 MagickRealType
939 pixel;
940
941 register const IndexPacket
942 *restrict indexes;
943
944 register const PixelPacket
945 *restrict p;
946
947 register long
948 x;
949
950 register IndexPacket
951 *restrict color_indexes;
952
953 register PixelPacket
954 *restrict q;
955
956 if (status == MagickFalse)
957 continue;
958 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
959 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
960 exception);
961 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
962 {
963 status=MagickFalse;
964 continue;
965 }
966 indexes=GetCacheViewVirtualIndexQueue(image_view);
967 color_indexes=GetCacheViewAuthenticIndexQueue(color_view);
968 for (x=0; x < (long) image->columns; x++)
969 {
970 register long
971 v;
972
973 unsigned long
974 height;
975
976 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
977 for (v=0; v < (long) height; v++)
978 {
979 pixel=ColorMatrix[v][0]*p->red+ColorMatrix[v][1]*p->green+
980 ColorMatrix[v][2]*p->blue;
981 if (image->matte != MagickFalse)
982 pixel+=ColorMatrix[v][3]*(QuantumRange-p->opacity);
983 if (image->colorspace == CMYKColorspace)
984 pixel+=ColorMatrix[v][4]*indexes[x];
985 pixel+=QuantumRange*ColorMatrix[v][5];
986 switch (v)
987 {
988 case 0: q->red=ClampToQuantum(pixel); break;
989 case 1: q->green=ClampToQuantum(pixel); break;
990 case 2: q->blue=ClampToQuantum(pixel); break;
991 case 3:
992 {
993 if (image->matte != MagickFalse)
994 q->opacity=ClampToQuantum(QuantumRange-pixel);
995 break;
996 }
997 case 4:
998 {
999 if (image->colorspace == CMYKColorspace)
1000 color_indexes[x]=ClampToQuantum(pixel);
1001 break;
1002 }
1003 }
1004 }
1005 p++;
1006 q++;
1007 }
1008 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1009 status=MagickFalse;
1010 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1011 {
1012 MagickBooleanType
1013 proceed;
1014
1015#if defined(MAGICKCORE_OPENMP_SUPPORT)
1016 #pragma omp critical (MagickCore_ColorMatrixImage)
1017#endif
1018 proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1019 image->rows);
1020 if (proceed == MagickFalse)
1021 status=MagickFalse;
1022 }
1023 }
1024 color_view=DestroyCacheView(color_view);
1025 image_view=DestroyCacheView(image_view);
1026 if (status == MagickFalse)
1027 color_image=DestroyImage(color_image);
1028 return(color_image);
1029}
1030
1031/*
1032%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1033% %
1034% %
1035% %
cristy3ed852e2009-09-05 21:47:34 +00001036+ D e s t r o y F x I n f o %
1037% %
1038% %
1039% %
1040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1041%
1042% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1043%
1044% The format of the DestroyFxInfo method is:
1045%
1046% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1047%
1048% A description of each parameter follows:
1049%
1050% o fx_info: the fx info.
1051%
1052*/
1053MagickExport FxInfo *DestroyFxInfo(FxInfo *fx_info)
1054{
1055 register long
1056 i;
1057
1058 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1059 fx_info->expression=DestroyString(fx_info->expression);
1060 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1061 fx_info->colors=DestroySplayTree(fx_info->colors);
1062 for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
1063 fx_info->resample_filter[i]=DestroyResampleFilter(
1064 fx_info->resample_filter[i]);
1065 fx_info->resample_filter=(ResampleFilter **) RelinquishMagickMemory(
1066 fx_info->resample_filter);
1067 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1068 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1069 return(fx_info);
1070}
1071
1072/*
1073%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1074% %
1075% %
1076% %
cristy3ed852e2009-09-05 21:47:34 +00001077+ 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 %
1078% %
1079% %
1080% %
1081%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1082%
1083% FxEvaluateChannelExpression() evaluates an expression and returns the
1084% results.
1085%
1086% The format of the FxEvaluateExpression method is:
1087%
1088% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
1089% const ChannelType channel,const long x,const long y,
1090% MagickRealType *alpha,Exceptioninfo *exception)
1091% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1092% MagickRealType *alpha,Exceptioninfo *exception)
1093%
1094% A description of each parameter follows:
1095%
1096% o fx_info: the fx info.
1097%
1098% o channel: the channel.
1099%
1100% o x,y: the pixel position.
1101%
1102% o alpha: the result.
1103%
1104% o exception: return any errors or warnings in this structure.
1105%
1106*/
1107
cristy351842f2010-03-07 15:27:38 +00001108static inline double MagickMax(const double x,const double y)
1109{
1110 if (x > y)
1111 return(x);
1112 return(y);
1113}
1114
1115static inline double MagickMin(const double x,const double y)
1116{
1117 if (x < y)
1118 return(x);
1119 return(y);
1120}
1121
cristy3ed852e2009-09-05 21:47:34 +00001122static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
1123 ChannelType channel,const char *symbol,ExceptionInfo *exception)
1124{
1125 char
1126 key[MaxTextExtent],
1127 statistic[MaxTextExtent];
1128
1129 const char
1130 *value;
1131
1132 register const char
1133 *p;
1134
1135 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1136 if (*p == '.')
1137 switch (*++p) /* e.g. depth.r */
1138 {
1139 case 'r': channel=RedChannel; break;
1140 case 'g': channel=GreenChannel; break;
1141 case 'b': channel=BlueChannel; break;
1142 case 'c': channel=CyanChannel; break;
1143 case 'm': channel=MagentaChannel; break;
1144 case 'y': channel=YellowChannel; break;
1145 case 'k': channel=BlackChannel; break;
1146 default: break;
1147 }
cristyccfb71b2010-01-22 03:12:53 +00001148 (void) FormatMagickString(key,MaxTextExtent,"%p.%ld.%s",(void *) image,
1149 (long) channel,symbol);
cristy3ed852e2009-09-05 21:47:34 +00001150 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1151 if (value != (const char *) NULL)
cristyf2f27272009-12-17 14:48:46 +00001152 return(QuantumScale*StringToDouble(value));
cristy3ed852e2009-09-05 21:47:34 +00001153 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1154 if (LocaleNCompare(symbol,"depth",5) == 0)
1155 {
1156 unsigned long
1157 depth;
1158
1159 depth=GetImageChannelDepth(image,channel,exception);
1160 (void) FormatMagickString(statistic,MaxTextExtent,"%lu",depth);
1161 }
1162 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1163 {
1164 double
1165 kurtosis,
1166 skewness;
1167
1168 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1169 exception);
cristye7f51092010-01-17 00:39:37 +00001170 (void) FormatMagickString(statistic,MaxTextExtent,"%g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001171 }
1172 if (LocaleNCompare(symbol,"maxima",6) == 0)
1173 {
1174 double
1175 maxima,
1176 minima;
1177
1178 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristye7f51092010-01-17 00:39:37 +00001179 (void) FormatMagickString(statistic,MaxTextExtent,"%g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001180 }
1181 if (LocaleNCompare(symbol,"mean",4) == 0)
1182 {
1183 double
1184 mean,
1185 standard_deviation;
1186
1187 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1188 exception);
cristye7f51092010-01-17 00:39:37 +00001189 (void) FormatMagickString(statistic,MaxTextExtent,"%g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001190 }
1191 if (LocaleNCompare(symbol,"minima",6) == 0)
1192 {
1193 double
1194 maxima,
1195 minima;
1196
1197 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristye7f51092010-01-17 00:39:37 +00001198 (void) FormatMagickString(statistic,MaxTextExtent,"%g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001199 }
1200 if (LocaleNCompare(symbol,"skewness",8) == 0)
1201 {
1202 double
1203 kurtosis,
1204 skewness;
1205
1206 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1207 exception);
cristye7f51092010-01-17 00:39:37 +00001208 (void) FormatMagickString(statistic,MaxTextExtent,"%g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001209 }
1210 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1211 {
1212 double
1213 mean,
1214 standard_deviation;
1215
1216 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1217 exception);
cristye7f51092010-01-17 00:39:37 +00001218 (void) FormatMagickString(statistic,MaxTextExtent,"%g",
cristy3ed852e2009-09-05 21:47:34 +00001219 standard_deviation);
1220 }
1221 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1222 ConstantString(statistic));
cristyf2f27272009-12-17 14:48:46 +00001223 return(QuantumScale*StringToDouble(statistic));
cristy3ed852e2009-09-05 21:47:34 +00001224}
1225
1226static MagickRealType
1227 FxEvaluateSubexpression(FxInfo *,const ChannelType,const long,const long,
1228 const char *,MagickRealType *,ExceptionInfo *);
1229
1230static inline MagickRealType FxMax(FxInfo *fx_info,const ChannelType channel,
1231 const long x,const long y,const char *expression,ExceptionInfo *exception)
1232{
1233 MagickRealType
1234 alpha,
1235 beta;
1236
1237 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1238 return((MagickRealType) MagickMax((double) alpha,(double) beta));
1239}
1240
1241static inline MagickRealType FxMin(FxInfo *fx_info,ChannelType channel,
1242 const long x,const long y,const char *expression,ExceptionInfo *exception)
1243{
1244 MagickRealType
1245 alpha,
1246 beta;
1247
1248 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1249 return((MagickRealType) MagickMin((double) alpha,(double) beta));
1250}
1251
1252static inline const char *FxSubexpression(const char *expression,
1253 ExceptionInfo *exception)
1254{
1255 const char
1256 *subexpression;
1257
1258 register long
1259 level;
1260
1261 level=0;
1262 subexpression=expression;
1263 while ((*subexpression != '\0') &&
1264 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1265 {
1266 if (strchr("(",(int) *subexpression) != (char *) NULL)
1267 level++;
1268 else
1269 if (strchr(")",(int) *subexpression) != (char *) NULL)
1270 level--;
1271 subexpression++;
1272 }
1273 if (*subexpression == '\0')
1274 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1275 "UnbalancedParenthesis","`%s'",expression);
1276 return(subexpression);
1277}
1278
1279static MagickRealType FxGetSymbol(FxInfo *fx_info,const ChannelType channel,
1280 const long x,const long y,const char *expression,ExceptionInfo *exception)
1281{
1282 char
1283 *q,
1284 subexpression[MaxTextExtent],
1285 symbol[MaxTextExtent];
1286
1287 const char
1288 *p,
1289 *value;
1290
1291 Image
1292 *image;
1293
1294 MagickPixelPacket
1295 pixel;
1296
1297 MagickRealType
1298 alpha,
1299 beta;
1300
1301 PointInfo
1302 point;
1303
1304 register long
1305 i;
1306
1307 size_t
1308 length;
1309
1310 unsigned long
1311 level;
1312
1313 p=expression;
1314 i=GetImageIndexInList(fx_info->images);
1315 level=0;
1316 point.x=(double) x;
1317 point.y=(double) y;
1318 if (isalpha((int) *(p+1)) == 0)
1319 {
1320 if (strchr("suv",(int) *p) != (char *) NULL)
1321 {
1322 switch (*p)
1323 {
1324 case 's':
1325 default:
1326 {
1327 i=GetImageIndexInList(fx_info->images);
1328 break;
1329 }
1330 case 'u': i=0; break;
1331 case 'v': i=1; break;
1332 }
1333 p++;
1334 if (*p == '[')
1335 {
1336 level++;
1337 q=subexpression;
1338 for (p++; *p != '\0'; )
1339 {
1340 if (*p == '[')
1341 level++;
1342 else
1343 if (*p == ']')
1344 {
1345 level--;
1346 if (level == 0)
1347 break;
1348 }
1349 *q++=(*p++);
1350 }
1351 *q='\0';
1352 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1353 &beta,exception);
1354 i=(long) (alpha+0.5);
1355 p++;
1356 }
1357 if (*p == '.')
1358 p++;
1359 }
1360 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1361 {
1362 p++;
1363 if (*p == '{')
1364 {
1365 level++;
1366 q=subexpression;
1367 for (p++; *p != '\0'; )
1368 {
1369 if (*p == '{')
1370 level++;
1371 else
1372 if (*p == '}')
1373 {
1374 level--;
1375 if (level == 0)
1376 break;
1377 }
1378 *q++=(*p++);
1379 }
1380 *q='\0';
1381 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1382 &beta,exception);
1383 point.x=alpha;
1384 point.y=beta;
1385 p++;
1386 }
1387 else
1388 if (*p == '[')
1389 {
1390 level++;
1391 q=subexpression;
1392 for (p++; *p != '\0'; )
1393 {
1394 if (*p == '[')
1395 level++;
1396 else
1397 if (*p == ']')
1398 {
1399 level--;
1400 if (level == 0)
1401 break;
1402 }
1403 *q++=(*p++);
1404 }
1405 *q='\0';
1406 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1407 &beta,exception);
1408 point.x+=alpha;
1409 point.y+=beta;
1410 p++;
1411 }
1412 if (*p == '.')
1413 p++;
1414 }
1415 }
1416 length=GetImageListLength(fx_info->images);
1417 while (i < 0)
1418 i+=(long) length;
1419 i%=length;
1420 image=GetImageFromList(fx_info->images,i);
1421 if (image == (Image *) NULL)
1422 {
1423 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1424 "NoSuchImage","`%s'",expression);
1425 return(0.0);
1426 }
1427 (void) ResamplePixelColor(fx_info->resample_filter[i],point.x,point.y,&pixel);
1428 if ((strlen(p) > 2) &&
1429 (LocaleCompare(p,"intensity") != 0) &&
1430 (LocaleCompare(p,"luminance") != 0) &&
1431 (LocaleCompare(p,"hue") != 0) &&
1432 (LocaleCompare(p,"saturation") != 0) &&
1433 (LocaleCompare(p,"lightness") != 0))
1434 {
1435 char
1436 name[MaxTextExtent];
1437
1438 (void) CopyMagickString(name,p,MaxTextExtent);
1439 for (q=name+(strlen(name)-1); q > name; q--)
1440 {
1441 if (*q == ')')
1442 break;
1443 if (*q == '.')
1444 {
1445 *q='\0';
1446 break;
1447 }
1448 }
1449 if ((strlen(name) > 2) &&
1450 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1451 {
1452 MagickPixelPacket
1453 *color;
1454
1455 color=(MagickPixelPacket *) GetValueFromSplayTree(fx_info->colors,
1456 name);
1457 if (color != (MagickPixelPacket *) NULL)
1458 {
1459 pixel=(*color);
1460 p+=strlen(name);
1461 }
1462 else
1463 if (QueryMagickColor(name,&pixel,fx_info->exception) != MagickFalse)
1464 {
1465 (void) AddValueToSplayTree(fx_info->colors,ConstantString(name),
1466 CloneMagickPixelPacket(&pixel));
1467 p+=strlen(name);
1468 }
1469 }
1470 }
1471 (void) CopyMagickString(symbol,p,MaxTextExtent);
1472 StripString(symbol);
1473 if (*symbol == '\0')
1474 {
1475 switch (channel)
1476 {
1477 case RedChannel: return(QuantumScale*pixel.red);
1478 case GreenChannel: return(QuantumScale*pixel.green);
1479 case BlueChannel: return(QuantumScale*pixel.blue);
1480 case OpacityChannel:
1481 {
1482 if (pixel.matte == MagickFalse)
1483 {
1484 fx_info->matte=MagickFalse;
1485 return(1.0);
1486 }
cristy46f08202010-01-10 04:04:21 +00001487 return((MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel)));
cristy3ed852e2009-09-05 21:47:34 +00001488 }
1489 case IndexChannel:
1490 {
1491 if (image->colorspace != CMYKColorspace)
1492 {
1493 (void) ThrowMagickException(exception,GetMagickModule(),
1494 OptionError,"ColorSeparatedImageRequired","`%s'",
1495 image->filename);
1496 return(0.0);
1497 }
1498 return(QuantumScale*pixel.index);
1499 }
1500 default:
1501 break;
1502 }
1503 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1504 "UnableToParseExpression","`%s'",p);
1505 return(0.0);
1506 }
1507 switch (*symbol)
1508 {
1509 case 'A':
1510 case 'a':
1511 {
1512 if (LocaleCompare(symbol,"a") == 0)
cristy46f08202010-01-10 04:04:21 +00001513 return((MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel)));
cristy3ed852e2009-09-05 21:47:34 +00001514 break;
1515 }
1516 case 'B':
1517 case 'b':
1518 {
1519 if (LocaleCompare(symbol,"b") == 0)
1520 return(QuantumScale*pixel.blue);
1521 break;
1522 }
1523 case 'C':
1524 case 'c':
1525 {
1526 if (LocaleNCompare(symbol,"channel",7) == 0)
1527 {
1528 GeometryInfo
1529 channel_info;
1530
1531 MagickStatusType
1532 flags;
1533
1534 flags=ParseGeometry(symbol+7,&channel_info);
1535 if (image->colorspace == CMYKColorspace)
1536 switch (channel)
1537 {
1538 case CyanChannel:
1539 {
1540 if ((flags & RhoValue) == 0)
1541 return(0.0);
1542 return(channel_info.rho);
1543 }
1544 case MagentaChannel:
1545 {
1546 if ((flags & SigmaValue) == 0)
1547 return(0.0);
1548 return(channel_info.sigma);
1549 }
1550 case YellowChannel:
1551 {
1552 if ((flags & XiValue) == 0)
1553 return(0.0);
1554 return(channel_info.xi);
1555 }
1556 case BlackChannel:
1557 {
1558 if ((flags & PsiValue) == 0)
1559 return(0.0);
1560 return(channel_info.psi);
1561 }
1562 case OpacityChannel:
1563 {
1564 if ((flags & ChiValue) == 0)
1565 return(0.0);
1566 return(channel_info.chi);
1567 }
1568 default:
1569 return(0.0);
1570 }
1571 switch (channel)
1572 {
1573 case RedChannel:
1574 {
1575 if ((flags & RhoValue) == 0)
1576 return(0.0);
1577 return(channel_info.rho);
1578 }
1579 case GreenChannel:
1580 {
1581 if ((flags & SigmaValue) == 0)
1582 return(0.0);
1583 return(channel_info.sigma);
1584 }
1585 case BlueChannel:
1586 {
1587 if ((flags & XiValue) == 0)
1588 return(0.0);
1589 return(channel_info.xi);
1590 }
1591 case OpacityChannel:
1592 {
1593 if ((flags & PsiValue) == 0)
1594 return(0.0);
1595 return(channel_info.psi);
1596 }
1597 case IndexChannel:
1598 {
1599 if ((flags & ChiValue) == 0)
1600 return(0.0);
1601 return(channel_info.chi);
1602 }
1603 default:
1604 return(0.0);
1605 }
1606 return(0.0);
1607 }
1608 if (LocaleCompare(symbol,"c") == 0)
1609 return(QuantumScale*pixel.red);
1610 break;
1611 }
1612 case 'D':
1613 case 'd':
1614 {
1615 if (LocaleNCompare(symbol,"depth",5) == 0)
1616 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1617 break;
1618 }
1619 case 'G':
1620 case 'g':
1621 {
1622 if (LocaleCompare(symbol,"g") == 0)
1623 return(QuantumScale*pixel.green);
1624 break;
1625 }
1626 case 'K':
1627 case 'k':
1628 {
1629 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1630 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1631 if (LocaleCompare(symbol,"k") == 0)
1632 {
1633 if (image->colorspace != CMYKColorspace)
1634 {
1635 (void) ThrowMagickException(exception,GetMagickModule(),
1636 OptionError,"ColorSeparatedImageRequired","`%s'",
1637 image->filename);
1638 return(0.0);
1639 }
1640 return(QuantumScale*pixel.index);
1641 }
1642 break;
1643 }
1644 case 'H':
1645 case 'h':
1646 {
1647 if (LocaleCompare(symbol,"h") == 0)
1648 return((MagickRealType) image->rows);
1649 if (LocaleCompare(symbol,"hue") == 0)
1650 {
1651 double
1652 hue,
1653 lightness,
1654 saturation;
1655
cristyce70c172010-01-07 17:15:30 +00001656 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1657 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001658 return(hue);
1659 }
1660 break;
1661 }
1662 case 'I':
1663 case 'i':
1664 {
1665 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1666 (LocaleCompare(symbol,"image.minima") == 0) ||
1667 (LocaleCompare(symbol,"image.maxima") == 0) ||
1668 (LocaleCompare(symbol,"image.mean") == 0) ||
1669 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1670 (LocaleCompare(symbol,"image.skewness") == 0) ||
1671 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1672 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1673 if (LocaleCompare(symbol,"image.resolution.x") == 0)
1674 return(image->x_resolution);
1675 if (LocaleCompare(symbol,"image.resolution.y") == 0)
1676 return(image->y_resolution);
1677 if (LocaleCompare(symbol,"intensity") == 0)
1678 return(QuantumScale*MagickPixelIntensityToQuantum(&pixel));
1679 if (LocaleCompare(symbol,"i") == 0)
1680 return((MagickRealType) x);
1681 break;
1682 }
1683 case 'J':
1684 case 'j':
1685 {
1686 if (LocaleCompare(symbol,"j") == 0)
1687 return((MagickRealType) y);
1688 break;
1689 }
1690 case 'L':
1691 case 'l':
1692 {
1693 if (LocaleCompare(symbol,"lightness") == 0)
1694 {
1695 double
1696 hue,
1697 lightness,
1698 saturation;
1699
cristyce70c172010-01-07 17:15:30 +00001700 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1701 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001702 return(lightness);
1703 }
1704 if (LocaleCompare(symbol,"luminance") == 0)
1705 {
1706 double
1707 luminence;
1708
1709 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1710 return(QuantumScale*luminence);
1711 }
1712 break;
1713 }
1714 case 'M':
1715 case 'm':
1716 {
1717 if (LocaleNCompare(symbol,"maxima",6) == 0)
1718 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1719 if (LocaleNCompare(symbol,"mean",4) == 0)
1720 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1721 if (LocaleNCompare(symbol,"minima",6) == 0)
1722 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1723 if (LocaleCompare(symbol,"m") == 0)
1724 return(QuantumScale*pixel.blue);
1725 break;
1726 }
1727 case 'N':
1728 case 'n':
1729 {
1730 if (LocaleCompare(symbol,"n") == 0)
1731 return((MagickRealType) GetImageListLength(fx_info->images));
1732 break;
1733 }
1734 case 'O':
1735 case 'o':
1736 {
1737 if (LocaleCompare(symbol,"o") == 0)
1738 return(QuantumScale*pixel.opacity);
1739 break;
1740 }
1741 case 'P':
1742 case 'p':
1743 {
1744 if (LocaleCompare(symbol,"page.height") == 0)
1745 return((MagickRealType) image->page.height);
1746 if (LocaleCompare(symbol,"page.width") == 0)
1747 return((MagickRealType) image->page.width);
1748 if (LocaleCompare(symbol,"page.x") == 0)
1749 return((MagickRealType) image->page.x);
1750 if (LocaleCompare(symbol,"page.y") == 0)
1751 return((MagickRealType) image->page.y);
1752 break;
1753 }
1754 case 'R':
1755 case 'r':
1756 {
1757 if (LocaleCompare(symbol,"resolution.x") == 0)
1758 return(image->x_resolution);
1759 if (LocaleCompare(symbol,"resolution.y") == 0)
1760 return(image->y_resolution);
1761 if (LocaleCompare(symbol,"r") == 0)
1762 return(QuantumScale*pixel.red);
1763 break;
1764 }
1765 case 'S':
1766 case 's':
1767 {
1768 if (LocaleCompare(symbol,"saturation") == 0)
1769 {
1770 double
1771 hue,
1772 lightness,
1773 saturation;
1774
cristyce70c172010-01-07 17:15:30 +00001775 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1776 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001777 return(saturation);
1778 }
1779 if (LocaleNCompare(symbol,"skewness",8) == 0)
1780 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1781 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1782 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1783 break;
1784 }
1785 case 'T':
1786 case 't':
1787 {
1788 if (LocaleCompare(symbol,"t") == 0)
1789 return((MagickRealType) fx_info->images->scene);
1790 break;
1791 }
1792 case 'W':
1793 case 'w':
1794 {
1795 if (LocaleCompare(symbol,"w") == 0)
1796 return((MagickRealType) image->columns);
1797 break;
1798 }
1799 case 'Y':
1800 case 'y':
1801 {
1802 if (LocaleCompare(symbol,"y") == 0)
1803 return(QuantumScale*pixel.green);
1804 break;
1805 }
1806 case 'Z':
1807 case 'z':
1808 {
1809 if (LocaleCompare(symbol,"z") == 0)
1810 {
1811 MagickRealType
1812 depth;
1813
1814 depth=(MagickRealType) GetImageChannelDepth(image,channel,
1815 fx_info->exception);
1816 return(depth);
1817 }
1818 break;
1819 }
1820 default:
1821 break;
1822 }
1823 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1824 if (value != (const char *) NULL)
cristyf2f27272009-12-17 14:48:46 +00001825 return((MagickRealType) StringToDouble(value));
cristy3ed852e2009-09-05 21:47:34 +00001826 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1827 "UnableToParseExpression","`%s'",symbol);
1828 return(0.0);
1829}
1830
1831static const char *FxOperatorPrecedence(const char *expression,
1832 ExceptionInfo *exception)
1833{
1834 typedef enum
1835 {
1836 UndefinedPrecedence,
1837 NullPrecedence,
1838 BitwiseComplementPrecedence,
1839 ExponentPrecedence,
1840 MultiplyPrecedence,
1841 AdditionPrecedence,
1842 ShiftPrecedence,
1843 RelationalPrecedence,
1844 EquivalencyPrecedence,
1845 BitwiseAndPrecedence,
1846 BitwiseOrPrecedence,
1847 LogicalAndPrecedence,
1848 LogicalOrPrecedence,
1849 TernaryPrecedence,
1850 AssignmentPrecedence,
1851 CommaPrecedence,
1852 SeparatorPrecedence
1853 } FxPrecedence;
1854
1855 FxPrecedence
1856 precedence,
1857 target;
1858
1859 register const char
1860 *subexpression;
1861
1862 register int
1863 c;
1864
1865 unsigned long
1866 level;
1867
1868 c=0;
1869 level=0;
1870 subexpression=(const char *) NULL;
1871 target=NullPrecedence;
1872 while (*expression != '\0')
1873 {
1874 precedence=UndefinedPrecedence;
1875 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1876 {
1877 expression++;
1878 continue;
1879 }
cristy488fa882010-03-01 22:34:24 +00001880 switch (*expression)
1881 {
1882 case 'A':
1883 case 'a':
cristy3ed852e2009-09-05 21:47:34 +00001884 {
cristy488fa882010-03-01 22:34:24 +00001885 if (LocaleNCompare(expression,"atan2",5) == 0)
1886 {
1887 expression+=5;
1888 break;
1889 }
1890 break;
cristy3ed852e2009-09-05 21:47:34 +00001891 }
cristy488fa882010-03-01 22:34:24 +00001892 case 'J':
1893 case 'j':
1894 {
1895 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1896 (LocaleNCompare(expression,"j1",2) == 0))
1897 {
1898 expression+=2;
1899 break;
1900 }
1901 break;
1902 }
1903 default:
1904 break;
1905 }
cristy3ed852e2009-09-05 21:47:34 +00001906 if ((c == (int) '{') || (c == (int) '['))
1907 level++;
1908 else
1909 if ((c == (int) '}') || (c == (int) ']'))
1910 level--;
1911 if (level == 0)
1912 switch ((unsigned char) *expression)
1913 {
1914 case '~':
1915 case '!':
1916 {
1917 precedence=BitwiseComplementPrecedence;
1918 break;
1919 }
1920 case '^':
1921 {
1922 precedence=ExponentPrecedence;
1923 break;
1924 }
1925 default:
1926 {
1927 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1928 (strchr(")",c) != (char *) NULL))) &&
1929 (((islower((int) ((char) *expression)) != 0) ||
1930 (strchr("(",(int) *expression) != (char *) NULL)) ||
1931 ((isdigit((int) ((char) c)) == 0) &&
1932 (isdigit((int) ((char) *expression)) != 0))) &&
1933 (strchr("xy",(int) *expression) == (char *) NULL))
1934 precedence=MultiplyPrecedence;
1935 break;
1936 }
1937 case '*':
1938 case '/':
1939 case '%':
1940 {
1941 precedence=MultiplyPrecedence;
1942 break;
1943 }
1944 case '+':
1945 case '-':
1946 {
1947 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1948 (isalpha(c) != 0))
1949 precedence=AdditionPrecedence;
1950 break;
1951 }
1952 case LeftShiftOperator:
1953 case RightShiftOperator:
1954 {
1955 precedence=ShiftPrecedence;
1956 break;
1957 }
1958 case '<':
1959 case LessThanEqualOperator:
1960 case GreaterThanEqualOperator:
1961 case '>':
1962 {
1963 precedence=RelationalPrecedence;
1964 break;
1965 }
1966 case EqualOperator:
1967 case NotEqualOperator:
1968 {
1969 precedence=EquivalencyPrecedence;
1970 break;
1971 }
1972 case '&':
1973 {
1974 precedence=BitwiseAndPrecedence;
1975 break;
1976 }
1977 case '|':
1978 {
1979 precedence=BitwiseOrPrecedence;
1980 break;
1981 }
1982 case LogicalAndOperator:
1983 {
1984 precedence=LogicalAndPrecedence;
1985 break;
1986 }
1987 case LogicalOrOperator:
1988 {
1989 precedence=LogicalOrPrecedence;
1990 break;
1991 }
1992 case ':':
1993 case '?':
1994 {
1995 precedence=TernaryPrecedence;
1996 break;
1997 }
1998 case '=':
1999 {
2000 precedence=AssignmentPrecedence;
2001 break;
2002 }
2003 case ',':
2004 {
2005 precedence=CommaPrecedence;
2006 break;
2007 }
2008 case ';':
2009 {
2010 precedence=SeparatorPrecedence;
2011 break;
2012 }
2013 }
2014 if ((precedence == BitwiseComplementPrecedence) ||
2015 (precedence == TernaryPrecedence) ||
2016 (precedence == AssignmentPrecedence))
2017 {
2018 if (precedence > target)
2019 {
2020 /*
2021 Right-to-left associativity.
2022 */
2023 target=precedence;
2024 subexpression=expression;
2025 }
2026 }
2027 else
2028 if (precedence >= target)
2029 {
2030 /*
2031 Left-to-right associativity.
2032 */
2033 target=precedence;
2034 subexpression=expression;
2035 }
2036 if (strchr("(",(int) *expression) != (char *) NULL)
2037 expression=FxSubexpression(expression,exception);
2038 c=(int) (*expression++);
2039 }
2040 return(subexpression);
2041}
2042
2043static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
2044 const ChannelType channel,const long x,const long y,const char *expression,
2045 MagickRealType *beta,ExceptionInfo *exception)
2046{
2047 char
2048 *q,
2049 subexpression[MaxTextExtent];
2050
2051 MagickRealType
2052 alpha,
2053 gamma;
2054
2055 register const char
2056 *p;
2057
2058 *beta=0.0;
2059 if (exception->severity != UndefinedException)
2060 return(0.0);
2061 while (isspace((int) *expression) != 0)
2062 expression++;
2063 if (*expression == '\0')
2064 {
2065 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2066 "MissingExpression","`%s'",expression);
2067 return(0.0);
2068 }
2069 p=FxOperatorPrecedence(expression,exception);
2070 if (p != (const char *) NULL)
2071 {
2072 (void) CopyMagickString(subexpression,expression,(size_t)
2073 (p-expression+1));
2074 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2075 exception);
2076 switch ((unsigned char) *p)
2077 {
2078 case '~':
2079 {
2080 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2081 *beta=(MagickRealType) (~(unsigned long) *beta);
2082 return(*beta);
2083 }
2084 case '!':
2085 {
2086 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2087 return(*beta == 0.0 ? 1.0 : 0.0);
2088 }
2089 case '^':
2090 {
2091 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2092 channel,x,y,++p,beta,exception));
2093 return(*beta);
2094 }
2095 case '*':
2096 {
2097 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2098 return(alpha*(*beta));
2099 }
2100 case '/':
2101 {
2102 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2103 if (*beta == 0.0)
2104 {
2105 if (exception->severity == UndefinedException)
2106 (void) ThrowMagickException(exception,GetMagickModule(),
2107 OptionError,"DivideByZero","`%s'",expression);
2108 return(0.0);
2109 }
2110 return(alpha/(*beta));
2111 }
2112 case '%':
2113 {
2114 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2115 *beta=fabs(floor(((double) *beta)+0.5));
2116 if (*beta == 0.0)
2117 {
2118 (void) ThrowMagickException(exception,GetMagickModule(),
2119 OptionError,"DivideByZero","`%s'",expression);
2120 return(0.0);
2121 }
2122 return(fmod((double) alpha,(double) *beta));
2123 }
2124 case '+':
2125 {
2126 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2127 return(alpha+(*beta));
2128 }
2129 case '-':
2130 {
2131 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2132 return(alpha-(*beta));
2133 }
2134 case LeftShiftOperator:
2135 {
2136 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2137 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) << (unsigned long)
2138 (gamma+0.5));
2139 return(*beta);
2140 }
2141 case RightShiftOperator:
2142 {
2143 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2144 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) >> (unsigned long)
2145 (gamma+0.5));
2146 return(*beta);
2147 }
2148 case '<':
2149 {
2150 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2151 return(alpha < *beta ? 1.0 : 0.0);
2152 }
2153 case LessThanEqualOperator:
2154 {
2155 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2156 return(alpha <= *beta ? 1.0 : 0.0);
2157 }
2158 case '>':
2159 {
2160 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2161 return(alpha > *beta ? 1.0 : 0.0);
2162 }
2163 case GreaterThanEqualOperator:
2164 {
2165 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2166 return(alpha >= *beta ? 1.0 : 0.0);
2167 }
2168 case EqualOperator:
2169 {
2170 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2171 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2172 }
2173 case NotEqualOperator:
2174 {
2175 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2176 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2177 }
2178 case '&':
2179 {
2180 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2181 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) & (unsigned long)
2182 (gamma+0.5));
2183 return(*beta);
2184 }
2185 case '|':
2186 {
2187 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2188 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) | (unsigned long)
2189 (gamma+0.5));
2190 return(*beta);
2191 }
2192 case LogicalAndOperator:
2193 {
2194 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2195 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2196 return(*beta);
2197 }
2198 case LogicalOrOperator:
2199 {
2200 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2201 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2202 return(*beta);
2203 }
2204 case '?':
2205 {
2206 MagickRealType
2207 gamma;
2208
2209 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2210 q=subexpression;
2211 p=StringToken(":",&q);
2212 if (q == (char *) NULL)
2213 {
2214 (void) ThrowMagickException(exception,GetMagickModule(),
2215 OptionError,"UnableToParseExpression","`%s'",subexpression);
2216 return(0.0);
2217 }
2218 if (fabs((double) alpha) > MagickEpsilon)
2219 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2220 else
2221 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2222 return(gamma);
2223 }
2224 case '=':
2225 {
2226 char
2227 numeric[MaxTextExtent];
2228
2229 q=subexpression;
2230 while (isalpha((int) ((unsigned char) *q)) != 0)
2231 q++;
2232 if (*q != '\0')
2233 {
2234 (void) ThrowMagickException(exception,GetMagickModule(),
2235 OptionError,"UnableToParseExpression","`%s'",subexpression);
2236 return(0.0);
2237 }
2238 ClearMagickException(exception);
2239 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristye7f51092010-01-17 00:39:37 +00002240 (void) FormatMagickString(numeric,MaxTextExtent,"%g",(double)
cristy8cd5b312010-01-07 01:10:24 +00002241 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002242 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2243 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2244 subexpression),ConstantString(numeric));
2245 return(*beta);
2246 }
2247 case ',':
2248 {
2249 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2250 return(alpha);
2251 }
2252 case ';':
2253 {
2254 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2255 return(*beta);
2256 }
2257 default:
2258 {
2259 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2260 exception);
2261 return(gamma);
2262 }
2263 }
2264 }
2265 if (strchr("(",(int) *expression) != (char *) NULL)
2266 {
2267 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2268 subexpression[strlen(subexpression)-1]='\0';
2269 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2270 exception);
2271 return(gamma);
2272 }
2273 switch (*expression)
2274 {
2275 case '+':
2276 {
2277 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2278 exception);
2279 return(1.0*gamma);
2280 }
2281 case '-':
2282 {
2283 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2284 exception);
2285 return(-1.0*gamma);
2286 }
2287 case '~':
2288 {
2289 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2290 exception);
2291 return((MagickRealType) (~(unsigned long) (gamma+0.5)));
2292 }
2293 case 'A':
2294 case 'a':
2295 {
2296 if (LocaleNCompare(expression,"abs",3) == 0)
2297 {
2298 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2299 exception);
2300 return((MagickRealType) fabs((double) alpha));
2301 }
2302 if (LocaleNCompare(expression,"acos",4) == 0)
2303 {
2304 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2305 exception);
2306 return((MagickRealType) acos((double) alpha));
2307 }
cristy43c22f42010-03-30 12:34:07 +00002308#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002309 if (LocaleNCompare(expression,"airy",4) == 0)
2310 {
2311 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2312 exception);
2313 if (alpha == 0.0)
cristy2dd03222010-03-30 22:12:11 +00002314 return(1.0);
2315 gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
cristy43c22f42010-03-30 12:34:07 +00002316 return(gamma*gamma);
cristyee56cf12010-03-01 22:17:06 +00002317 }
cristy43c22f42010-03-30 12:34:07 +00002318#endif
cristy3ed852e2009-09-05 21:47:34 +00002319 if (LocaleNCompare(expression,"asin",4) == 0)
2320 {
2321 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2322 exception);
2323 return((MagickRealType) asin((double) alpha));
2324 }
2325 if (LocaleNCompare(expression,"alt",3) == 0)
2326 {
2327 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2328 exception);
2329 return(((long) alpha) & 0x01 ? -1.0 : 1.0);
2330 }
2331 if (LocaleNCompare(expression,"atan2",5) == 0)
2332 {
2333 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2334 exception);
2335 return((MagickRealType) atan2((double) alpha,(double) *beta));
2336 }
2337 if (LocaleNCompare(expression,"atan",4) == 0)
2338 {
2339 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2340 exception);
2341 return((MagickRealType) atan((double) alpha));
2342 }
2343 if (LocaleCompare(expression,"a") == 0)
2344 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2345 break;
2346 }
2347 case 'B':
2348 case 'b':
2349 {
2350 if (LocaleCompare(expression,"b") == 0)
2351 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2352 break;
2353 }
2354 case 'C':
2355 case 'c':
2356 {
2357 if (LocaleNCompare(expression,"ceil",4) == 0)
2358 {
2359 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2360 exception);
2361 return((MagickRealType) ceil((double) alpha));
2362 }
2363 if (LocaleNCompare(expression,"cosh",4) == 0)
2364 {
2365 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2366 exception);
2367 return((MagickRealType) cosh((double) alpha));
2368 }
2369 if (LocaleNCompare(expression,"cos",3) == 0)
2370 {
2371 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2372 exception);
2373 return((MagickRealType) cos((double) alpha));
2374 }
2375 if (LocaleCompare(expression,"c") == 0)
2376 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2377 break;
2378 }
2379 case 'D':
2380 case 'd':
2381 {
2382 if (LocaleNCompare(expression,"debug",5) == 0)
2383 {
2384 const char
2385 *type;
2386
2387 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2388 exception);
2389 if (fx_info->images->colorspace == CMYKColorspace)
2390 switch (channel)
2391 {
2392 case CyanChannel: type="cyan"; break;
2393 case MagentaChannel: type="magenta"; break;
2394 case YellowChannel: type="yellow"; break;
2395 case OpacityChannel: type="opacity"; break;
2396 case BlackChannel: type="black"; break;
2397 default: type="unknown"; break;
2398 }
2399 else
2400 switch (channel)
2401 {
2402 case RedChannel: type="red"; break;
2403 case GreenChannel: type="green"; break;
2404 case BlueChannel: type="blue"; break;
2405 case OpacityChannel: type="opacity"; break;
2406 default: type="unknown"; break;
2407 }
2408 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2409 if (strlen(subexpression) > 1)
2410 subexpression[strlen(subexpression)-1]='\0';
2411 if (fx_info->file != (FILE *) NULL)
cristy4bfde7e2010-01-17 03:18:37 +00002412 (void) fprintf(fx_info->file,"%s[%ld,%ld].%s: %s=%.*g\n",
cristye7f51092010-01-17 00:39:37 +00002413 fx_info->images->filename,x,y,type,subexpression,
2414 GetMagickPrecision(),(double) alpha);
cristy3ed852e2009-09-05 21:47:34 +00002415 return(0.0);
2416 }
2417 break;
2418 }
2419 case 'E':
2420 case 'e':
2421 {
2422 if (LocaleCompare(expression,"epsilon") == 0)
2423 return((MagickRealType) MagickEpsilon);
2424 if (LocaleNCompare(expression,"exp",3) == 0)
2425 {
2426 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2427 exception);
2428 return((MagickRealType) exp((double) alpha));
2429 }
2430 if (LocaleCompare(expression,"e") == 0)
2431 return((MagickRealType) 2.7182818284590452354);
2432 break;
2433 }
2434 case 'F':
2435 case 'f':
2436 {
2437 if (LocaleNCompare(expression,"floor",5) == 0)
2438 {
2439 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2440 exception);
2441 return((MagickRealType) floor((double) alpha));
2442 }
2443 break;
2444 }
2445 case 'G':
2446 case 'g':
2447 {
2448 if (LocaleCompare(expression,"g") == 0)
2449 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2450 break;
2451 }
2452 case 'H':
2453 case 'h':
2454 {
2455 if (LocaleCompare(expression,"h") == 0)
2456 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2457 if (LocaleCompare(expression,"hue") == 0)
2458 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2459 if (LocaleNCompare(expression,"hypot",5) == 0)
2460 {
2461 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2462 exception);
2463 return((MagickRealType) hypot((double) alpha,(double) *beta));
2464 }
2465 break;
2466 }
2467 case 'K':
2468 case 'k':
2469 {
2470 if (LocaleCompare(expression,"k") == 0)
2471 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2472 break;
2473 }
2474 case 'I':
2475 case 'i':
2476 {
2477 if (LocaleCompare(expression,"intensity") == 0)
2478 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2479 if (LocaleNCompare(expression,"int",3) == 0)
2480 {
2481 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2482 exception);
2483 return((MagickRealType) floor(alpha+0.5));
2484 }
2485 if (LocaleCompare(expression,"i") == 0)
2486 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2487 break;
2488 }
2489 case 'J':
2490 case 'j':
2491 {
2492 if (LocaleCompare(expression,"j") == 0)
2493 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
cristy161b9262010-03-20 19:34:32 +00002494#if defined(MAGICKCORE_HAVE_J0)
cristyee56cf12010-03-01 22:17:06 +00002495 if (LocaleNCompare(expression,"j0",2) == 0)
2496 {
2497 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2498 exception);
2499 return((MagickRealType) j0((double) alpha));
2500 }
cristy161b9262010-03-20 19:34:32 +00002501#endif
2502#if defined(MAGICKCORE_HAVE_J1)
cristyee56cf12010-03-01 22:17:06 +00002503 if (LocaleNCompare(expression,"j1",2) == 0)
2504 {
2505 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2506 exception);
2507 return((MagickRealType) j1((double) alpha));
2508 }
cristy161b9262010-03-20 19:34:32 +00002509#endif
cristya6a09e72010-03-02 14:51:02 +00002510 if (LocaleNCompare(expression,"jinc",4) == 0)
2511 {
2512 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2513 exception);
cristy0946a822010-03-12 17:14:58 +00002514 if (alpha == 0.0)
2515 return(1.0);
cristy69928f92010-03-12 13:27:09 +00002516 gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/
cristyfce2f7b2010-03-12 00:29:49 +00002517 (MagickPI*alpha));
2518 return(gamma);
cristya6a09e72010-03-02 14:51:02 +00002519 }
cristy3ed852e2009-09-05 21:47:34 +00002520 break;
2521 }
2522 case 'L':
2523 case 'l':
2524 {
2525 if (LocaleNCompare(expression,"ln",2) == 0)
2526 {
2527 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2528 exception);
2529 return((MagickRealType) log((double) alpha));
2530 }
2531 if (LocaleNCompare(expression,"logtwo",4) == 0)
2532 {
2533 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2534 exception);
2535 return((MagickRealType) log10((double) alpha))/log10(2.0);
2536 }
2537 if (LocaleNCompare(expression,"log",3) == 0)
2538 {
2539 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2540 exception);
2541 return((MagickRealType) log10((double) alpha));
2542 }
2543 if (LocaleCompare(expression,"lightness") == 0)
2544 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2545 break;
2546 }
2547 case 'M':
2548 case 'm':
2549 {
2550 if (LocaleCompare(expression,"MaxRGB") == 0)
2551 return((MagickRealType) QuantumRange);
2552 if (LocaleNCompare(expression,"maxima",6) == 0)
2553 break;
2554 if (LocaleNCompare(expression,"max",3) == 0)
2555 return(FxMax(fx_info,channel,x,y,expression+3,exception));
2556 if (LocaleNCompare(expression,"minima",6) == 0)
2557 break;
2558 if (LocaleNCompare(expression,"min",3) == 0)
2559 return(FxMin(fx_info,channel,x,y,expression+3,exception));
2560 if (LocaleNCompare(expression,"mod",3) == 0)
2561 {
2562 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2563 exception);
2564 return((MagickRealType) fmod((double) alpha,(double) *beta));
2565 }
2566 if (LocaleCompare(expression,"m") == 0)
2567 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2568 break;
2569 }
2570 case 'N':
2571 case 'n':
2572 {
2573 if (LocaleCompare(expression,"n") == 0)
2574 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2575 break;
2576 }
2577 case 'O':
2578 case 'o':
2579 {
2580 if (LocaleCompare(expression,"Opaque") == 0)
2581 return(1.0);
2582 if (LocaleCompare(expression,"o") == 0)
2583 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2584 break;
2585 }
2586 case 'P':
2587 case 'p':
2588 {
2589 if (LocaleCompare(expression,"pi") == 0)
2590 return((MagickRealType) MagickPI);
2591 if (LocaleNCompare(expression,"pow",3) == 0)
2592 {
2593 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2594 exception);
2595 return((MagickRealType) pow((double) alpha,(double) *beta));
2596 }
2597 if (LocaleCompare(expression,"p") == 0)
2598 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2599 break;
2600 }
2601 case 'Q':
2602 case 'q':
2603 {
2604 if (LocaleCompare(expression,"QuantumRange") == 0)
2605 return((MagickRealType) QuantumRange);
2606 if (LocaleCompare(expression,"QuantumScale") == 0)
2607 return((MagickRealType) QuantumScale);
2608 break;
2609 }
2610 case 'R':
2611 case 'r':
2612 {
2613 if (LocaleNCompare(expression,"rand",4) == 0)
2614 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2615 if (LocaleNCompare(expression,"round",5) == 0)
2616 {
2617 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2618 exception);
2619 if (alpha >= 0.0)
2620 return((MagickRealType) floor((double) alpha+0.5));
2621 return((MagickRealType) ceil((double) alpha-0.5));
2622 }
2623 if (LocaleCompare(expression,"r") == 0)
2624 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2625 break;
2626 }
2627 case 'S':
2628 case 's':
2629 {
2630 if (LocaleCompare(expression,"saturation") == 0)
2631 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2632 if (LocaleNCompare(expression,"sign",4) == 0)
2633 {
2634 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2635 exception);
2636 return(alpha < 0.0 ? -1.0 : 1.0);
2637 }
cristya6a09e72010-03-02 14:51:02 +00002638 if (LocaleNCompare(expression,"sinc",4) == 0)
2639 {
2640 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2641 exception);
2642 if (alpha == 0)
2643 return(1.0);
2644 gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2645 (MagickPI*alpha));
2646 return(gamma);
2647 }
cristy3ed852e2009-09-05 21:47:34 +00002648 if (LocaleNCompare(expression,"sinh",4) == 0)
2649 {
2650 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2651 exception);
2652 return((MagickRealType) sinh((double) alpha));
2653 }
2654 if (LocaleNCompare(expression,"sin",3) == 0)
2655 {
2656 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2657 exception);
2658 return((MagickRealType) sin((double) alpha));
2659 }
2660 if (LocaleNCompare(expression,"sqrt",4) == 0)
2661 {
2662 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2663 exception);
2664 return((MagickRealType) sqrt((double) alpha));
2665 }
2666 if (LocaleCompare(expression,"s") == 0)
2667 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2668 break;
2669 }
2670 case 'T':
2671 case 't':
2672 {
2673 if (LocaleNCompare(expression,"tanh",4) == 0)
2674 {
2675 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2676 exception);
2677 return((MagickRealType) tanh((double) alpha));
2678 }
2679 if (LocaleNCompare(expression,"tan",3) == 0)
2680 {
2681 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2682 exception);
2683 return((MagickRealType) tan((double) alpha));
2684 }
2685 if (LocaleCompare(expression,"Transparent") == 0)
2686 return(0.0);
2687 if (LocaleCompare(expression,"t") == 0)
2688 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2689 break;
2690 }
2691 case 'U':
2692 case 'u':
2693 {
2694 if (LocaleCompare(expression,"u") == 0)
2695 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2696 break;
2697 }
2698 case 'V':
2699 case 'v':
2700 {
2701 if (LocaleCompare(expression,"v") == 0)
2702 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2703 break;
2704 }
2705 case 'W':
2706 case 'w':
2707 {
2708 if (LocaleCompare(expression,"w") == 0)
2709 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2710 break;
2711 }
2712 case 'Y':
2713 case 'y':
2714 {
2715 if (LocaleCompare(expression,"y") == 0)
2716 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2717 break;
2718 }
2719 case 'Z':
2720 case 'z':
2721 {
2722 if (LocaleCompare(expression,"z") == 0)
2723 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2724 break;
2725 }
2726 default:
2727 break;
2728 }
2729 q=(char *) expression;
2730 alpha=strtod(expression,&q);
2731 if (q == expression)
2732 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2733 return(alpha);
2734}
2735
2736MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2737 MagickRealType *alpha,ExceptionInfo *exception)
2738{
2739 MagickBooleanType
2740 status;
2741
2742 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2743 return(status);
2744}
2745
2746MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2747 MagickRealType *alpha,ExceptionInfo *exception)
2748{
2749 FILE
2750 *file;
2751
2752 MagickBooleanType
2753 status;
2754
2755 file=fx_info->file;
2756 fx_info->file=(FILE *) NULL;
2757 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2758 fx_info->file=file;
2759 return(status);
2760}
2761
2762MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
2763 const ChannelType channel,const long x,const long y,MagickRealType *alpha,
2764 ExceptionInfo *exception)
2765{
2766 MagickRealType
2767 beta;
2768
2769 beta=0.0;
2770 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2771 exception);
2772 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2773}
2774
2775/*
2776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2777% %
2778% %
2779% %
2780% F x I m a g e %
2781% %
2782% %
2783% %
2784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2785%
2786% FxImage() applies a mathematical expression to the specified image.
2787%
2788% The format of the FxImage method is:
2789%
2790% Image *FxImage(const Image *image,const char *expression,
2791% ExceptionInfo *exception)
2792% Image *FxImageChannel(const Image *image,const ChannelType channel,
2793% const char *expression,ExceptionInfo *exception)
2794%
2795% A description of each parameter follows:
2796%
2797% o image: the image.
2798%
2799% o channel: the channel.
2800%
2801% o expression: A mathematical expression.
2802%
2803% o exception: return any errors or warnings in this structure.
2804%
2805*/
2806
2807static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2808{
2809 register long
2810 i;
2811
2812 assert(fx_info != (FxInfo **) NULL);
2813 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
2814 if (fx_info[i] != (FxInfo *) NULL)
2815 fx_info[i]=DestroyFxInfo(fx_info[i]);
2816 fx_info=(FxInfo **) RelinquishAlignedMemory(fx_info);
2817 return(fx_info);
2818}
2819
2820static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2821 ExceptionInfo *exception)
2822{
2823 char
2824 *fx_expression;
2825
2826 FxInfo
2827 **fx_info;
2828
2829 MagickRealType
2830 alpha;
2831
2832 register long
2833 i;
2834
2835 unsigned long
2836 number_threads;
2837
2838 number_threads=GetOpenMPMaximumThreads();
2839 fx_info=(FxInfo **) AcquireAlignedMemory(number_threads,sizeof(*fx_info));
2840 if (fx_info == (FxInfo **) NULL)
2841 return((FxInfo **) NULL);
2842 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2843 if (*expression != '@')
2844 fx_expression=ConstantString(expression);
2845 else
2846 fx_expression=FileToString(expression+1,~0,exception);
2847 for (i=0; i < (long) number_threads; i++)
2848 {
2849 fx_info[i]=AcquireFxInfo(image,fx_expression);
2850 if (fx_info[i] == (FxInfo *) NULL)
2851 return(DestroyFxThreadSet(fx_info));
2852 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2853 }
2854 fx_expression=DestroyString(fx_expression);
2855 return(fx_info);
2856}
2857
2858MagickExport Image *FxImage(const Image *image,const char *expression,
2859 ExceptionInfo *exception)
2860{
2861 Image
2862 *fx_image;
2863
2864 fx_image=FxImageChannel(image,GrayChannel,expression,exception);
2865 return(fx_image);
2866}
2867
2868MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
2869 const char *expression,ExceptionInfo *exception)
2870{
2871#define FxImageTag "Fx/Image"
2872
cristyfa112112010-01-04 17:48:07 +00002873 CacheView
2874 *fx_view;
2875
cristy3ed852e2009-09-05 21:47:34 +00002876 FxInfo
cristyfa112112010-01-04 17:48:07 +00002877 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00002878
2879 Image
2880 *fx_image;
2881
2882 long
2883 progress,
2884 y;
2885
2886 MagickBooleanType
2887 status;
2888
2889 MagickRealType
2890 alpha;
2891
cristy3ed852e2009-09-05 21:47:34 +00002892 assert(image != (Image *) NULL);
2893 assert(image->signature == MagickSignature);
2894 if (image->debug != MagickFalse)
2895 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2896 fx_image=CloneImage(image,0,0,MagickTrue,exception);
2897 if (fx_image == (Image *) NULL)
2898 return((Image *) NULL);
2899 if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
2900 {
2901 InheritException(exception,&fx_image->exception);
2902 fx_image=DestroyImage(fx_image);
2903 return((Image *) NULL);
2904 }
2905 fx_info=AcquireFxThreadSet(image,expression,exception);
2906 if (fx_info == (FxInfo **) NULL)
2907 {
2908 fx_image=DestroyImage(fx_image);
2909 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2910 }
2911 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
2912 if (status == MagickFalse)
2913 {
2914 fx_image=DestroyImage(fx_image);
2915 fx_info=DestroyFxThreadSet(fx_info);
2916 return((Image *) NULL);
2917 }
2918 /*
2919 Fx image.
2920 */
2921 status=MagickTrue;
2922 progress=0;
2923 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00002924#if defined(MAGICKCORE_OPENMP_SUPPORT)
2925 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002926#endif
2927 for (y=0; y < (long) fx_image->rows; y++)
2928 {
2929 MagickRealType
2930 alpha;
2931
2932 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002933 *restrict fx_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002934
2935 register long
2936 id,
2937 x;
2938
2939 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002940 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002941
2942 if (status == MagickFalse)
2943 continue;
2944 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
2945 if (q == (PixelPacket *) NULL)
2946 {
2947 status=MagickFalse;
2948 continue;
2949 }
2950 fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
2951 id=GetOpenMPThreadId();
2952 alpha=0.0;
2953 for (x=0; x < (long) fx_image->columns; x++)
2954 {
2955 if ((channel & RedChannel) != 0)
2956 {
2957 (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
2958 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00002959 q->red=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00002960 }
2961 if ((channel & GreenChannel) != 0)
2962 {
2963 (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
2964 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00002965 q->green=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00002966 }
2967 if ((channel & BlueChannel) != 0)
2968 {
2969 (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
2970 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00002971 q->blue=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00002972 }
2973 if ((channel & OpacityChannel) != 0)
2974 {
2975 (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
2976 &alpha,exception);
2977 if (image->matte == MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002978 q->opacity=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00002979 else
cristyce70c172010-01-07 17:15:30 +00002980 q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
cristy3ed852e2009-09-05 21:47:34 +00002981 QuantumRange*alpha));
2982 }
2983 if (((channel & IndexChannel) != 0) &&
2984 (fx_image->colorspace == CMYKColorspace))
2985 {
2986 (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
2987 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00002988 fx_indexes[x]=(IndexPacket) ClampToQuantum((MagickRealType)
cristy3ed852e2009-09-05 21:47:34 +00002989 QuantumRange*alpha);
2990 }
2991 q++;
2992 }
2993 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
2994 status=MagickFalse;
2995 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2996 {
2997 MagickBooleanType
2998 proceed;
2999
cristyb5d5f722009-11-04 03:03:49 +00003000#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003001 #pragma omp critical (MagickCore_FxImageChannel)
3002#endif
3003 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3004 if (proceed == MagickFalse)
3005 status=MagickFalse;
3006 }
3007 }
3008 fx_image->matte=fx_info[0]->matte;
3009 fx_view=DestroyCacheView(fx_view);
3010 fx_info=DestroyFxThreadSet(fx_info);
3011 if (status == MagickFalse)
3012 fx_image=DestroyImage(fx_image);
3013 return(fx_image);
3014}
3015
3016/*
3017%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3018% %
3019% %
3020% %
3021% I m p l o d e I m a g e %
3022% %
3023% %
3024% %
3025%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3026%
3027% ImplodeImage() creates a new image that is a copy of an existing
3028% one with the image pixels "implode" by the specified percentage. It
3029% allocates the memory necessary for the new Image structure and returns a
3030% pointer to the new image.
3031%
3032% The format of the ImplodeImage method is:
3033%
3034% Image *ImplodeImage(const Image *image,const double amount,
3035% ExceptionInfo *exception)
3036%
3037% A description of each parameter follows:
3038%
3039% o implode_image: Method ImplodeImage returns a pointer to the image
3040% after it is implode. A null image is returned if there is a memory
3041% shortage.
3042%
3043% o image: the image.
3044%
3045% o amount: Define the extent of the implosion.
3046%
3047% o exception: return any errors or warnings in this structure.
3048%
3049*/
3050MagickExport Image *ImplodeImage(const Image *image,const double amount,
3051 ExceptionInfo *exception)
3052{
3053#define ImplodeImageTag "Implode/Image"
3054
cristyfa112112010-01-04 17:48:07 +00003055 CacheView
3056 *image_view,
3057 *implode_view;
3058
cristy3ed852e2009-09-05 21:47:34 +00003059 Image
3060 *implode_image;
3061
3062 long
3063 progress,
3064 y;
3065
3066 MagickBooleanType
3067 status;
3068
3069 MagickPixelPacket
3070 zero;
3071
3072 MagickRealType
3073 radius;
3074
3075 PointInfo
3076 center,
3077 scale;
3078
3079 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00003080 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00003081
3082 /*
3083 Initialize implode image attributes.
3084 */
3085 assert(image != (Image *) NULL);
3086 assert(image->signature == MagickSignature);
3087 if (image->debug != MagickFalse)
3088 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3089 assert(exception != (ExceptionInfo *) NULL);
3090 assert(exception->signature == MagickSignature);
3091 implode_image=CloneImage(image,0,0,MagickTrue,exception);
3092 if (implode_image == (Image *) NULL)
3093 return((Image *) NULL);
3094 if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
3095 {
3096 InheritException(exception,&implode_image->exception);
3097 implode_image=DestroyImage(implode_image);
3098 return((Image *) NULL);
3099 }
3100 if (implode_image->background_color.opacity != OpaqueOpacity)
3101 implode_image->matte=MagickTrue;
3102 /*
3103 Compute scaling factor.
3104 */
3105 scale.x=1.0;
3106 scale.y=1.0;
3107 center.x=0.5*image->columns;
3108 center.y=0.5*image->rows;
3109 radius=center.x;
3110 if (image->columns > image->rows)
3111 scale.y=(double) image->columns/(double) image->rows;
3112 else
3113 if (image->columns < image->rows)
3114 {
3115 scale.x=(double) image->rows/(double) image->columns;
3116 radius=center.y;
3117 }
3118 /*
3119 Implode image.
3120 */
3121 status=MagickTrue;
3122 progress=0;
3123 GetMagickPixelPacket(implode_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00003124 resample_filter=AcquireResampleFilterThreadSet(image,
3125 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003126 image_view=AcquireCacheView(image);
3127 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003128#if defined(MAGICKCORE_OPENMP_SUPPORT)
3129 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003130#endif
3131 for (y=0; y < (long) image->rows; y++)
3132 {
3133 MagickPixelPacket
3134 pixel;
3135
3136 MagickRealType
3137 distance;
3138
3139 PointInfo
3140 delta;
3141
3142 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003143 *restrict implode_indexes;
cristy3ed852e2009-09-05 21:47:34 +00003144
3145 register long
3146 id,
3147 x;
3148
3149 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003150 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003151
3152 if (status == MagickFalse)
3153 continue;
3154 q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3155 exception);
3156 if (q == (PixelPacket *) NULL)
3157 {
3158 status=MagickFalse;
3159 continue;
3160 }
3161 implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
3162 delta.y=scale.y*(double) (y-center.y);
3163 pixel=zero;
3164 id=GetOpenMPThreadId();
3165 for (x=0; x < (long) image->columns; x++)
3166 {
3167 /*
3168 Determine if the pixel is within an ellipse.
3169 */
3170 delta.x=scale.x*(double) (x-center.x);
3171 distance=delta.x*delta.x+delta.y*delta.y;
3172 if (distance < (radius*radius))
3173 {
3174 double
3175 factor;
3176
3177 /*
3178 Implode the pixel.
3179 */
3180 factor=1.0;
3181 if (distance > 0.0)
3182 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3183 radius/2)),-amount);
3184 (void) ResamplePixelColor(resample_filter[id],(double)
3185 (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3186 scale.y+center.y),&pixel);
3187 SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
3188 }
3189 q++;
3190 }
3191 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3192 status=MagickFalse;
3193 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3194 {
3195 MagickBooleanType
3196 proceed;
3197
cristyb5d5f722009-11-04 03:03:49 +00003198#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003199 #pragma omp critical (MagickCore_ImplodeImage)
3200#endif
3201 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3202 if (proceed == MagickFalse)
3203 status=MagickFalse;
3204 }
3205 }
3206 implode_view=DestroyCacheView(implode_view);
3207 image_view=DestroyCacheView(image_view);
3208 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
3209 if (status == MagickFalse)
3210 implode_image=DestroyImage(implode_image);
3211 return(implode_image);
3212}
3213
3214/*
3215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3216% %
3217% %
3218% %
3219% M o r p h I m a g e s %
3220% %
3221% %
3222% %
3223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3224%
3225% The MorphImages() method requires a minimum of two images. The first
3226% image is transformed into the second by a number of intervening images
3227% as specified by frames.
3228%
3229% The format of the MorphImage method is:
3230%
3231% Image *MorphImages(const Image *image,const unsigned long number_frames,
3232% ExceptionInfo *exception)
3233%
3234% A description of each parameter follows:
3235%
3236% o image: the image.
3237%
3238% o number_frames: Define the number of in-between image to generate.
3239% The more in-between frames, the smoother the morph.
3240%
3241% o exception: return any errors or warnings in this structure.
3242%
3243*/
3244MagickExport Image *MorphImages(const Image *image,
3245 const unsigned long number_frames,ExceptionInfo *exception)
3246{
3247#define MorphImageTag "Morph/Image"
3248
3249 Image
3250 *morph_image,
3251 *morph_images;
3252
3253 long
3254 y;
3255
3256 MagickOffsetType
3257 scene;
3258
3259 MagickRealType
3260 alpha,
3261 beta;
3262
3263 register const Image
3264 *next;
3265
3266 register long
3267 i;
3268
3269 MagickBooleanType
3270 status;
3271
3272 /*
3273 Clone first frame in sequence.
3274 */
3275 assert(image != (Image *) NULL);
3276 assert(image->signature == MagickSignature);
3277 if (image->debug != MagickFalse)
3278 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3279 assert(exception != (ExceptionInfo *) NULL);
3280 assert(exception->signature == MagickSignature);
3281 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3282 if (morph_images == (Image *) NULL)
3283 return((Image *) NULL);
3284 if (GetNextImageInList(image) == (Image *) NULL)
3285 {
3286 /*
3287 Morph single image.
3288 */
3289 for (i=1; i < (long) number_frames; i++)
3290 {
3291 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3292 if (morph_image == (Image *) NULL)
3293 {
3294 morph_images=DestroyImageList(morph_images);
3295 return((Image *) NULL);
3296 }
3297 AppendImageToList(&morph_images,morph_image);
cristy8b27a6d2010-02-14 03:31:15 +00003298 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003299 {
cristy8b27a6d2010-02-14 03:31:15 +00003300 MagickBooleanType
3301 proceed;
3302
3303 proceed=SetImageProgress(image,MorphImageTag,i,number_frames);
3304 if (proceed == MagickFalse)
3305 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003306 }
3307 }
3308 return(GetFirstImageInList(morph_images));
3309 }
3310 /*
3311 Morph image sequence.
3312 */
3313 status=MagickTrue;
3314 scene=0;
3315 next=image;
3316 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3317 {
3318 for (i=0; i < (long) number_frames; i++)
3319 {
3320 CacheView
3321 *image_view,
3322 *morph_view;
3323
3324 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3325 alpha=1.0-beta;
3326 morph_image=ZoomImage(next,(unsigned long) (alpha*next->columns+beta*
3327 GetNextImageInList(next)->columns+0.5),(unsigned long) (alpha*
3328 next->rows+beta*GetNextImageInList(next)->rows+0.5),exception);
3329 if (morph_image == (Image *) NULL)
3330 {
3331 morph_images=DestroyImageList(morph_images);
3332 return((Image *) NULL);
3333 }
3334 if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
3335 {
3336 InheritException(exception,&morph_image->exception);
3337 morph_image=DestroyImage(morph_image);
3338 return((Image *) NULL);
3339 }
3340 AppendImageToList(&morph_images,morph_image);
3341 morph_images=GetLastImageInList(morph_images);
3342 morph_image=ZoomImage(GetNextImageInList(next),morph_images->columns,
3343 morph_images->rows,exception);
3344 if (morph_image == (Image *) NULL)
3345 {
3346 morph_images=DestroyImageList(morph_images);
3347 return((Image *) NULL);
3348 }
3349 image_view=AcquireCacheView(morph_image);
3350 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003351#if defined(MAGICKCORE_OPENMP_SUPPORT)
3352 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003353#endif
3354 for (y=0; y < (long) morph_images->rows; y++)
3355 {
3356 MagickBooleanType
3357 sync;
3358
3359 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003360 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003361
3362 register long
3363 x;
3364
3365 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003366 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003367
3368 if (status == MagickFalse)
3369 continue;
3370 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3371 exception);
3372 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3373 exception);
3374 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3375 {
3376 status=MagickFalse;
3377 continue;
3378 }
3379 for (x=0; x < (long) morph_images->columns; x++)
3380 {
cristyce70c172010-01-07 17:15:30 +00003381 q->red=ClampToQuantum(alpha*q->red+beta*GetRedPixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00003382 q->green=ClampToQuantum(alpha*q->green+beta*
3383 GetGreenPixelComponent(p));
cristyce70c172010-01-07 17:15:30 +00003384 q->blue=ClampToQuantum(alpha*q->blue+beta*GetBluePixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00003385 q->opacity=ClampToQuantum(alpha*q->opacity+beta*
3386 GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00003387 p++;
3388 q++;
3389 }
3390 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3391 if (sync == MagickFalse)
3392 status=MagickFalse;
3393 }
3394 morph_view=DestroyCacheView(morph_view);
3395 image_view=DestroyCacheView(image_view);
3396 morph_image=DestroyImage(morph_image);
3397 }
3398 if (i < (long) number_frames)
3399 break;
3400 /*
3401 Clone last frame in sequence.
3402 */
3403 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3404 if (morph_image == (Image *) NULL)
3405 {
3406 morph_images=DestroyImageList(morph_images);
3407 return((Image *) NULL);
3408 }
3409 AppendImageToList(&morph_images,morph_image);
3410 morph_images=GetLastImageInList(morph_images);
3411 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3412 {
3413 MagickBooleanType
3414 proceed;
3415
cristyb5d5f722009-11-04 03:03:49 +00003416#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003417 #pragma omp critical (MagickCore_MorphImages)
3418#endif
3419 proceed=SetImageProgress(image,MorphImageTag,scene,
3420 GetImageListLength(image));
3421 if (proceed == MagickFalse)
3422 status=MagickFalse;
3423 }
3424 scene++;
3425 }
3426 if (GetNextImageInList(next) != (Image *) NULL)
3427 {
3428 morph_images=DestroyImageList(morph_images);
3429 return((Image *) NULL);
3430 }
3431 return(GetFirstImageInList(morph_images));
3432}
3433
3434/*
3435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3436% %
3437% %
3438% %
3439% P l a s m a I m a g e %
3440% %
3441% %
3442% %
3443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3444%
3445% PlasmaImage() initializes an image with plasma fractal values. The image
3446% must be initialized with a base color and the random number generator
3447% seeded before this method is called.
3448%
3449% The format of the PlasmaImage method is:
3450%
3451% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
3452% unsigned long attenuate,unsigned long depth)
3453%
3454% A description of each parameter follows:
3455%
3456% o image: the image.
3457%
3458% o segment: Define the region to apply plasma fractals values.
3459%
3460% o attenuate: Define the plasmattenuation factor.
3461%
3462% o depth: Limit the plasma recursion depth.
3463%
3464*/
3465
3466static inline Quantum PlasmaPixel(RandomInfo *random_info,
3467 const MagickRealType pixel,const MagickRealType noise)
3468{
3469 Quantum
3470 plasma;
3471
cristyce70c172010-01-07 17:15:30 +00003472 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003473 noise/2.0);
3474 return(plasma);
3475}
3476
3477MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
3478 RandomInfo *random_info,const SegmentInfo *segment,unsigned long attenuate,
3479 unsigned long depth)
3480{
3481 ExceptionInfo
3482 *exception;
3483
3484 long
3485 x,
3486 x_mid,
3487 y,
3488 y_mid;
3489
3490 MagickRealType
3491 plasma;
3492
3493 PixelPacket
3494 u,
3495 v;
3496
3497 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3498 return(MagickTrue);
3499 if (depth != 0)
3500 {
3501 SegmentInfo
3502 local_info;
3503
3504 /*
3505 Divide the area into quadrants and recurse.
3506 */
3507 depth--;
3508 attenuate++;
cristy254181b2010-03-18 01:08:51 +00003509 x_mid=(long) ceil((segment->x1+segment->x2)/2-0.5);
3510 y_mid=(long) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003511 local_info=(*segment);
3512 local_info.x2=(double) x_mid;
3513 local_info.y2=(double) y_mid;
3514 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3515 local_info=(*segment);
3516 local_info.y1=(double) y_mid;
3517 local_info.x2=(double) x_mid;
3518 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3519 local_info=(*segment);
3520 local_info.x1=(double) x_mid;
3521 local_info.y2=(double) y_mid;
3522 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3523 local_info=(*segment);
3524 local_info.x1=(double) x_mid;
3525 local_info.y1=(double) y_mid;
3526 return(PlasmaImageProxy(image,random_info,&local_info,attenuate,depth));
3527 }
3528 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
3529 return(MagickFalse);
cristy254181b2010-03-18 01:08:51 +00003530 x_mid=(long) ceil((segment->x1+segment->x2)/2-0.5);
3531 y_mid=(long) ceil((segment->y1+segment->y2)/2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003532 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3533 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3534 return(MagickFalse);
3535 /*
3536 Average pixels and apply plasma.
3537 */
3538 exception=(&image->exception);
3539 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3540 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3541 {
3542 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003543 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003544
3545 /*
3546 Left pixel.
3547 */
cristy06609ee2010-03-17 20:21:27 +00003548 x=(long) ceil(segment->x1-0.5);
3549 (void) GetOneVirtualPixel(image,x,(long) ceil(segment->y1-0.5),&u,
3550 exception);
3551 (void) GetOneVirtualPixel(image,x,(long) ceil(segment->y2-0.5),&v,
3552 exception);
cristy3ed852e2009-09-05 21:47:34 +00003553 q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
3554 if (q == (PixelPacket *) NULL)
3555 return(MagickTrue);
3556 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3557 plasma);
3558 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3559 plasma);
3560 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3561 plasma);
3562 (void) SyncAuthenticPixels(image,exception);
3563 if (segment->x1 != segment->x2)
3564 {
3565 /*
3566 Right pixel.
3567 */
cristy06609ee2010-03-17 20:21:27 +00003568 x=(long) ceil(segment->x2-0.5);
3569 (void) GetOneVirtualPixel(image,x,(long) ceil(segment->y1-0.5),&u,
cristy3ed852e2009-09-05 21:47:34 +00003570 exception);
cristy06609ee2010-03-17 20:21:27 +00003571 (void) GetOneVirtualPixel(image,x,(long) ceil(segment->y2-0.5),&v,
cristy3ed852e2009-09-05 21:47:34 +00003572 exception);
3573 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)/
3579 2.0,plasma);
3580 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3581 plasma);
3582 (void) SyncAuthenticPixels(image,exception);
3583 }
3584 }
3585 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3586 {
3587 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3588 {
3589 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003590 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003591
3592 /*
3593 Bottom pixel.
3594 */
cristy06609ee2010-03-17 20:21:27 +00003595 y=(long) ceil(segment->y2-0.5);
3596 (void) GetOneVirtualPixel(image,(long) ceil(segment->x1-0.5),y,&u,
cristy3ed852e2009-09-05 21:47:34 +00003597 exception);
cristy06609ee2010-03-17 20:21:27 +00003598 (void) GetOneVirtualPixel(image,(long) ceil(segment->x2-0.5),y,&v,
cristy3ed852e2009-09-05 21:47:34 +00003599 exception);
3600 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3601 if (q == (PixelPacket *) NULL)
3602 return(MagickTrue);
3603 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3604 plasma);
3605 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3606 2.0,plasma);
3607 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3608 plasma);
3609 (void) SyncAuthenticPixels(image,exception);
3610 }
3611 if (segment->y1 != segment->y2)
3612 {
3613 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003614 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003615
3616 /*
3617 Top pixel.
3618 */
cristy06609ee2010-03-17 20:21:27 +00003619 y=(long) ceil(segment->y1-0.5);
3620 (void) GetOneVirtualPixel(image,(long) ceil(segment->x1-0.5),y,&u,
cristy3ed852e2009-09-05 21:47:34 +00003621 exception);
cristy06609ee2010-03-17 20:21:27 +00003622 (void) GetOneVirtualPixel(image,(long) ceil(segment->x2-0.5),y,&v,
cristy3ed852e2009-09-05 21:47:34 +00003623 exception);
3624 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3625 if (q == (PixelPacket *) NULL)
3626 return(MagickTrue);
3627 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3628 plasma);
3629 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3630 2.0,plasma);
3631 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3632 plasma);
3633 (void) SyncAuthenticPixels(image,exception);
3634 }
3635 }
3636 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3637 {
3638 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003639 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003640
3641 /*
3642 Middle pixel.
3643 */
cristy06609ee2010-03-17 20:21:27 +00003644 x=(long) ceil(segment->x1-0.5);
3645 y=(long) ceil(segment->y1-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003646 (void) GetOneVirtualPixel(image,x,y,&u,exception);
cristy06609ee2010-03-17 20:21:27 +00003647 x=(long) ceil(segment->x2-0.5);
3648 y=(long) ceil(segment->y2-0.5);
cristy3ed852e2009-09-05 21:47:34 +00003649 (void) GetOneVirtualPixel(image,x,y,&v,exception);
3650 q=QueueAuthenticPixels(image,x_mid,y_mid,1,1,exception);
3651 if (q == (PixelPacket *) NULL)
3652 return(MagickTrue);
3653 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3654 plasma);
3655 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3656 plasma);
3657 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3658 plasma);
3659 (void) SyncAuthenticPixels(image,exception);
3660 }
3661 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3662 return(MagickTrue);
3663 return(MagickFalse);
3664}
3665
3666MagickExport MagickBooleanType PlasmaImage(Image *image,
3667 const SegmentInfo *segment,unsigned long attenuate,unsigned long depth)
3668{
3669 MagickBooleanType
3670 status;
3671
3672 RandomInfo
3673 *random_info;
3674
3675 if (image->debug != MagickFalse)
3676 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3677 assert(image != (Image *) NULL);
3678 assert(image->signature == MagickSignature);
3679 if (image->debug != MagickFalse)
3680 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3681 random_info=AcquireRandomInfo();
3682 status=PlasmaImageProxy(image,random_info,segment,attenuate,depth);
3683 random_info=DestroyRandomInfo(random_info);
3684 return(status);
3685}
3686
3687/*
3688%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3689% %
3690% %
3691% %
3692% P o l a r o i d I m a g e %
3693% %
3694% %
3695% %
3696%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3697%
3698% PolaroidImage() simulates a Polaroid picture.
3699%
3700% The format of the AnnotateImage method is:
3701%
3702% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3703% const double angle,ExceptionInfo exception)
3704%
3705% A description of each parameter follows:
3706%
3707% o image: the image.
3708%
3709% o draw_info: the draw info.
3710%
3711% o angle: Apply the effect along this angle.
3712%
3713% o exception: return any errors or warnings in this structure.
3714%
3715*/
3716MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3717 const double angle,ExceptionInfo *exception)
3718{
3719 const char
3720 *value;
3721
3722 long
3723 quantum;
3724
3725 Image
3726 *bend_image,
3727 *caption_image,
3728 *flop_image,
3729 *picture_image,
3730 *polaroid_image,
3731 *rotate_image,
3732 *trim_image;
3733
3734 unsigned long
3735 height;
3736
3737 /*
3738 Simulate a Polaroid picture.
3739 */
3740 assert(image != (Image *) NULL);
3741 assert(image->signature == MagickSignature);
3742 if (image->debug != MagickFalse)
3743 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3744 assert(exception != (ExceptionInfo *) NULL);
3745 assert(exception->signature == MagickSignature);
3746 quantum=(long) MagickMax(MagickMax((double) image->columns,(double)
3747 image->rows)/25.0,10.0);
3748 height=image->rows+2*quantum;
3749 caption_image=(Image *) NULL;
3750 value=GetImageProperty(image,"Caption");
3751 if (value != (const char *) NULL)
3752 {
3753 char
3754 *caption,
3755 geometry[MaxTextExtent];
3756
3757 DrawInfo
3758 *annotate_info;
3759
3760 long
3761 count;
3762
3763 MagickBooleanType
3764 status;
3765
3766 TypeMetric
3767 metrics;
3768
3769 /*
3770 Generate caption image.
3771 */
3772 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3773 if (caption_image == (Image *) NULL)
3774 return((Image *) NULL);
3775 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
3776 caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
3777 value);
3778 (void) CloneString(&annotate_info->text,caption);
3779 count=FormatMagickCaption(caption_image,annotate_info,&metrics,&caption);
3780 status=SetImageExtent(caption_image,image->columns,(unsigned long)
3781 ((count+1)*(metrics.ascent-metrics.descent)+0.5));
3782 if (status == MagickFalse)
3783 caption_image=DestroyImage(caption_image);
3784 else
3785 {
3786 caption_image->background_color=image->border_color;
3787 (void) SetImageBackgroundColor(caption_image);
3788 (void) CloneString(&annotate_info->text,caption);
cristye7f51092010-01-17 00:39:37 +00003789 (void) FormatMagickString(geometry,MaxTextExtent,"+0+%g",
cristy3ed852e2009-09-05 21:47:34 +00003790 metrics.ascent);
3791 if (annotate_info->gravity == UndefinedGravity)
3792 (void) CloneString(&annotate_info->geometry,AcquireString(
3793 geometry));
3794 (void) AnnotateImage(caption_image,annotate_info);
3795 height+=caption_image->rows;
3796 }
3797 annotate_info=DestroyDrawInfo(annotate_info);
3798 caption=DestroyString(caption);
3799 }
3800 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3801 exception);
3802 if (picture_image == (Image *) NULL)
3803 {
3804 if (caption_image != (Image *) NULL)
3805 caption_image=DestroyImage(caption_image);
3806 return((Image *) NULL);
3807 }
3808 picture_image->background_color=image->border_color;
3809 (void) SetImageBackgroundColor(picture_image);
3810 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
3811 if (caption_image != (Image *) NULL)
3812 {
3813 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
3814 quantum,(long) (image->rows+3*quantum/2));
3815 caption_image=DestroyImage(caption_image);
3816 }
3817 (void) QueryColorDatabase("none",&picture_image->background_color,exception);
3818 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
3819 rotate_image=RotateImage(picture_image,90.0,exception);
3820 picture_image=DestroyImage(picture_image);
3821 if (rotate_image == (Image *) NULL)
3822 return((Image *) NULL);
3823 picture_image=rotate_image;
3824 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
3825 picture_image->columns,exception);
3826 picture_image=DestroyImage(picture_image);
3827 if (bend_image == (Image *) NULL)
3828 return((Image *) NULL);
3829 InheritException(&bend_image->exception,exception);
3830 picture_image=bend_image;
3831 rotate_image=RotateImage(picture_image,-90.0,exception);
3832 picture_image=DestroyImage(picture_image);
3833 if (rotate_image == (Image *) NULL)
3834 return((Image *) NULL);
3835 picture_image=rotate_image;
3836 picture_image->background_color=image->background_color;
3837 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
3838 exception);
3839 if (polaroid_image == (Image *) NULL)
3840 {
3841 picture_image=DestroyImage(picture_image);
3842 return(picture_image);
3843 }
3844 flop_image=FlopImage(polaroid_image,exception);
3845 polaroid_image=DestroyImage(polaroid_image);
3846 if (flop_image == (Image *) NULL)
3847 {
3848 picture_image=DestroyImage(picture_image);
3849 return(picture_image);
3850 }
3851 polaroid_image=flop_image;
3852 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
3853 (long) (-0.01*picture_image->columns/2.0),0L);
3854 picture_image=DestroyImage(picture_image);
3855 (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
3856 rotate_image=RotateImage(polaroid_image,angle,exception);
3857 polaroid_image=DestroyImage(polaroid_image);
3858 if (rotate_image == (Image *) NULL)
3859 return((Image *) NULL);
3860 polaroid_image=rotate_image;
3861 trim_image=TrimImage(polaroid_image,exception);
3862 polaroid_image=DestroyImage(polaroid_image);
3863 if (trim_image == (Image *) NULL)
3864 return((Image *) NULL);
3865 polaroid_image=trim_image;
3866 return(polaroid_image);
3867}
3868
3869/*
3870%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3871% %
3872% %
3873% %
cristy3ed852e2009-09-05 21:47:34 +00003874% S e p i a T o n e I m a g e %
3875% %
3876% %
3877% %
3878%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3879%
3880% MagickSepiaToneImage() applies a special effect to the image, similar to the
3881% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
3882% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
3883% threshold of 80% is a good starting point for a reasonable tone.
3884%
3885% The format of the SepiaToneImage method is:
3886%
3887% Image *SepiaToneImage(const Image *image,const double threshold,
3888% ExceptionInfo *exception)
3889%
3890% A description of each parameter follows:
3891%
3892% o image: the image.
3893%
3894% o threshold: the tone threshold.
3895%
3896% o exception: return any errors or warnings in this structure.
3897%
3898*/
3899MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
3900 ExceptionInfo *exception)
3901{
3902#define SepiaToneImageTag "SepiaTone/Image"
3903
cristyc4c8d132010-01-07 01:58:38 +00003904 CacheView
3905 *image_view,
3906 *sepia_view;
3907
cristy3ed852e2009-09-05 21:47:34 +00003908 Image
3909 *sepia_image;
3910
3911 long
3912 progress,
3913 y;
3914
3915 MagickBooleanType
3916 status;
3917
cristy3ed852e2009-09-05 21:47:34 +00003918 /*
3919 Initialize sepia-toned image attributes.
3920 */
3921 assert(image != (const Image *) NULL);
3922 assert(image->signature == MagickSignature);
3923 if (image->debug != MagickFalse)
3924 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3925 assert(exception != (ExceptionInfo *) NULL);
3926 assert(exception->signature == MagickSignature);
3927 sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3928 if (sepia_image == (Image *) NULL)
3929 return((Image *) NULL);
3930 if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
3931 {
3932 InheritException(exception,&sepia_image->exception);
3933 sepia_image=DestroyImage(sepia_image);
3934 return((Image *) NULL);
3935 }
3936 /*
3937 Tone each row of the image.
3938 */
3939 status=MagickTrue;
3940 progress=0;
3941 image_view=AcquireCacheView(image);
3942 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00003943#if defined(MAGICKCORE_OPENMP_SUPPORT)
3944 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003945#endif
3946 for (y=0; y < (long) image->rows; y++)
3947 {
3948 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003949 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003950
3951 register long
3952 x;
3953
3954 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003955 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003956
3957 if (status == MagickFalse)
3958 continue;
3959 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3960 q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
3961 exception);
3962 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3963 {
3964 status=MagickFalse;
3965 continue;
3966 }
3967 for (x=0; x < (long) image->columns; x++)
3968 {
3969 MagickRealType
3970 intensity,
3971 tone;
3972
3973 intensity=(MagickRealType) PixelIntensityToQuantum(p);
3974 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
3975 (MagickRealType) QuantumRange-threshold;
cristyce70c172010-01-07 17:15:30 +00003976 q->red=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00003977 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
3978 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristyce70c172010-01-07 17:15:30 +00003979 q->green=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00003980 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristyce70c172010-01-07 17:15:30 +00003981 q->blue=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00003982 tone=threshold/7.0;
3983 if ((MagickRealType) q->green < tone)
cristyce70c172010-01-07 17:15:30 +00003984 q->green=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00003985 if ((MagickRealType) q->blue < tone)
cristyce70c172010-01-07 17:15:30 +00003986 q->blue=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00003987 p++;
3988 q++;
3989 }
3990 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
3991 status=MagickFalse;
3992 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3993 {
3994 MagickBooleanType
3995 proceed;
3996
cristyb5d5f722009-11-04 03:03:49 +00003997#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003998 #pragma omp critical (MagickCore_SepiaToneImage)
3999#endif
4000 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4001 image->rows);
4002 if (proceed == MagickFalse)
4003 status=MagickFalse;
4004 }
4005 }
4006 sepia_view=DestroyCacheView(sepia_view);
4007 image_view=DestroyCacheView(image_view);
4008 (void) NormalizeImage(sepia_image);
4009 (void) ContrastImage(sepia_image,MagickTrue);
4010 if (status == MagickFalse)
4011 sepia_image=DestroyImage(sepia_image);
4012 return(sepia_image);
4013}
4014
4015/*
4016%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4017% %
4018% %
4019% %
4020% S h a d o w I m a g e %
4021% %
4022% %
4023% %
4024%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4025%
4026% ShadowImage() simulates a shadow from the specified image and returns it.
4027%
4028% The format of the ShadowImage method is:
4029%
4030% Image *ShadowImage(const Image *image,const double opacity,
4031% const double sigma,const long x_offset,const long y_offset,
4032% ExceptionInfo *exception)
4033%
4034% A description of each parameter follows:
4035%
4036% o image: the image.
4037%
4038% o opacity: percentage transparency.
4039%
4040% o sigma: the standard deviation of the Gaussian, in pixels.
4041%
4042% o x_offset: the shadow x-offset.
4043%
4044% o y_offset: the shadow y-offset.
4045%
4046% o exception: return any errors or warnings in this structure.
4047%
4048*/
4049MagickExport Image *ShadowImage(const Image *image,const double opacity,
4050 const double sigma,const long x_offset,const long y_offset,
4051 ExceptionInfo *exception)
4052{
4053#define ShadowImageTag "Shadow/Image"
4054
cristyc4c8d132010-01-07 01:58:38 +00004055 CacheView
4056 *image_view;
4057
cristy3ed852e2009-09-05 21:47:34 +00004058 Image
4059 *border_image,
4060 *clone_image,
4061 *shadow_image;
4062
4063 long
4064 progress,
4065 y;
4066
4067 MagickBooleanType
4068 status;
4069
4070 RectangleInfo
4071 border_info;
4072
cristy3ed852e2009-09-05 21:47:34 +00004073 assert(image != (Image *) NULL);
4074 assert(image->signature == MagickSignature);
4075 if (image->debug != MagickFalse)
4076 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4077 assert(exception != (ExceptionInfo *) NULL);
4078 assert(exception->signature == MagickSignature);
4079 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4080 if (clone_image == (Image *) NULL)
4081 return((Image *) NULL);
4082 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4083 clone_image->compose=OverCompositeOp;
cristy06609ee2010-03-17 20:21:27 +00004084 border_info.width=(unsigned long) floor(2.0*sigma+0.5);
4085 border_info.height=(unsigned long) floor(2.0*sigma+0.5);
cristy3ed852e2009-09-05 21:47:34 +00004086 border_info.x=0;
4087 border_info.y=0;
4088 (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4089 border_image=BorderImage(clone_image,&border_info,exception);
4090 clone_image=DestroyImage(clone_image);
4091 if (border_image == (Image *) NULL)
4092 return((Image *) NULL);
4093 if (border_image->matte == MagickFalse)
4094 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4095 /*
4096 Shadow image.
4097 */
4098 status=MagickTrue;
4099 progress=0;
4100 image_view=AcquireCacheView(border_image);
cristyb5d5f722009-11-04 03:03:49 +00004101#if defined(MAGICKCORE_OPENMP_SUPPORT)
4102 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004103#endif
4104 for (y=0; y < (long) border_image->rows; y++)
4105 {
4106 register long
4107 x;
4108
4109 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004110 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004111
4112 if (status == MagickFalse)
4113 continue;
4114 q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4115 exception);
4116 if (q == (PixelPacket *) NULL)
4117 {
4118 status=MagickFalse;
4119 continue;
4120 }
4121 for (x=0; x < (long) border_image->columns; x++)
4122 {
4123 q->red=border_image->background_color.red;
4124 q->green=border_image->background_color.green;
4125 q->blue=border_image->background_color.blue;
4126 if (border_image->matte == MagickFalse)
4127 q->opacity=border_image->background_color.opacity;
4128 else
cristy46f08202010-01-10 04:04:21 +00004129 q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
4130 GetAlphaPixelComponent(q)*opacity/100.0));
cristy3ed852e2009-09-05 21:47:34 +00004131 q++;
4132 }
4133 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4134 status=MagickFalse;
4135 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4136 {
4137 MagickBooleanType
4138 proceed;
4139
cristyb5d5f722009-11-04 03:03:49 +00004140#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004141 #pragma omp critical (MagickCore_ShadowImage)
4142#endif
4143 proceed=SetImageProgress(image,ShadowImageTag,progress++,
4144 border_image->rows);
4145 if (proceed == MagickFalse)
4146 status=MagickFalse;
4147 }
4148 }
4149 image_view=DestroyCacheView(image_view);
4150 shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
4151 border_image=DestroyImage(border_image);
4152 if (shadow_image == (Image *) NULL)
4153 return((Image *) NULL);
4154 if (shadow_image->page.width == 0)
4155 shadow_image->page.width=shadow_image->columns;
4156 if (shadow_image->page.height == 0)
4157 shadow_image->page.height=shadow_image->rows;
4158 shadow_image->page.width+=x_offset-(long) border_info.width;
4159 shadow_image->page.height+=y_offset-(long) border_info.height;
4160 shadow_image->page.x+=x_offset-(long) border_info.width;
4161 shadow_image->page.y+=y_offset-(long) border_info.height;
4162 return(shadow_image);
4163}
4164
4165/*
4166%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4167% %
4168% %
4169% %
4170% S k e t c h I m a g e %
4171% %
4172% %
4173% %
4174%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4175%
4176% SketchImage() simulates a pencil sketch. We convolve the image with a
4177% Gaussian operator of the given radius and standard deviation (sigma). For
4178% reasonable results, radius should be larger than sigma. Use a radius of 0
4179% and SketchImage() selects a suitable radius for you. Angle gives the angle
4180% of the sketch.
4181%
4182% The format of the SketchImage method is:
4183%
4184% Image *SketchImage(const Image *image,const double radius,
4185% const double sigma,const double angle,ExceptionInfo *exception)
4186%
4187% A description of each parameter follows:
4188%
4189% o image: the image.
4190%
4191% o radius: the radius of the Gaussian, in pixels, not counting
4192% the center pixel.
4193%
4194% o sigma: the standard deviation of the Gaussian, in pixels.
4195%
4196% o angle: Apply the effect along this angle.
4197%
4198% o exception: return any errors or warnings in this structure.
4199%
4200*/
4201MagickExport Image *SketchImage(const Image *image,const double radius,
4202 const double sigma,const double angle,ExceptionInfo *exception)
4203{
cristyfa112112010-01-04 17:48:07 +00004204 CacheView
4205 *random_view;
4206
cristy3ed852e2009-09-05 21:47:34 +00004207 Image
4208 *blend_image,
4209 *blur_image,
4210 *dodge_image,
4211 *random_image,
4212 *sketch_image;
4213
4214 long
4215 y;
4216
4217 MagickBooleanType
4218 status;
4219
4220 MagickPixelPacket
4221 zero;
4222
4223 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004224 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004225
4226 /*
4227 Sketch image.
4228 */
4229 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4230 MagickTrue,exception);
4231 if (random_image == (Image *) NULL)
4232 return((Image *) NULL);
4233 status=MagickTrue;
4234 GetMagickPixelPacket(random_image,&zero);
cristy1b784432009-12-19 02:20:40 +00004235 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004236 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004237#if defined(MAGICKCORE_OPENMP_SUPPORT)
4238 #pragma omp parallel for schedule(dynamic,4) shared(status)
4239#endif
cristy3ed852e2009-09-05 21:47:34 +00004240 for (y=0; y < (long) random_image->rows; y++)
4241 {
4242 MagickPixelPacket
4243 pixel;
4244
4245 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004246 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00004247
4248 register long
cristy1b784432009-12-19 02:20:40 +00004249 id,
cristy3ed852e2009-09-05 21:47:34 +00004250 x;
4251
4252 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004253 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004254
cristy1b784432009-12-19 02:20:40 +00004255 if (status == MagickFalse)
4256 continue;
cristy3ed852e2009-09-05 21:47:34 +00004257 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4258 exception);
4259 if (q == (PixelPacket *) NULL)
4260 {
4261 status=MagickFalse;
4262 continue;
4263 }
4264 indexes=GetCacheViewAuthenticIndexQueue(random_view);
4265 pixel=zero;
cristy1b784432009-12-19 02:20:40 +00004266 id=GetOpenMPThreadId();
cristy3ed852e2009-09-05 21:47:34 +00004267 for (x=0; x < (long) random_image->columns; x++)
4268 {
4269 pixel.red=(MagickRealType) (QuantumRange*
cristy1b784432009-12-19 02:20:40 +00004270 GetPseudoRandomValue(random_info[id]));
cristy3ed852e2009-09-05 21:47:34 +00004271 pixel.green=pixel.red;
4272 pixel.blue=pixel.red;
4273 if (image->colorspace == CMYKColorspace)
4274 pixel.index=pixel.red;
4275 SetPixelPacket(random_image,&pixel,q,indexes+x);
4276 q++;
4277 }
4278 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4279 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004280 }
4281 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004282 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004283 if (status == MagickFalse)
4284 {
4285 random_image=DestroyImage(random_image);
4286 return(random_image);
4287 }
4288 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4289 random_image=DestroyImage(random_image);
4290 if (blur_image == (Image *) NULL)
4291 return((Image *) NULL);
4292 dodge_image=EdgeImage(blur_image,radius,exception);
4293 blur_image=DestroyImage(blur_image);
4294 if (dodge_image == (Image *) NULL)
4295 return((Image *) NULL);
4296 (void) NormalizeImage(dodge_image);
4297 (void) NegateImage(dodge_image,MagickFalse);
4298 (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4299 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4300 if (sketch_image == (Image *) NULL)
4301 {
4302 dodge_image=DestroyImage(dodge_image);
4303 return((Image *) NULL);
4304 }
4305 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4306 dodge_image=DestroyImage(dodge_image);
4307 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4308 if (blend_image == (Image *) NULL)
4309 {
4310 sketch_image=DestroyImage(sketch_image);
4311 return((Image *) NULL);
4312 }
4313 (void) SetImageArtifact(blend_image,"compose:args","20x80");
4314 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4315 blend_image=DestroyImage(blend_image);
4316 return(sketch_image);
4317}
4318
4319/*
4320%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4321% %
4322% %
4323% %
4324% S o l a r i z e I m a g e %
4325% %
4326% %
4327% %
4328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4329%
4330% SolarizeImage() applies a special effect to the image, similar to the effect
4331% achieved in a photo darkroom by selectively exposing areas of photo
4332% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4333% measure of the extent of the solarization.
4334%
4335% The format of the SolarizeImage method is:
4336%
4337% MagickBooleanType SolarizeImage(Image *image,const double threshold)
4338%
4339% A description of each parameter follows:
4340%
4341% o image: the image.
4342%
4343% o threshold: Define the extent of the solarization.
4344%
4345*/
4346MagickExport MagickBooleanType SolarizeImage(Image *image,
4347 const double threshold)
4348{
4349#define SolarizeImageTag "Solarize/Image"
4350
cristyc4c8d132010-01-07 01:58:38 +00004351 CacheView
4352 *image_view;
4353
cristy3ed852e2009-09-05 21:47:34 +00004354 ExceptionInfo
4355 *exception;
4356
4357 long
4358 progress,
4359 y;
4360
4361 MagickBooleanType
4362 status;
4363
cristy3ed852e2009-09-05 21:47:34 +00004364 assert(image != (Image *) NULL);
4365 assert(image->signature == MagickSignature);
4366 if (image->debug != MagickFalse)
4367 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4368 if (image->storage_class == PseudoClass)
4369 {
4370 register long
4371 i;
4372
4373 /*
4374 Solarize colormap.
4375 */
4376 for (i=0; i < (long) image->colors; i++)
4377 {
4378 if ((MagickRealType) image->colormap[i].red > threshold)
4379 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4380 if ((MagickRealType) image->colormap[i].green > threshold)
4381 image->colormap[i].green=(Quantum) QuantumRange-
4382 image->colormap[i].green;
4383 if ((MagickRealType) image->colormap[i].blue > threshold)
4384 image->colormap[i].blue=(Quantum) QuantumRange-
4385 image->colormap[i].blue;
4386 }
4387 }
4388 /*
4389 Solarize image.
4390 */
4391 status=MagickTrue;
4392 progress=0;
4393 exception=(&image->exception);
4394 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004395#if defined(MAGICKCORE_OPENMP_SUPPORT)
4396 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004397#endif
4398 for (y=0; y < (long) image->rows; y++)
4399 {
4400 register long
4401 x;
4402
4403 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004404 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004405
4406 if (status == MagickFalse)
4407 continue;
4408 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4409 exception);
4410 if (q == (PixelPacket *) NULL)
4411 {
4412 status=MagickFalse;
4413 continue;
4414 }
4415 for (x=0; x < (long) image->columns; x++)
4416 {
4417 if ((MagickRealType) q->red > threshold)
4418 q->red=(Quantum) QuantumRange-q->red;
4419 if ((MagickRealType) q->green > threshold)
4420 q->green=(Quantum) QuantumRange-q->green;
4421 if ((MagickRealType) q->blue > threshold)
4422 q->blue=(Quantum) QuantumRange-q->blue;
4423 q++;
4424 }
4425 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4426 status=MagickFalse;
4427 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4428 {
4429 MagickBooleanType
4430 proceed;
4431
cristyb5d5f722009-11-04 03:03:49 +00004432#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004433 #pragma omp critical (MagickCore_SolarizeImage)
4434#endif
4435 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4436 if (proceed == MagickFalse)
4437 status=MagickFalse;
4438 }
4439 }
4440 image_view=DestroyCacheView(image_view);
4441 return(status);
4442}
4443
4444/*
4445%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4446% %
4447% %
4448% %
4449% S t e g a n o I m a g e %
4450% %
4451% %
4452% %
4453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4454%
4455% SteganoImage() hides a digital watermark within the image. Recover
4456% the hidden watermark later to prove that the authenticity of an image.
4457% Offset defines the start position within the image to hide the watermark.
4458%
4459% The format of the SteganoImage method is:
4460%
4461% Image *SteganoImage(const Image *image,Image *watermark,
4462% ExceptionInfo *exception)
4463%
4464% A description of each parameter follows:
4465%
4466% o image: the image.
4467%
4468% o watermark: the watermark image.
4469%
4470% o exception: return any errors or warnings in this structure.
4471%
4472*/
4473MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4474 ExceptionInfo *exception)
4475{
4476#define GetBit(alpha,i) ((((unsigned long) (alpha) >> (unsigned long) \
4477 (i)) & 0x01) != 0)
4478#define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) ? (unsigned long) (alpha) \
4479 | (1UL << (unsigned long) (i)) : (unsigned long) (alpha) & \
4480 ~(1UL << (unsigned long) (i)))
4481#define SteganoImageTag "Stegano/Image"
4482
4483 Image
4484 *stegano_image;
4485
4486 int
4487 c;
4488
4489 long
4490 i,
4491 j,
4492 k,
4493 y;
4494
4495 MagickBooleanType
4496 status;
4497
4498 PixelPacket
4499 pixel;
4500
4501 register long
4502 x;
4503
4504 register PixelPacket
4505 *q;
4506
4507 unsigned long
4508 depth;
4509
4510 /*
4511 Initialize steganographic image attributes.
4512 */
4513 assert(image != (const Image *) NULL);
4514 assert(image->signature == MagickSignature);
4515 if (image->debug != MagickFalse)
4516 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4517 assert(watermark != (const Image *) NULL);
4518 assert(watermark->signature == MagickSignature);
4519 assert(exception != (ExceptionInfo *) NULL);
4520 assert(exception->signature == MagickSignature);
4521 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4522 if (stegano_image == (Image *) NULL)
4523 return((Image *) NULL);
4524 if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
4525 {
4526 InheritException(exception,&stegano_image->exception);
4527 stegano_image=DestroyImage(stegano_image);
4528 return((Image *) NULL);
4529 }
4530 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4531 /*
4532 Hide watermark in low-order bits of image.
4533 */
4534 c=0;
4535 i=0;
4536 j=0;
4537 depth=stegano_image->depth;
4538 k=image->offset;
4539 for (i=(long) depth-1; (i >= 0) && (j < (long) depth); i--)
4540 {
4541 for (y=0; (y < (long) watermark->rows) && (j < (long) depth); y++)
4542 {
4543 for (x=0; (x < (long) watermark->columns) && (j < (long) depth); x++)
4544 {
4545 (void) GetOneVirtualPixel(watermark,x,y,&pixel,exception);
4546 if ((k/(long) stegano_image->columns) >= (long) stegano_image->rows)
4547 break;
4548 q=GetAuthenticPixels(stegano_image,k % (long) stegano_image->columns,
4549 k/(long) stegano_image->columns,1,1,exception);
4550 if (q == (PixelPacket *) NULL)
4551 break;
4552 switch (c)
4553 {
4554 case 0:
4555 {
4556 SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4557 break;
4558 }
4559 case 1:
4560 {
4561 SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4562 break;
4563 }
4564 case 2:
4565 {
4566 SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4567 break;
4568 }
4569 }
4570 if (SyncAuthenticPixels(stegano_image,exception) == MagickFalse)
4571 break;
4572 c++;
4573 if (c == 3)
4574 c=0;
4575 k++;
4576 if (k == (long) (stegano_image->columns*stegano_image->columns))
4577 k=0;
4578 if (k == image->offset)
4579 j++;
4580 }
4581 }
cristy8b27a6d2010-02-14 03:31:15 +00004582 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004583 {
cristy8b27a6d2010-02-14 03:31:15 +00004584 MagickBooleanType
4585 proceed;
4586
4587 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4588 (depth-i),depth);
4589 if (proceed == MagickFalse)
4590 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004591 }
4592 }
4593 if (stegano_image->storage_class == PseudoClass)
4594 (void) SyncImage(stegano_image);
4595 return(stegano_image);
4596}
4597
4598/*
4599%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4600% %
4601% %
4602% %
4603% S t e r e o A n a g l y p h I m a g e %
4604% %
4605% %
4606% %
4607%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4608%
4609% StereoAnaglyphImage() combines two images and produces a single image that
4610% is the composite of a left and right image of a stereo pair. Special
4611% red-green stereo glasses are required to view this effect.
4612%
4613% The format of the StereoAnaglyphImage method is:
4614%
4615% Image *StereoImage(const Image *left_image,const Image *right_image,
4616% ExceptionInfo *exception)
4617% Image *StereoAnaglyphImage(const Image *left_image,
4618% const Image *right_image,const long x_offset,const long y_offset,
4619% ExceptionInfo *exception)
4620%
4621% A description of each parameter follows:
4622%
4623% o left_image: the left image.
4624%
4625% o right_image: the right image.
4626%
4627% o exception: return any errors or warnings in this structure.
4628%
4629% o x_offset: amount, in pixels, by which the left image is offset to the
4630% right of the right image.
4631%
4632% o y_offset: amount, in pixels, by which the left image is offset to the
4633% bottom of the right image.
4634%
4635%
4636*/
4637MagickExport Image *StereoImage(const Image *left_image,
4638 const Image *right_image,ExceptionInfo *exception)
4639{
4640 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4641}
4642
4643MagickExport Image *StereoAnaglyphImage(const Image *left_image,
4644 const Image *right_image,const long x_offset,const long y_offset,
4645 ExceptionInfo *exception)
4646{
4647#define StereoImageTag "Stereo/Image"
4648
4649 const Image
4650 *image;
4651
4652 Image
4653 *stereo_image;
4654
4655 long
4656 y;
4657
4658 MagickBooleanType
4659 status;
4660
4661 assert(left_image != (const Image *) NULL);
4662 assert(left_image->signature == MagickSignature);
4663 if (left_image->debug != MagickFalse)
4664 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4665 left_image->filename);
4666 assert(right_image != (const Image *) NULL);
4667 assert(right_image->signature == MagickSignature);
4668 assert(exception != (ExceptionInfo *) NULL);
4669 assert(exception->signature == MagickSignature);
4670 assert(right_image != (const Image *) NULL);
4671 image=left_image;
4672 if ((left_image->columns != right_image->columns) ||
4673 (left_image->rows != right_image->rows))
4674 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4675 /*
4676 Initialize stereo image attributes.
4677 */
4678 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4679 MagickTrue,exception);
4680 if (stereo_image == (Image *) NULL)
4681 return((Image *) NULL);
4682 if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
4683 {
4684 InheritException(exception,&stereo_image->exception);
4685 stereo_image=DestroyImage(stereo_image);
4686 return((Image *) NULL);
4687 }
4688 /*
4689 Copy left image to red channel and right image to blue channel.
4690 */
4691 for (y=0; y < (long) stereo_image->rows; y++)
4692 {
4693 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004694 *restrict p,
4695 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004696
4697 register long
4698 x;
4699
4700 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004701 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00004702
4703 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4704 exception);
4705 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4706 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
4707 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
4708 (r == (PixelPacket *) NULL))
4709 break;
4710 for (x=0; x < (long) stereo_image->columns; x++)
4711 {
cristyce70c172010-01-07 17:15:30 +00004712 r->red=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00004713 r->green=q->green;
4714 r->blue=q->blue;
4715 r->opacity=(Quantum) ((p->opacity+q->opacity)/2);
4716 p++;
4717 q++;
4718 r++;
4719 }
4720 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4721 break;
cristy8b27a6d2010-02-14 03:31:15 +00004722 if (image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004723 {
cristy8b27a6d2010-02-14 03:31:15 +00004724 MagickBooleanType
4725 proceed;
4726
4727 proceed=SetImageProgress(image,StereoImageTag,y,stereo_image->rows);
4728 if (proceed == MagickFalse)
4729 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004730 }
4731 }
4732 return(stereo_image);
4733}
4734
4735/*
4736%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4737% %
4738% %
4739% %
4740% S w i r l I m a g e %
4741% %
4742% %
4743% %
4744%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4745%
4746% SwirlImage() swirls the pixels about the center of the image, where
4747% degrees indicates the sweep of the arc through which each pixel is moved.
4748% You get a more dramatic effect as the degrees move from 1 to 360.
4749%
4750% The format of the SwirlImage method is:
4751%
4752% Image *SwirlImage(const Image *image,double degrees,
4753% ExceptionInfo *exception)
4754%
4755% A description of each parameter follows:
4756%
4757% o image: the image.
4758%
4759% o degrees: Define the tightness of the swirling effect.
4760%
4761% o exception: return any errors or warnings in this structure.
4762%
4763*/
4764MagickExport Image *SwirlImage(const Image *image,double degrees,
4765 ExceptionInfo *exception)
4766{
4767#define SwirlImageTag "Swirl/Image"
4768
cristyfa112112010-01-04 17:48:07 +00004769 CacheView
4770 *image_view,
4771 *swirl_view;
4772
cristy3ed852e2009-09-05 21:47:34 +00004773 Image
4774 *swirl_image;
4775
4776 long
4777 progress,
4778 y;
4779
4780 MagickBooleanType
4781 status;
4782
4783 MagickPixelPacket
4784 zero;
4785
4786 MagickRealType
4787 radius;
4788
4789 PointInfo
4790 center,
4791 scale;
4792
4793 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00004794 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00004795
4796 /*
4797 Initialize swirl image attributes.
4798 */
4799 assert(image != (const Image *) NULL);
4800 assert(image->signature == MagickSignature);
4801 if (image->debug != MagickFalse)
4802 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4803 assert(exception != (ExceptionInfo *) NULL);
4804 assert(exception->signature == MagickSignature);
4805 swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4806 if (swirl_image == (Image *) NULL)
4807 return((Image *) NULL);
4808 if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
4809 {
4810 InheritException(exception,&swirl_image->exception);
4811 swirl_image=DestroyImage(swirl_image);
4812 return((Image *) NULL);
4813 }
4814 if (swirl_image->background_color.opacity != OpaqueOpacity)
4815 swirl_image->matte=MagickTrue;
4816 /*
4817 Compute scaling factor.
4818 */
4819 center.x=(double) image->columns/2.0;
4820 center.y=(double) image->rows/2.0;
4821 radius=MagickMax(center.x,center.y);
4822 scale.x=1.0;
4823 scale.y=1.0;
4824 if (image->columns > image->rows)
4825 scale.y=(double) image->columns/(double) image->rows;
4826 else
4827 if (image->columns < image->rows)
4828 scale.x=(double) image->rows/(double) image->columns;
4829 degrees=(double) DegreesToRadians(degrees);
4830 /*
4831 Swirl image.
4832 */
4833 status=MagickTrue;
4834 progress=0;
4835 GetMagickPixelPacket(swirl_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00004836 resample_filter=AcquireResampleFilterThreadSet(image,
4837 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004838 image_view=AcquireCacheView(image);
4839 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00004840#if defined(MAGICKCORE_OPENMP_SUPPORT)
4841 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004842#endif
4843 for (y=0; y < (long) image->rows; y++)
4844 {
4845 MagickPixelPacket
4846 pixel;
4847
4848 MagickRealType
4849 distance;
4850
4851 PointInfo
4852 delta;
4853
4854 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004855 *restrict swirl_indexes;
cristy3ed852e2009-09-05 21:47:34 +00004856
4857 register long
4858 id,
4859 x;
4860
4861 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004862 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004863
4864 if (status == MagickFalse)
4865 continue;
4866 q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
4867 exception);
4868 if (q == (PixelPacket *) NULL)
4869 {
4870 status=MagickFalse;
4871 continue;
4872 }
4873 swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
4874 delta.y=scale.y*(double) (y-center.y);
4875 pixel=zero;
4876 id=GetOpenMPThreadId();
4877 for (x=0; x < (long) image->columns; x++)
4878 {
4879 /*
4880 Determine if the pixel is within an ellipse.
4881 */
4882 delta.x=scale.x*(double) (x-center.x);
4883 distance=delta.x*delta.x+delta.y*delta.y;
4884 if (distance < (radius*radius))
4885 {
4886 MagickRealType
4887 cosine,
4888 factor,
4889 sine;
4890
4891 /*
4892 Swirl the pixel.
4893 */
4894 factor=1.0-sqrt((double) distance)/radius;
4895 sine=sin((double) (degrees*factor*factor));
4896 cosine=cos((double) (degrees*factor*factor));
4897 (void) ResamplePixelColor(resample_filter[id],(double) ((cosine*
4898 delta.x-sine*delta.y)/scale.x+center.x),(double) ((sine*delta.x+
4899 cosine*delta.y)/scale.y+center.y),&pixel);
4900 SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
4901 }
4902 q++;
4903 }
4904 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
4905 status=MagickFalse;
4906 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4907 {
4908 MagickBooleanType
4909 proceed;
4910
cristyb5d5f722009-11-04 03:03:49 +00004911#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004912 #pragma omp critical (MagickCore_SwirlImage)
4913#endif
4914 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
4915 if (proceed == MagickFalse)
4916 status=MagickFalse;
4917 }
4918 }
4919 swirl_view=DestroyCacheView(swirl_view);
4920 image_view=DestroyCacheView(image_view);
4921 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
4922 if (status == MagickFalse)
4923 swirl_image=DestroyImage(swirl_image);
4924 return(swirl_image);
4925}
4926
4927/*
4928%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4929% %
4930% %
4931% %
4932% T i n t I m a g e %
4933% %
4934% %
4935% %
4936%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4937%
4938% TintImage() applies a color vector to each pixel in the image. The length
4939% of the vector is 0 for black and white and at its maximum for the midtones.
4940% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
4941%
4942% The format of the TintImage method is:
4943%
4944% Image *TintImage(const Image *image,const char *opacity,
4945% const PixelPacket tint,ExceptionInfo *exception)
4946%
4947% A description of each parameter follows:
4948%
4949% o image: the image.
4950%
4951% o opacity: A color value used for tinting.
4952%
4953% o tint: A color value used for tinting.
4954%
4955% o exception: return any errors or warnings in this structure.
4956%
4957*/
4958MagickExport Image *TintImage(const Image *image,const char *opacity,
4959 const PixelPacket tint,ExceptionInfo *exception)
4960{
4961#define TintImageTag "Tint/Image"
4962
cristyc4c8d132010-01-07 01:58:38 +00004963 CacheView
4964 *image_view,
4965 *tint_view;
4966
cristy3ed852e2009-09-05 21:47:34 +00004967 GeometryInfo
4968 geometry_info;
4969
4970 Image
4971 *tint_image;
4972
4973 long
4974 progress,
4975 y;
4976
4977 MagickBooleanType
4978 status;
4979
4980 MagickStatusType
4981 flags;
4982
4983 MagickPixelPacket
4984 color_vector,
4985 pixel;
4986
cristy3ed852e2009-09-05 21:47:34 +00004987 /*
4988 Allocate tint image.
4989 */
4990 assert(image != (const Image *) NULL);
4991 assert(image->signature == MagickSignature);
4992 if (image->debug != MagickFalse)
4993 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4994 assert(exception != (ExceptionInfo *) NULL);
4995 assert(exception->signature == MagickSignature);
4996 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4997 if (tint_image == (Image *) NULL)
4998 return((Image *) NULL);
4999 if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5000 {
5001 InheritException(exception,&tint_image->exception);
5002 tint_image=DestroyImage(tint_image);
5003 return((Image *) NULL);
5004 }
5005 if (opacity == (const char *) NULL)
5006 return(tint_image);
5007 /*
5008 Determine RGB values of the color.
5009 */
5010 flags=ParseGeometry(opacity,&geometry_info);
5011 pixel.red=geometry_info.rho;
5012 if ((flags & SigmaValue) != 0)
5013 pixel.green=geometry_info.sigma;
5014 else
5015 pixel.green=pixel.red;
5016 if ((flags & XiValue) != 0)
5017 pixel.blue=geometry_info.xi;
5018 else
5019 pixel.blue=pixel.red;
5020 if ((flags & PsiValue) != 0)
5021 pixel.opacity=geometry_info.psi;
5022 else
5023 pixel.opacity=(MagickRealType) OpaqueOpacity;
5024 color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5025 PixelIntensity(&tint));
5026 color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5027 PixelIntensity(&tint));
5028 color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5029 PixelIntensity(&tint));
5030 /*
5031 Tint image.
5032 */
5033 status=MagickTrue;
5034 progress=0;
5035 image_view=AcquireCacheView(image);
5036 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005037#if defined(MAGICKCORE_OPENMP_SUPPORT)
5038 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005039#endif
5040 for (y=0; y < (long) image->rows; y++)
5041 {
5042 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005043 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005044
5045 register long
5046 x;
5047
5048 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005049 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005050
5051 if (status == MagickFalse)
5052 continue;
5053 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5054 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5055 exception);
5056 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5057 {
5058 status=MagickFalse;
5059 continue;
5060 }
5061 for (x=0; x < (long) image->columns; x++)
5062 {
5063 MagickPixelPacket
5064 pixel;
5065
5066 MagickRealType
5067 weight;
5068
5069 weight=QuantumScale*p->red-0.5;
5070 pixel.red=(MagickRealType) p->red+color_vector.red*(1.0-(4.0*
5071 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005072 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00005073 weight=QuantumScale*p->green-0.5;
5074 pixel.green=(MagickRealType) p->green+color_vector.green*(1.0-(4.0*
5075 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005076 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00005077 weight=QuantumScale*p->blue-0.5;
5078 pixel.blue=(MagickRealType) p->blue+color_vector.blue*(1.0-(4.0*
5079 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005080 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5081 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00005082 p++;
5083 q++;
5084 }
5085 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5086 status=MagickFalse;
5087 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5088 {
5089 MagickBooleanType
5090 proceed;
5091
cristyb5d5f722009-11-04 03:03:49 +00005092#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005093 #pragma omp critical (MagickCore_TintImage)
5094#endif
5095 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5096 if (proceed == MagickFalse)
5097 status=MagickFalse;
5098 }
5099 }
5100 tint_view=DestroyCacheView(tint_view);
5101 image_view=DestroyCacheView(image_view);
5102 if (status == MagickFalse)
5103 tint_image=DestroyImage(tint_image);
5104 return(tint_image);
5105}
5106
5107/*
5108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5109% %
5110% %
5111% %
5112% V i g n e t t e I m a g e %
5113% %
5114% %
5115% %
5116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5117%
5118% VignetteImage() softens the edges of the image in vignette style.
5119%
5120% The format of the VignetteImage method is:
5121%
5122% Image *VignetteImage(const Image *image,const double radius,
5123% const double sigma,const long x,const long y,ExceptionInfo *exception)
5124%
5125% A description of each parameter follows:
5126%
5127% o image: the image.
5128%
5129% o radius: the radius of the pixel neighborhood.
5130%
5131% o sigma: the standard deviation of the Gaussian, in pixels.
5132%
5133% o x, y: Define the x and y ellipse offset.
5134%
5135% o exception: return any errors or warnings in this structure.
5136%
5137*/
5138MagickExport Image *VignetteImage(const Image *image,const double radius,
5139 const double sigma,const long x,const long y,ExceptionInfo *exception)
5140{
5141 char
5142 ellipse[MaxTextExtent];
5143
5144 DrawInfo
5145 *draw_info;
5146
5147 Image
5148 *canvas_image,
5149 *blur_image,
5150 *oval_image,
5151 *vignette_image;
5152
5153 assert(image != (Image *) NULL);
5154 assert(image->signature == MagickSignature);
5155 if (image->debug != MagickFalse)
5156 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5157 assert(exception != (ExceptionInfo *) NULL);
5158 assert(exception->signature == MagickSignature);
5159 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5160 if (canvas_image == (Image *) NULL)
5161 return((Image *) NULL);
5162 if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
5163 {
5164 InheritException(exception,&canvas_image->exception);
5165 canvas_image=DestroyImage(canvas_image);
5166 return((Image *) NULL);
5167 }
5168 canvas_image->matte=MagickTrue;
5169 oval_image=CloneImage(canvas_image,canvas_image->columns,
5170 canvas_image->rows,MagickTrue,exception);
5171 if (oval_image == (Image *) NULL)
5172 {
5173 canvas_image=DestroyImage(canvas_image);
5174 return((Image *) NULL);
5175 }
5176 (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5177 (void) SetImageBackgroundColor(oval_image);
5178 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5179 (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5180 (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
5181 (void) FormatMagickString(ellipse,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00005182 "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
cristy8cd5b312010-01-07 01:10:24 +00005183 image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005184 draw_info->primitive=AcquireString(ellipse);
5185 (void) DrawImage(oval_image,draw_info);
5186 draw_info=DestroyDrawInfo(draw_info);
5187 blur_image=BlurImage(oval_image,radius,sigma,exception);
5188 oval_image=DestroyImage(oval_image);
5189 if (blur_image == (Image *) NULL)
5190 {
5191 canvas_image=DestroyImage(canvas_image);
5192 return((Image *) NULL);
5193 }
5194 blur_image->matte=MagickFalse;
5195 (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5196 blur_image=DestroyImage(blur_image);
5197 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5198 canvas_image=DestroyImage(canvas_image);
5199 return(vignette_image);
5200}
5201
5202/*
5203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5204% %
5205% %
5206% %
5207% W a v e I m a g e %
5208% %
5209% %
5210% %
5211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5212%
5213% WaveImage() creates a "ripple" effect in the image by shifting the pixels
5214% vertically along a sine wave whose amplitude and wavelength is specified
5215% by the given parameters.
5216%
5217% The format of the WaveImage method is:
5218%
5219% Image *WaveImage(const Image *image,const double amplitude,
5220% const double wave_length,ExceptionInfo *exception)
5221%
5222% A description of each parameter follows:
5223%
5224% o image: the image.
5225%
5226% o amplitude, wave_length: Define the amplitude and wave length of the
5227% sine wave.
5228%
5229% o exception: return any errors or warnings in this structure.
5230%
5231*/
5232MagickExport Image *WaveImage(const Image *image,const double amplitude,
5233 const double wave_length,ExceptionInfo *exception)
5234{
5235#define WaveImageTag "Wave/Image"
5236
cristyfa112112010-01-04 17:48:07 +00005237 CacheView
5238 *wave_view;
5239
cristy3ed852e2009-09-05 21:47:34 +00005240 Image
5241 *wave_image;
5242
5243 long
5244 progress,
5245 y;
5246
5247 MagickBooleanType
5248 status;
5249
5250 MagickPixelPacket
5251 zero;
5252
5253 MagickRealType
5254 *sine_map;
5255
5256 register long
5257 i;
5258
5259 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00005260 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00005261
5262 /*
5263 Initialize wave image attributes.
5264 */
5265 assert(image != (Image *) NULL);
5266 assert(image->signature == MagickSignature);
5267 if (image->debug != MagickFalse)
5268 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5269 assert(exception != (ExceptionInfo *) NULL);
5270 assert(exception->signature == MagickSignature);
5271 wave_image=CloneImage(image,image->columns,(unsigned long) (image->rows+2.0*
5272 fabs(amplitude)),MagickTrue,exception);
5273 if (wave_image == (Image *) NULL)
5274 return((Image *) NULL);
5275 if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
5276 {
5277 InheritException(exception,&wave_image->exception);
5278 wave_image=DestroyImage(wave_image);
5279 return((Image *) NULL);
5280 }
5281 if (wave_image->background_color.opacity != OpaqueOpacity)
5282 wave_image->matte=MagickTrue;
5283 /*
5284 Allocate sine map.
5285 */
5286 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5287 sizeof(*sine_map));
5288 if (sine_map == (MagickRealType *) NULL)
5289 {
5290 wave_image=DestroyImage(wave_image);
5291 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5292 }
5293 for (i=0; i < (long) wave_image->columns; i++)
5294 sine_map[i]=fabs(amplitude)+amplitude*sin((2*MagickPI*i)/wave_length);
5295 /*
5296 Wave image.
5297 */
5298 status=MagickTrue;
5299 progress=0;
5300 GetMagickPixelPacket(wave_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00005301 resample_filter=AcquireResampleFilterThreadSet(image,
5302 BackgroundVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00005303 wave_view=AcquireCacheView(wave_image);
cristyb5d5f722009-11-04 03:03:49 +00005304#if defined(MAGICKCORE_OPENMP_SUPPORT)
5305 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005306#endif
5307 for (y=0; y < (long) wave_image->rows; y++)
5308 {
5309 MagickPixelPacket
5310 pixel;
5311
5312 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00005313 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00005314
5315 register long
5316 id,
5317 x;
5318
5319 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005320 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005321
5322 if (status == MagickFalse)
5323 continue;
5324 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5325 exception);
5326 if (q == (PixelPacket *) NULL)
5327 {
5328 status=MagickFalse;
5329 continue;
5330 }
5331 indexes=GetCacheViewAuthenticIndexQueue(wave_view);
5332 pixel=zero;
5333 id=GetOpenMPThreadId();
cristy3ed852e2009-09-05 21:47:34 +00005334 for (x=0; x < (long) wave_image->columns; x++)
5335 {
5336 (void) ResamplePixelColor(resample_filter[id],(double) x,(double) (y-
5337 sine_map[x]),&pixel);
5338 SetPixelPacket(wave_image,&pixel,q,indexes+x);
5339 q++;
5340 }
5341 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5342 status=MagickFalse;
5343 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5344 {
5345 MagickBooleanType
5346 proceed;
5347
cristyb5d5f722009-11-04 03:03:49 +00005348#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005349 #pragma omp critical (MagickCore_WaveImage)
5350#endif
5351 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5352 if (proceed == MagickFalse)
5353 status=MagickFalse;
5354 }
5355 }
5356 wave_view=DestroyCacheView(wave_view);
5357 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5358 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5359 if (status == MagickFalse)
5360 wave_image=DestroyImage(wave_image);
5361 return(wave_image);
5362}