blob: 3eb1b6e12c56d94393967966a47cee87c7a469bb [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"
67#include "magick/memory_.h"
68#include "magick/monitor.h"
69#include "magick/monitor-private.h"
70#include "magick/option.h"
71#include "magick/pixel-private.h"
72#include "magick/property.h"
73#include "magick/quantum.h"
cristyce70c172010-01-07 17:15:30 +000074#include "magick/quantum-private.h"
cristy3ed852e2009-09-05 21:47:34 +000075#include "magick/random_.h"
76#include "magick/random-private.h"
77#include "magick/resample.h"
78#include "magick/resample-private.h"
79#include "magick/resize.h"
80#include "magick/shear.h"
81#include "magick/splay-tree.h"
82#include "magick/statistic.h"
83#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000084#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000085#include "magick/thread-private.h"
86#include "magick/transform.h"
87#include "magick/utility.h"
88
89/*
90 Define declarations.
91*/
92#define LeftShiftOperator 0xf5
93#define RightShiftOperator 0xf6
94#define LessThanEqualOperator 0xf7
95#define GreaterThanEqualOperator 0xf8
96#define EqualOperator 0xf9
97#define NotEqualOperator 0xfa
98#define LogicalAndOperator 0xfb
99#define LogicalOrOperator 0xfc
100
101struct _FxInfo
102{
103 const Image
104 *images;
105
106 MagickBooleanType
107 matte;
108
109 char
110 *expression;
111
112 FILE
113 *file;
114
115 SplayTreeInfo
116 *colors,
117 *symbols;
118
119 ResampleFilter
120 **resample_filter;
121
122 RandomInfo
123 *random_info;
124
125 ExceptionInfo
126 *exception;
127};
128
129/*
130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
131% %
132% %
133% %
134+ A c q u i r e F x I n f o %
135% %
136% %
137% %
138%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
139%
140% AcquireFxInfo() allocates the FxInfo structure.
141%
142% The format of the AcquireFxInfo method is:
143%
144% FxInfo *AcquireFxInfo(Image *image,const char *expression)
145% A description of each parameter follows:
146%
147% o image: the image.
148%
149% o expression: the expression.
150%
151*/
152MagickExport FxInfo *AcquireFxInfo(const Image *image,const char *expression)
153{
154 char
155 fx_op[2];
156
157 FxInfo
158 *fx_info;
159
160 register long
161 i;
162
cristy90823212009-12-12 20:48:33 +0000163 fx_info=(FxInfo *) AcquireAlignedMemory(1,sizeof(*fx_info));
cristy3ed852e2009-09-05 21:47:34 +0000164 if (fx_info == (FxInfo *) NULL)
165 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
166 (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
167 fx_info->exception=AcquireExceptionInfo();
168 fx_info->images=image;
169 fx_info->matte=image->matte;
170 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
171 RelinquishMagickMemory);
172 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
173 RelinquishMagickMemory);
174 fx_info->resample_filter=(ResampleFilter **) AcquireQuantumMemory(
175 GetImageListLength(fx_info->images),sizeof(*fx_info->resample_filter));
176 if (fx_info->resample_filter == (ResampleFilter **) NULL)
177 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
178 for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
179 {
180 fx_info->resample_filter[i]=AcquireResampleFilter(GetImageFromList(
181 fx_info->images,i),fx_info->exception);
182 SetResampleFilter(fx_info->resample_filter[i],PointFilter,1.0);
183 }
184 fx_info->random_info=AcquireRandomInfo();
185 fx_info->expression=ConstantString(expression);
186 fx_info->file=stderr;
187 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
188 if ((strstr(fx_info->expression,"e+") != (char *) NULL) ||
189 (strstr(fx_info->expression,"e-") != (char *) NULL))
190 {
191 /*
192 Convert scientific notation.
193 */
194 (void) SubstituteString(&fx_info->expression,"0e+","0*10^");
195 (void) SubstituteString(&fx_info->expression,"1e+","1*10^");
196 (void) SubstituteString(&fx_info->expression,"2e+","2*10^");
197 (void) SubstituteString(&fx_info->expression,"3e+","3*10^");
198 (void) SubstituteString(&fx_info->expression,"4e+","4*10^");
199 (void) SubstituteString(&fx_info->expression,"5e+","5*10^");
200 (void) SubstituteString(&fx_info->expression,"6e+","6*10^");
201 (void) SubstituteString(&fx_info->expression,"7e+","7*10^");
202 (void) SubstituteString(&fx_info->expression,"8e+","8*10^");
203 (void) SubstituteString(&fx_info->expression,"9e+","9*10^");
204 (void) SubstituteString(&fx_info->expression,"0e-","0*10^-");
205 (void) SubstituteString(&fx_info->expression,"1e-","1*10^-");
206 (void) SubstituteString(&fx_info->expression,"2e-","2*10^-");
207 (void) SubstituteString(&fx_info->expression,"3e-","3*10^-");
208 (void) SubstituteString(&fx_info->expression,"4e-","4*10^-");
209 (void) SubstituteString(&fx_info->expression,"5e-","5*10^-");
210 (void) SubstituteString(&fx_info->expression,"6e-","6*10^-");
211 (void) SubstituteString(&fx_info->expression,"7e-","7*10^-");
212 (void) SubstituteString(&fx_info->expression,"8e-","8*10^-");
213 (void) SubstituteString(&fx_info->expression,"9e-","9*10^-");
214 }
215 /*
216 Convert complex to simple operators.
217 */
218 fx_op[1]='\0';
219 *fx_op=(char) LeftShiftOperator;
220 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
221 *fx_op=(char) RightShiftOperator;
222 (void) SubstituteString(&fx_info->expression,">>",fx_op);
223 *fx_op=(char) LessThanEqualOperator;
224 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
225 *fx_op=(char) GreaterThanEqualOperator;
226 (void) SubstituteString(&fx_info->expression,">=",fx_op);
227 *fx_op=(char) EqualOperator;
228 (void) SubstituteString(&fx_info->expression,"==",fx_op);
229 *fx_op=(char) NotEqualOperator;
230 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
231 *fx_op=(char) LogicalAndOperator;
232 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
233 *fx_op=(char) LogicalOrOperator;
234 (void) SubstituteString(&fx_info->expression,"||",fx_op);
235 return(fx_info);
236}
237
238/*
239%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
240% %
241% %
242% %
243% A d d N o i s e I m a g e %
244% %
245% %
246% %
247%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
248%
249% AddNoiseImage() adds random noise to the image.
250%
251% The format of the AddNoiseImage method is:
252%
253% Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
254% ExceptionInfo *exception)
255% Image *AddNoiseImageChannel(const Image *image,const ChannelType channel,
256% const NoiseType noise_type,ExceptionInfo *exception)
257%
258% A description of each parameter follows:
259%
260% o image: the image.
261%
262% o channel: the channel type.
263%
264% o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
265% Impulse, Laplacian, or Poisson.
266%
267% o exception: return any errors or warnings in this structure.
268%
269*/
270MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
271 ExceptionInfo *exception)
272{
273 Image
274 *noise_image;
275
276 noise_image=AddNoiseImageChannel(image,DefaultChannels,noise_type,exception);
277 return(noise_image);
278}
279
280MagickExport Image *AddNoiseImageChannel(const Image *image,
281 const ChannelType channel,const NoiseType noise_type,ExceptionInfo *exception)
282{
283#define AddNoiseImageTag "AddNoise/Image"
284
cristyfa112112010-01-04 17:48:07 +0000285 CacheView
286 *image_view,
287 *noise_view;
288
cristy3ed852e2009-09-05 21:47:34 +0000289 const char
290 *option;
291
292 Image
293 *noise_image;
294
295 long
296 progress,
297 y;
298
299 MagickBooleanType
300 status;
301
302 MagickRealType
303 attenuate;
304
305 RandomInfo
cristyfa112112010-01-04 17:48:07 +0000306 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +0000307
308 /*
309 Initialize noise image attributes.
310 */
311 assert(image != (const Image *) NULL);
312 assert(image->signature == MagickSignature);
313 if (image->debug != MagickFalse)
314 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
315 assert(exception != (ExceptionInfo *) NULL);
316 assert(exception->signature == MagickSignature);
317 noise_image=CloneImage(image,0,0,MagickTrue,exception);
318 if (noise_image == (Image *) NULL)
319 return((Image *) NULL);
320 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
321 {
322 InheritException(exception,&noise_image->exception);
323 noise_image=DestroyImage(noise_image);
324 return((Image *) NULL);
325 }
326 /*
327 Add noise in each row.
328 */
329 attenuate=1.0;
330 option=GetImageArtifact(image,"attenuate");
331 if (option != (char *) NULL)
cristyf2f27272009-12-17 14:48:46 +0000332 attenuate=StringToDouble(option);
cristy3ed852e2009-09-05 21:47:34 +0000333 status=MagickTrue;
334 progress=0;
335 random_info=AcquireRandomInfoThreadSet();
336 image_view=AcquireCacheView(image);
337 noise_view=AcquireCacheView(noise_image);
cristyb28d6472009-10-17 20:13:35 +0000338#if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
cristyb5d5f722009-11-04 03:03:49 +0000339 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000340#endif
341 for (y=0; y < (long) image->rows; y++)
342 {
343 MagickBooleanType
344 sync;
345
346 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000347 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000348
349 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000350 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000351
352 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000353 *restrict noise_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000354
355 register long
356 id,
357 x;
358
359 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000360 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000361
362 if (status == MagickFalse)
363 continue;
364 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
365 q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
366 exception);
367 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
368 {
369 status=MagickFalse;
370 continue;
371 }
372 indexes=GetCacheViewVirtualIndexQueue(image_view);
373 noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
374 id=GetOpenMPThreadId();
375 for (x=0; x < (long) image->columns; x++)
376 {
377 if ((channel & RedChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000378 q->red=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000379 p->red,noise_type,attenuate));
380 if ((channel & GreenChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000381 q->green=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000382 p->green,noise_type,attenuate));
383 if ((channel & BlueChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000384 q->blue=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000385 p->blue,noise_type,attenuate));
386 if ((channel & OpacityChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000387 q->opacity=ClampToQuantum(GenerateDifferentialNoise(random_info[id],
cristy3ed852e2009-09-05 21:47:34 +0000388 p->opacity,noise_type,attenuate));
389 if (((channel & IndexChannel) != 0) &&
390 (image->colorspace == CMYKColorspace))
cristyce70c172010-01-07 17:15:30 +0000391 noise_indexes[x]=(IndexPacket) ClampToQuantum(GenerateDifferentialNoise(
cristy3ed852e2009-09-05 21:47:34 +0000392 random_info[id],indexes[x],noise_type,attenuate));
393 p++;
394 q++;
395 }
396 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
397 if (sync == MagickFalse)
398 status=MagickFalse;
399 if (image->progress_monitor != (MagickProgressMonitor) NULL)
400 {
401 MagickBooleanType
402 proceed;
403
cristyb5d5f722009-11-04 03:03:49 +0000404#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000405 #pragma omp critical (MagickCore_AverageImages)
406#endif
407 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
408 image->rows);
409 if (proceed == MagickFalse)
410 status=MagickFalse;
411 }
412 }
413 noise_view=DestroyCacheView(noise_view);
414 image_view=DestroyCacheView(image_view);
415 random_info=DestroyRandomInfoThreadSet(random_info);
416 if (status == MagickFalse)
417 noise_image=DestroyImage(noise_image);
418 return(noise_image);
419}
420
421/*
422%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
423% %
424% %
425% %
426% B l u e S h i f t I m a g e %
427% %
428% %
429% %
430%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
431%
432% BlueShiftImage() mutes the colors of the image to simulate a scene at
433% nighttime in the moonlight.
434%
435% The format of the BlueShiftImage method is:
436%
437% Image *BlueShiftImage(const Image *image,const double factor,
438% ExceptionInfo *exception)
439%
440% A description of each parameter follows:
441%
442% o image: the image.
443%
444% o factor: the shift factor.
445%
446% o exception: return any errors or warnings in this structure.
447%
448*/
449MagickExport Image *BlueShiftImage(const Image *image,const double factor,
450 ExceptionInfo *exception)
451{
452#define BlueShiftImageTag "BlueShift/Image"
453
cristyc4c8d132010-01-07 01:58:38 +0000454 CacheView
455 *image_view,
456 *shift_view;
457
cristy3ed852e2009-09-05 21:47:34 +0000458 Image
459 *shift_image;
460
461 long
462 progress,
463 y;
464
465 MagickBooleanType
466 status;
467
cristy3ed852e2009-09-05 21:47:34 +0000468 /*
469 Allocate blue shift image.
470 */
471 assert(image != (const Image *) NULL);
472 assert(image->signature == MagickSignature);
473 if (image->debug != MagickFalse)
474 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
475 assert(exception != (ExceptionInfo *) NULL);
476 assert(exception->signature == MagickSignature);
477 shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,
478 exception);
479 if (shift_image == (Image *) NULL)
480 return((Image *) NULL);
481 if (SetImageStorageClass(shift_image,DirectClass) == MagickFalse)
482 {
483 InheritException(exception,&shift_image->exception);
484 shift_image=DestroyImage(shift_image);
485 return((Image *) NULL);
486 }
487 /*
488 Blue-shift DirectClass image.
489 */
490 status=MagickTrue;
491 progress=0;
492 image_view=AcquireCacheView(image);
493 shift_view=AcquireCacheView(shift_image);
cristyb28d6472009-10-17 20:13:35 +0000494#if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
cristyb5d5f722009-11-04 03:03:49 +0000495 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000496#endif
497 for (y=0; y < (long) image->rows; y++)
498 {
499 MagickBooleanType
500 sync;
501
502 MagickPixelPacket
503 pixel;
504
505 Quantum
506 quantum;
507
508 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000509 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000510
511 register long
512 x;
513
514 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000515 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000516
517 if (status == MagickFalse)
518 continue;
519 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
520 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
521 exception);
522 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
523 {
524 status=MagickFalse;
525 continue;
526 }
527 for (x=0; x < (long) image->columns; x++)
528 {
cristyce70c172010-01-07 17:15:30 +0000529 quantum=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000530 if (p->green < quantum)
cristyce70c172010-01-07 17:15:30 +0000531 quantum=GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000532 if (p->blue < quantum)
cristyce70c172010-01-07 17:15:30 +0000533 quantum=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000534 pixel.red=0.5*(p->red+factor*quantum);
535 pixel.green=0.5*(p->green+factor*quantum);
536 pixel.blue=0.5*(p->blue+factor*quantum);
cristyce70c172010-01-07 17:15:30 +0000537 quantum=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000538 if (p->green > quantum)
cristyce70c172010-01-07 17:15:30 +0000539 quantum=GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000540 if (p->blue > quantum)
cristyce70c172010-01-07 17:15:30 +0000541 quantum=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000542 pixel.red=0.5*(pixel.red+factor*quantum);
543 pixel.green=0.5*(pixel.green+factor*quantum);
544 pixel.blue=0.5*(pixel.blue+factor*quantum);
cristyce70c172010-01-07 17:15:30 +0000545 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
546 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
547 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +0000548 p++;
549 q++;
550 }
551 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
552 if (sync == MagickFalse)
553 status=MagickFalse;
554 if (image->progress_monitor != (MagickProgressMonitor) NULL)
555 {
556 MagickBooleanType
557 proceed;
558
cristyb28d6472009-10-17 20:13:35 +0000559#if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
cristy3ed852e2009-09-05 21:47:34 +0000560 #pragma omp critical (MagickCore_BlueShiftImage)
561#endif
562 proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
563 image->rows);
564 if (proceed == MagickFalse)
565 status=MagickFalse;
566 }
567 }
568 image_view=DestroyCacheView(image_view);
569 shift_view=DestroyCacheView(shift_view);
570 if (status == MagickFalse)
571 shift_image=DestroyImage(shift_image);
572 return(shift_image);
573}
574
575/*
576%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
577% %
578% %
579% %
580% C h a r c o a l I m a g e %
581% %
582% %
583% %
584%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
585%
586% CharcoalImage() creates a new image that is a copy of an existing one with
587% the edge highlighted. It allocates the memory necessary for the new Image
588% structure and returns a pointer to the new image.
589%
590% The format of the CharcoalImage method is:
591%
592% Image *CharcoalImage(const Image *image,const double radius,
593% const double sigma,ExceptionInfo *exception)
594%
595% A description of each parameter follows:
596%
597% o image: the image.
598%
599% o radius: the radius of the pixel neighborhood.
600%
601% o sigma: the standard deviation of the Gaussian, in pixels.
602%
603% o exception: return any errors or warnings in this structure.
604%
605*/
606MagickExport Image *CharcoalImage(const Image *image,const double radius,
607 const double sigma,ExceptionInfo *exception)
608{
609 Image
610 *charcoal_image,
611 *clone_image,
612 *edge_image;
613
614 assert(image != (Image *) NULL);
615 assert(image->signature == MagickSignature);
616 if (image->debug != MagickFalse)
617 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
618 assert(exception != (ExceptionInfo *) NULL);
619 assert(exception->signature == MagickSignature);
620 clone_image=CloneImage(image,0,0,MagickTrue,exception);
621 if (clone_image == (Image *) NULL)
622 return((Image *) NULL);
623 (void) SetImageType(clone_image,GrayscaleType);
624 edge_image=EdgeImage(clone_image,radius,exception);
625 clone_image=DestroyImage(clone_image);
626 if (edge_image == (Image *) NULL)
627 return((Image *) NULL);
628 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
629 edge_image=DestroyImage(edge_image);
630 if (charcoal_image == (Image *) NULL)
631 return((Image *) NULL);
632 (void) NormalizeImage(charcoal_image);
633 (void) NegateImage(charcoal_image,MagickFalse);
634 (void) SetImageType(charcoal_image,GrayscaleType);
635 return(charcoal_image);
636}
637
638/*
639%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
640% %
641% %
642% %
643% C o l o r i z e I m a g e %
644% %
645% %
646% %
647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648%
649% ColorizeImage() blends the fill color with each pixel in the image.
650% A percentage blend is specified with opacity. Control the application
651% of different color components by specifying a different percentage for
652% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
653%
654% The format of the ColorizeImage method is:
655%
656% Image *ColorizeImage(const Image *image,const char *opacity,
657% const PixelPacket colorize,ExceptionInfo *exception)
658%
659% A description of each parameter follows:
660%
661% o image: the image.
662%
663% o opacity: A character string indicating the level of opacity as a
664% percentage.
665%
666% o colorize: A color value.
667%
668% o exception: return any errors or warnings in this structure.
669%
670*/
671MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
672 const PixelPacket colorize,ExceptionInfo *exception)
673{
674#define ColorizeImageTag "Colorize/Image"
675
cristyc4c8d132010-01-07 01:58:38 +0000676 CacheView
677 *colorize_view,
678 *image_view;
679
cristy3ed852e2009-09-05 21:47:34 +0000680 GeometryInfo
681 geometry_info;
682
683 Image
684 *colorize_image;
685
686 long
687 progress,
688 y;
689
690 MagickBooleanType
691 status;
692
693 MagickPixelPacket
694 pixel;
695
696 MagickStatusType
697 flags;
698
cristy3ed852e2009-09-05 21:47:34 +0000699 /*
700 Allocate colorized image.
701 */
702 assert(image != (const Image *) NULL);
703 assert(image->signature == MagickSignature);
704 if (image->debug != MagickFalse)
705 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
706 assert(exception != (ExceptionInfo *) NULL);
707 assert(exception->signature == MagickSignature);
708 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
709 exception);
710 if (colorize_image == (Image *) NULL)
711 return((Image *) NULL);
712 if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
713 {
714 InheritException(exception,&colorize_image->exception);
715 colorize_image=DestroyImage(colorize_image);
716 return((Image *) NULL);
717 }
718 if (opacity == (const char *) NULL)
719 return(colorize_image);
720 /*
721 Determine RGB values of the pen color.
722 */
723 flags=ParseGeometry(opacity,&geometry_info);
724 pixel.red=geometry_info.rho;
725 pixel.green=geometry_info.rho;
726 pixel.blue=geometry_info.rho;
727 pixel.opacity=(MagickRealType) OpaqueOpacity;
728 if ((flags & SigmaValue) != 0)
729 pixel.green=geometry_info.sigma;
730 if ((flags & XiValue) != 0)
731 pixel.blue=geometry_info.xi;
732 if ((flags & PsiValue) != 0)
733 pixel.opacity=geometry_info.psi;
734 /*
735 Colorize DirectClass image.
736 */
737 status=MagickTrue;
738 progress=0;
739 image_view=AcquireCacheView(image);
740 colorize_view=AcquireCacheView(colorize_image);
cristyb28d6472009-10-17 20:13:35 +0000741#if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
cristyb5d5f722009-11-04 03:03:49 +0000742 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000743#endif
744 for (y=0; y < (long) image->rows; y++)
745 {
746 MagickBooleanType
747 sync;
748
749 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000750 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000751
752 register long
753 x;
754
755 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000756 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000757
758 if (status == MagickFalse)
759 continue;
760 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
761 q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
762 exception);
763 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
764 {
765 status=MagickFalse;
766 continue;
767 }
768 for (x=0; x < (long) image->columns; x++)
769 {
770 q->red=(Quantum) ((p->red*(100.0-pixel.red)+
771 colorize.red*pixel.red)/100.0);
772 q->green=(Quantum) ((p->green*(100.0-pixel.green)+
773 colorize.green*pixel.green)/100.0);
774 q->blue=(Quantum) ((p->blue*(100.0-pixel.blue)+
775 colorize.blue*pixel.blue)/100.0);
776 q->opacity=(Quantum) ((p->opacity*(100.0-pixel.opacity)+
777 colorize.opacity*pixel.opacity)/100.0);
778 p++;
779 q++;
780 }
781 sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
782 if (sync == MagickFalse)
783 status=MagickFalse;
784 if (image->progress_monitor != (MagickProgressMonitor) NULL)
785 {
786 MagickBooleanType
787 proceed;
788
cristyb28d6472009-10-17 20:13:35 +0000789#if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
cristy3ed852e2009-09-05 21:47:34 +0000790 #pragma omp critical (MagickCore_ColorizeImage)
791#endif
792 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
793 if (proceed == MagickFalse)
794 status=MagickFalse;
795 }
796 }
797 image_view=DestroyCacheView(image_view);
798 colorize_view=DestroyCacheView(colorize_view);
799 if (status == MagickFalse)
800 colorize_image=DestroyImage(colorize_image);
801 return(colorize_image);
802}
803
804/*
805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806% %
807% %
808% %
cristy3ed852e2009-09-05 21:47:34 +0000809+ D e s t r o y F x I n f o %
810% %
811% %
812% %
813%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814%
815% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
816%
817% The format of the DestroyFxInfo method is:
818%
819% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
820%
821% A description of each parameter follows:
822%
823% o fx_info: the fx info.
824%
825*/
826MagickExport FxInfo *DestroyFxInfo(FxInfo *fx_info)
827{
828 register long
829 i;
830
831 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
832 fx_info->expression=DestroyString(fx_info->expression);
833 fx_info->symbols=DestroySplayTree(fx_info->symbols);
834 fx_info->colors=DestroySplayTree(fx_info->colors);
835 for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
836 fx_info->resample_filter[i]=DestroyResampleFilter(
837 fx_info->resample_filter[i]);
838 fx_info->resample_filter=(ResampleFilter **) RelinquishMagickMemory(
839 fx_info->resample_filter);
840 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
841 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
842 return(fx_info);
843}
844
845/*
846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847% %
848% %
849% %
850% E v a l u a t e I m a g e %
851% %
852% %
853% %
854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855%
856% EvaluateImage() applies a value to the image with an arithmetic, relational,
857% or logical operator to an image. Use these operations to lighten or darken
858% an image, to increase or decrease contrast in an image, or to produce the
859% "negative" of an image.
860%
861% The format of the EvaluateImageChannel method is:
862%
863% MagickBooleanType EvaluateImage(Image *image,
864% const MagickEvaluateOperator op,const double value,
865% ExceptionInfo *exception)
866% MagickBooleanType EvaluateImageChannel(Image *image,
867% const ChannelType channel,const MagickEvaluateOperator op,
868% const double value,ExceptionInfo *exception)
869%
870% A description of each parameter follows:
871%
872% o image: the image.
873%
874% o channel: the channel.
875%
876% o op: A channel op.
877%
878% o value: A value value.
879%
880% o exception: return any errors or warnings in this structure.
881%
882*/
883
884static inline double MagickMax(const double x,const double y)
885{
886 if (x > y)
887 return(x);
888 return(y);
889}
890
891static inline double MagickMin(const double x,const double y)
892{
893 if (x < y)
894 return(x);
895 return(y);
896}
897
898static Quantum ApplyEvaluateOperator(RandomInfo *random_info,Quantum pixel,
899 const MagickEvaluateOperator op,const MagickRealType value)
900{
901 MagickRealType
902 result;
903
904 result=0.0;
905 switch (op)
906 {
907 case UndefinedEvaluateOperator:
908 break;
909 case AddEvaluateOperator:
910 {
911 result=(MagickRealType) (pixel+value);
912 break;
913 }
914 case AddModulusEvaluateOperator:
915 {
916 /* This will return a 'floored modulus' of the addition which will
917 * always return a positive result, regardless of if the result is
918 * positive or negative, and thus in the 0 to QuantumRange range.
919 *
920 * WARNING: this is NOT the same as a % or fmod() which returns a
921 * 'truncated modulus' result, where floor() is replaced by trunc()
922 * and could return a negative result, which will be clipped.
923 */
924 result = pixel+value;
925 result -= (QuantumRange+1)*floor(result/(QuantumRange+1));
926 break;
927 }
928 case AndEvaluateOperator:
929 {
930 result=(MagickRealType) ((unsigned long) pixel & (unsigned long)
931 (value+0.5));
932 break;
933 }
934 case CosineEvaluateOperator:
935 {
936 result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
937 QuantumScale*pixel*value))+0.5));
938 break;
939 }
940 case DivideEvaluateOperator:
941 {
942 result=pixel/(value == 0.0 ? 1.0 : value);
943 break;
944 }
945 case GaussianNoiseEvaluateOperator:
946 {
947 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
948 GaussianNoise,value);
949 break;
950 }
951 case ImpulseNoiseEvaluateOperator:
952 {
953 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
954 ImpulseNoise,value);
955 break;
956 }
957 case LaplacianNoiseEvaluateOperator:
958 {
959 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
960 LaplacianNoise,value);
961 break;
962 }
963 case LeftShiftEvaluateOperator:
964 {
965 result=(MagickRealType) ((unsigned long) pixel << (unsigned long)
966 (value+0.5));
967 break;
968 }
969 case LogEvaluateOperator:
970 {
971 result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
972 pixel+1.0))/log((double) (value+1.0)));
973 break;
974 }
975 case MaxEvaluateOperator:
976 {
977 result=(MagickRealType) MagickMax((double) pixel,value);
978 break;
979 }
980 case MinEvaluateOperator:
981 {
982 result=(MagickRealType) MagickMin((double) pixel,value);
983 break;
984 }
985 case MultiplicativeNoiseEvaluateOperator:
986 {
987 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
988 MultiplicativeGaussianNoise,value);
989 break;
990 }
991 case MultiplyEvaluateOperator:
992 {
993 result=(MagickRealType) (value*pixel);
994 break;
995 }
996 case OrEvaluateOperator:
997 {
998 result=(MagickRealType) ((unsigned long) pixel | (unsigned long)
999 (value+0.5));
1000 break;
1001 }
1002 case PoissonNoiseEvaluateOperator:
1003 {
1004 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
1005 PoissonNoise,value);
1006 break;
1007 }
1008 case PowEvaluateOperator:
1009 {
1010 result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
1011 (double) value));
1012 break;
1013 }
1014 case RightShiftEvaluateOperator:
1015 {
1016 result=(MagickRealType) ((unsigned long) pixel >> (unsigned long)
1017 (value+0.5));
1018 break;
1019 }
1020 case SetEvaluateOperator:
1021 {
1022 result=value;
1023 break;
1024 }
1025 case SineEvaluateOperator:
1026 {
1027 result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
1028 QuantumScale*pixel*value))+0.5));
1029 break;
1030 }
1031 case SubtractEvaluateOperator:
1032 {
1033 result=(MagickRealType) (pixel-value);
1034 break;
1035 }
1036 case ThresholdEvaluateOperator:
1037 {
1038 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
1039 QuantumRange);
1040 break;
1041 }
1042 case ThresholdBlackEvaluateOperator:
1043 {
1044 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
1045 break;
1046 }
1047 case ThresholdWhiteEvaluateOperator:
1048 {
1049 result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
1050 pixel);
1051 break;
1052 }
1053 case UniformNoiseEvaluateOperator:
1054 {
1055 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
1056 UniformNoise,value);
1057 break;
1058 }
1059 case XorEvaluateOperator:
1060 {
1061 result=(MagickRealType) ((unsigned long) pixel ^ (unsigned long)
1062 (value+0.5));
1063 break;
1064 }
1065 }
cristyce70c172010-01-07 17:15:30 +00001066 return(ClampToQuantum(result));
cristy3ed852e2009-09-05 21:47:34 +00001067}
1068
1069MagickExport MagickBooleanType EvaluateImage(Image *image,
1070 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
1071{
1072 MagickBooleanType
1073 status;
1074
1075 status=EvaluateImageChannel(image,AllChannels,op,value,exception);
1076 return(status);
1077}
1078
1079MagickExport MagickBooleanType EvaluateImageChannel(Image *image,
1080 const ChannelType channel,const MagickEvaluateOperator op,const double value,
1081 ExceptionInfo *exception)
1082{
1083#define EvaluateImageTag "Evaluate/Image "
1084
cristyfa112112010-01-04 17:48:07 +00001085 CacheView
1086 *image_view;
1087
cristy3ed852e2009-09-05 21:47:34 +00001088 long
1089 progress,
1090 y;
1091
1092 MagickBooleanType
1093 status;
1094
1095 RandomInfo
cristyfa112112010-01-04 17:48:07 +00001096 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00001097
1098 assert(image != (Image *) NULL);
1099 assert(image->signature == MagickSignature);
1100 if (image->debug != MagickFalse)
1101 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1102 assert(exception != (ExceptionInfo *) NULL);
1103 assert(exception->signature == MagickSignature);
1104 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1105 {
1106 InheritException(exception,&image->exception);
1107 return(MagickFalse);
1108 }
1109 status=MagickTrue;
1110 progress=0;
1111 random_info=AcquireRandomInfoThreadSet();
1112 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001113#if defined(MAGICKCORE_OPENMP_SUPPORT)
1114 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001115#endif
1116 for (y=0; y < (long) image->rows; y++)
1117 {
1118 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001119 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001120
1121 register long
1122 id,
1123 x;
1124
1125 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001126 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001127
1128 if (status == MagickFalse)
1129 continue;
1130 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1131 if (q == (PixelPacket *) NULL)
1132 {
1133 status=MagickFalse;
1134 continue;
1135 }
1136 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1137 id=GetOpenMPThreadId();
1138 for (x=0; x < (long) image->columns; x++)
1139 {
1140 if ((channel & RedChannel) != 0)
1141 q->red=ApplyEvaluateOperator(random_info[id],q->red,op,value);
1142 if ((channel & GreenChannel) != 0)
1143 q->green=ApplyEvaluateOperator(random_info[id],q->green,op,value);
1144 if ((channel & BlueChannel) != 0)
1145 q->blue=ApplyEvaluateOperator(random_info[id],q->blue,op,value);
1146 if ((channel & OpacityChannel) != 0)
1147 {
1148 if (image->matte == MagickFalse)
1149 q->opacity=ApplyEvaluateOperator(random_info[id],q->opacity,op,
1150 value);
1151 else
1152 q->opacity=(Quantum) QuantumRange-ApplyEvaluateOperator(
cristy46f08202010-01-10 04:04:21 +00001153 random_info[id],(Quantum) GetAlphaPixelComponent(q),op,value);
cristy3ed852e2009-09-05 21:47:34 +00001154 }
1155 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
1156 indexes[x]=(IndexPacket) ApplyEvaluateOperator(random_info[id],
1157 indexes[x],op,value);
1158 q++;
1159 }
1160 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1161 status=MagickFalse;
1162 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1163 {
1164 MagickBooleanType
1165 proceed;
1166
cristyb5d5f722009-11-04 03:03:49 +00001167#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001168 #pragma omp critical (MagickCore_EvaluateImageChannel)
1169#endif
1170 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
1171 if (proceed == MagickFalse)
1172 status=MagickFalse;
1173 }
1174 }
1175 image_view=DestroyCacheView(image_view);
1176 random_info=DestroyRandomInfoThreadSet(random_info);
1177 return(status);
1178}
1179
1180/*
1181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1182% %
1183% %
1184% %
1185% F u n c t i o n I m a g e %
1186% %
1187% %
1188% %
1189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1190%
1191% FunctionImage() applies a value to the image with an arithmetic, relational,
1192% or logical operator to an image. Use these operations to lighten or darken
1193% an image, to increase or decrease contrast in an image, or to produce the
1194% "negative" of an image.
1195%
1196% The format of the FunctionImageChannel method is:
1197%
1198% MagickBooleanType FunctionImage(Image *image,
1199% const MagickFunction function,const long number_parameters,
1200% const double *parameters,ExceptionInfo *exception)
1201% MagickBooleanType FunctionImageChannel(Image *image,
1202% const ChannelType channel,const MagickFunction function,
1203% const long number_parameters,const double *argument,
1204% ExceptionInfo *exception)
1205%
1206% A description of each parameter follows:
1207%
1208% o image: the image.
1209%
1210% o channel: the channel.
1211%
1212% o function: A channel function.
1213%
1214% o parameters: one or more parameters.
1215%
1216% o exception: return any errors or warnings in this structure.
1217%
1218*/
1219
1220static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristy67d99e62009-09-12 15:38:30 +00001221 const unsigned long number_parameters,const double *parameters,
cristy3ed852e2009-09-05 21:47:34 +00001222 ExceptionInfo *exception)
1223{
1224 MagickRealType
1225 result;
1226
1227 register long
1228 i;
1229
1230 (void) exception;
1231 result=0.0;
1232 switch (function)
1233 {
1234 case PolynomialFunction:
1235 {
1236 /*
1237 * Polynomial
1238 * Parameters: polynomial constants, highest to lowest order
1239 * For example: c0*x^3 + c1*x^2 + c2*x + c3
1240 */
1241 result=0.0;
1242 for (i=0; i < (long) number_parameters; i++)
1243 result = result*QuantumScale*pixel + parameters[i];
1244 result *= QuantumRange;
1245 break;
1246 }
1247 case SinusoidFunction:
1248 {
1249 /* Sinusoid Function
1250 * Parameters: Freq, Phase, Ampl, bias
1251 */
1252 double freq,phase,ampl,bias;
1253 freq = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
1254 phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
1255 ampl = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
1256 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
1257 result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
1258 (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
1259 break;
1260 }
1261 case ArcsinFunction:
1262 {
1263 /* Arcsin Function (peged at range limits for invalid results)
1264 * Parameters: Width, Center, Range, Bias
1265 */
1266 double width,range,center,bias;
1267 width = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
1268 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
1269 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
1270 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
1271 result = 2.0/width*(QuantumScale*pixel - center);
1272 if ( result <= -1.0 )
1273 result = bias - range/2.0;
1274 else if ( result >= 1.0 )
1275 result = bias + range/2.0;
1276 else
1277 result=range/MagickPI*asin((double)result) + bias;
1278 result *= QuantumRange;
1279 break;
1280 }
1281 case ArctanFunction:
1282 {
1283 /* Arctan Function
1284 * Parameters: Slope, Center, Range, Bias
1285 */
1286 double slope,range,center,bias;
1287 slope = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
1288 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
1289 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
1290 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
1291 result = MagickPI*slope*(QuantumScale*pixel - center);
1292 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
1293 result) + bias ) );
1294 break;
1295 }
1296 case UndefinedFunction:
1297 break;
1298 }
cristyce70c172010-01-07 17:15:30 +00001299 return(ClampToQuantum(result));
cristy3ed852e2009-09-05 21:47:34 +00001300}
1301
1302MagickExport MagickBooleanType FunctionImage(Image *image,
1303 const MagickFunction function,const unsigned long number_parameters,
1304 const double *parameters,ExceptionInfo *exception)
1305{
1306 MagickBooleanType
1307 status;
1308
1309 status=FunctionImageChannel(image,AllChannels,function,number_parameters,
1310 parameters,exception);
1311 return(status);
1312}
1313
1314MagickExport MagickBooleanType FunctionImageChannel(Image *image,
1315 const ChannelType channel,const MagickFunction function,
1316 const unsigned long number_parameters,const double *parameters,
1317 ExceptionInfo *exception)
1318{
1319#define FunctionImageTag "Function/Image "
1320
cristyc4c8d132010-01-07 01:58:38 +00001321 CacheView
1322 *image_view;
1323
cristy3ed852e2009-09-05 21:47:34 +00001324 long
1325 progress,
1326 y;
1327
1328 MagickBooleanType
1329 status;
1330
cristy3ed852e2009-09-05 21:47:34 +00001331 assert(image != (Image *) NULL);
1332 assert(image->signature == MagickSignature);
1333 if (image->debug != MagickFalse)
1334 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1335 assert(exception != (ExceptionInfo *) NULL);
1336 assert(exception->signature == MagickSignature);
1337 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1338 {
1339 InheritException(exception,&image->exception);
1340 return(MagickFalse);
1341 }
1342 status=MagickTrue;
1343 progress=0;
1344 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001345#if defined(MAGICKCORE_OPENMP_SUPPORT)
1346 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001347#endif
1348 for (y=0; y < (long) image->rows; y++)
1349 {
1350 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001351 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001352
1353 register long
1354 x;
1355
1356 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001357 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001358
1359 if (status == MagickFalse)
1360 continue;
1361 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1362 if (q == (PixelPacket *) NULL)
1363 {
1364 status=MagickFalse;
1365 continue;
1366 }
1367 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1368 for (x=0; x < (long) image->columns; x++)
1369 {
1370 if ((channel & RedChannel) != 0)
1371 q->red=ApplyFunction(q->red,function,number_parameters,parameters,
1372 exception);
1373 if ((channel & GreenChannel) != 0)
1374 q->green=ApplyFunction(q->green,function,number_parameters,parameters,
1375 exception);
1376 if ((channel & BlueChannel) != 0)
1377 q->blue=ApplyFunction(q->blue,function,number_parameters,parameters,
1378 exception);
1379 if ((channel & OpacityChannel) != 0)
1380 {
1381 if (image->matte == MagickFalse)
1382 q->opacity=ApplyFunction(q->opacity,function,number_parameters,
1383 parameters,exception);
1384 else
cristy46f08202010-01-10 04:04:21 +00001385 q->opacity=(Quantum) QuantumRange-ApplyFunction(
1386 GetAlphaPixelComponent(q),function,number_parameters,parameters,
cristy3ed852e2009-09-05 21:47:34 +00001387 exception);
1388 }
1389 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
1390 indexes[x]=(IndexPacket) ApplyFunction(indexes[x],function,
1391 number_parameters,parameters,exception);
1392 q++;
1393 }
1394 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1395 status=MagickFalse;
1396 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1397 {
1398 MagickBooleanType
1399 proceed;
1400
cristyb5d5f722009-11-04 03:03:49 +00001401#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001402 #pragma omp critical (MagickCore_FunctionImageChannel)
1403#endif
1404 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1405 if (proceed == MagickFalse)
1406 status=MagickFalse;
1407 }
1408 }
1409 image_view=DestroyCacheView(image_view);
1410 return(status);
1411}
1412
1413/*
1414%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1415% %
1416% %
1417% %
1418+ 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 %
1419% %
1420% %
1421% %
1422%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1423%
1424% FxEvaluateChannelExpression() evaluates an expression and returns the
1425% results.
1426%
1427% The format of the FxEvaluateExpression method is:
1428%
1429% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
1430% const ChannelType channel,const long x,const long y,
1431% MagickRealType *alpha,Exceptioninfo *exception)
1432% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1433% MagickRealType *alpha,Exceptioninfo *exception)
1434%
1435% A description of each parameter follows:
1436%
1437% o fx_info: the fx info.
1438%
1439% o channel: the channel.
1440%
1441% o x,y: the pixel position.
1442%
1443% o alpha: the result.
1444%
1445% o exception: return any errors or warnings in this structure.
1446%
1447*/
1448
1449static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
1450 ChannelType channel,const char *symbol,ExceptionInfo *exception)
1451{
1452 char
1453 key[MaxTextExtent],
1454 statistic[MaxTextExtent];
1455
1456 const char
1457 *value;
1458
1459 register const char
1460 *p;
1461
1462 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1463 if (*p == '.')
1464 switch (*++p) /* e.g. depth.r */
1465 {
1466 case 'r': channel=RedChannel; break;
1467 case 'g': channel=GreenChannel; break;
1468 case 'b': channel=BlueChannel; break;
1469 case 'c': channel=CyanChannel; break;
1470 case 'm': channel=MagentaChannel; break;
1471 case 'y': channel=YellowChannel; break;
1472 case 'k': channel=BlackChannel; break;
1473 default: break;
1474 }
1475 (void) FormatMagickString(key,MaxTextExtent,"%p.%ld.%s",image,(long) channel,
1476 symbol);
1477 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1478 if (value != (const char *) NULL)
cristyf2f27272009-12-17 14:48:46 +00001479 return(QuantumScale*StringToDouble(value));
cristy3ed852e2009-09-05 21:47:34 +00001480 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1481 if (LocaleNCompare(symbol,"depth",5) == 0)
1482 {
1483 unsigned long
1484 depth;
1485
1486 depth=GetImageChannelDepth(image,channel,exception);
1487 (void) FormatMagickString(statistic,MaxTextExtent,"%lu",depth);
1488 }
1489 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1490 {
1491 double
1492 kurtosis,
1493 skewness;
1494
1495 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1496 exception);
cristy8cd5b312010-01-07 01:10:24 +00001497 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",kurtosis);
cristy3ed852e2009-09-05 21:47:34 +00001498 }
1499 if (LocaleNCompare(symbol,"maxima",6) == 0)
1500 {
1501 double
1502 maxima,
1503 minima;
1504
1505 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristy8cd5b312010-01-07 01:10:24 +00001506 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",maxima);
cristy3ed852e2009-09-05 21:47:34 +00001507 }
1508 if (LocaleNCompare(symbol,"mean",4) == 0)
1509 {
1510 double
1511 mean,
1512 standard_deviation;
1513
1514 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1515 exception);
cristy8cd5b312010-01-07 01:10:24 +00001516 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",mean);
cristy3ed852e2009-09-05 21:47:34 +00001517 }
1518 if (LocaleNCompare(symbol,"minima",6) == 0)
1519 {
1520 double
1521 maxima,
1522 minima;
1523
1524 (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
cristy8cd5b312010-01-07 01:10:24 +00001525 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",minima);
cristy3ed852e2009-09-05 21:47:34 +00001526 }
1527 if (LocaleNCompare(symbol,"skewness",8) == 0)
1528 {
1529 double
1530 kurtosis,
1531 skewness;
1532
1533 (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
1534 exception);
cristy8cd5b312010-01-07 01:10:24 +00001535 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",skewness);
cristy3ed852e2009-09-05 21:47:34 +00001536 }
1537 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1538 {
1539 double
1540 mean,
1541 standard_deviation;
1542
1543 (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
1544 exception);
cristy8cd5b312010-01-07 01:10:24 +00001545 (void) FormatMagickString(statistic,MaxTextExtent,"%.15g",
cristy3ed852e2009-09-05 21:47:34 +00001546 standard_deviation);
1547 }
1548 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1549 ConstantString(statistic));
cristyf2f27272009-12-17 14:48:46 +00001550 return(QuantumScale*StringToDouble(statistic));
cristy3ed852e2009-09-05 21:47:34 +00001551}
1552
1553static MagickRealType
1554 FxEvaluateSubexpression(FxInfo *,const ChannelType,const long,const long,
1555 const char *,MagickRealType *,ExceptionInfo *);
1556
1557static inline MagickRealType FxMax(FxInfo *fx_info,const ChannelType channel,
1558 const long x,const long y,const char *expression,ExceptionInfo *exception)
1559{
1560 MagickRealType
1561 alpha,
1562 beta;
1563
1564 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1565 return((MagickRealType) MagickMax((double) alpha,(double) beta));
1566}
1567
1568static inline MagickRealType FxMin(FxInfo *fx_info,ChannelType channel,
1569 const long x,const long y,const char *expression,ExceptionInfo *exception)
1570{
1571 MagickRealType
1572 alpha,
1573 beta;
1574
1575 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1576 return((MagickRealType) MagickMin((double) alpha,(double) beta));
1577}
1578
1579static inline const char *FxSubexpression(const char *expression,
1580 ExceptionInfo *exception)
1581{
1582 const char
1583 *subexpression;
1584
1585 register long
1586 level;
1587
1588 level=0;
1589 subexpression=expression;
1590 while ((*subexpression != '\0') &&
1591 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1592 {
1593 if (strchr("(",(int) *subexpression) != (char *) NULL)
1594 level++;
1595 else
1596 if (strchr(")",(int) *subexpression) != (char *) NULL)
1597 level--;
1598 subexpression++;
1599 }
1600 if (*subexpression == '\0')
1601 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1602 "UnbalancedParenthesis","`%s'",expression);
1603 return(subexpression);
1604}
1605
1606static MagickRealType FxGetSymbol(FxInfo *fx_info,const ChannelType channel,
1607 const long x,const long y,const char *expression,ExceptionInfo *exception)
1608{
1609 char
1610 *q,
1611 subexpression[MaxTextExtent],
1612 symbol[MaxTextExtent];
1613
1614 const char
1615 *p,
1616 *value;
1617
1618 Image
1619 *image;
1620
1621 MagickPixelPacket
1622 pixel;
1623
1624 MagickRealType
1625 alpha,
1626 beta;
1627
1628 PointInfo
1629 point;
1630
1631 register long
1632 i;
1633
1634 size_t
1635 length;
1636
1637 unsigned long
1638 level;
1639
1640 p=expression;
1641 i=GetImageIndexInList(fx_info->images);
1642 level=0;
1643 point.x=(double) x;
1644 point.y=(double) y;
1645 if (isalpha((int) *(p+1)) == 0)
1646 {
1647 if (strchr("suv",(int) *p) != (char *) NULL)
1648 {
1649 switch (*p)
1650 {
1651 case 's':
1652 default:
1653 {
1654 i=GetImageIndexInList(fx_info->images);
1655 break;
1656 }
1657 case 'u': i=0; break;
1658 case 'v': i=1; break;
1659 }
1660 p++;
1661 if (*p == '[')
1662 {
1663 level++;
1664 q=subexpression;
1665 for (p++; *p != '\0'; )
1666 {
1667 if (*p == '[')
1668 level++;
1669 else
1670 if (*p == ']')
1671 {
1672 level--;
1673 if (level == 0)
1674 break;
1675 }
1676 *q++=(*p++);
1677 }
1678 *q='\0';
1679 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1680 &beta,exception);
1681 i=(long) (alpha+0.5);
1682 p++;
1683 }
1684 if (*p == '.')
1685 p++;
1686 }
1687 if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1688 {
1689 p++;
1690 if (*p == '{')
1691 {
1692 level++;
1693 q=subexpression;
1694 for (p++; *p != '\0'; )
1695 {
1696 if (*p == '{')
1697 level++;
1698 else
1699 if (*p == '}')
1700 {
1701 level--;
1702 if (level == 0)
1703 break;
1704 }
1705 *q++=(*p++);
1706 }
1707 *q='\0';
1708 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1709 &beta,exception);
1710 point.x=alpha;
1711 point.y=beta;
1712 p++;
1713 }
1714 else
1715 if (*p == '[')
1716 {
1717 level++;
1718 q=subexpression;
1719 for (p++; *p != '\0'; )
1720 {
1721 if (*p == '[')
1722 level++;
1723 else
1724 if (*p == ']')
1725 {
1726 level--;
1727 if (level == 0)
1728 break;
1729 }
1730 *q++=(*p++);
1731 }
1732 *q='\0';
1733 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1734 &beta,exception);
1735 point.x+=alpha;
1736 point.y+=beta;
1737 p++;
1738 }
1739 if (*p == '.')
1740 p++;
1741 }
1742 }
1743 length=GetImageListLength(fx_info->images);
1744 while (i < 0)
1745 i+=(long) length;
1746 i%=length;
1747 image=GetImageFromList(fx_info->images,i);
1748 if (image == (Image *) NULL)
1749 {
1750 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1751 "NoSuchImage","`%s'",expression);
1752 return(0.0);
1753 }
1754 (void) ResamplePixelColor(fx_info->resample_filter[i],point.x,point.y,&pixel);
1755 if ((strlen(p) > 2) &&
1756 (LocaleCompare(p,"intensity") != 0) &&
1757 (LocaleCompare(p,"luminance") != 0) &&
1758 (LocaleCompare(p,"hue") != 0) &&
1759 (LocaleCompare(p,"saturation") != 0) &&
1760 (LocaleCompare(p,"lightness") != 0))
1761 {
1762 char
1763 name[MaxTextExtent];
1764
1765 (void) CopyMagickString(name,p,MaxTextExtent);
1766 for (q=name+(strlen(name)-1); q > name; q--)
1767 {
1768 if (*q == ')')
1769 break;
1770 if (*q == '.')
1771 {
1772 *q='\0';
1773 break;
1774 }
1775 }
1776 if ((strlen(name) > 2) &&
1777 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1778 {
1779 MagickPixelPacket
1780 *color;
1781
1782 color=(MagickPixelPacket *) GetValueFromSplayTree(fx_info->colors,
1783 name);
1784 if (color != (MagickPixelPacket *) NULL)
1785 {
1786 pixel=(*color);
1787 p+=strlen(name);
1788 }
1789 else
1790 if (QueryMagickColor(name,&pixel,fx_info->exception) != MagickFalse)
1791 {
1792 (void) AddValueToSplayTree(fx_info->colors,ConstantString(name),
1793 CloneMagickPixelPacket(&pixel));
1794 p+=strlen(name);
1795 }
1796 }
1797 }
1798 (void) CopyMagickString(symbol,p,MaxTextExtent);
1799 StripString(symbol);
1800 if (*symbol == '\0')
1801 {
1802 switch (channel)
1803 {
1804 case RedChannel: return(QuantumScale*pixel.red);
1805 case GreenChannel: return(QuantumScale*pixel.green);
1806 case BlueChannel: return(QuantumScale*pixel.blue);
1807 case OpacityChannel:
1808 {
1809 if (pixel.matte == MagickFalse)
1810 {
1811 fx_info->matte=MagickFalse;
1812 return(1.0);
1813 }
cristy46f08202010-01-10 04:04:21 +00001814 return((MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel)));
cristy3ed852e2009-09-05 21:47:34 +00001815 }
1816 case IndexChannel:
1817 {
1818 if (image->colorspace != CMYKColorspace)
1819 {
1820 (void) ThrowMagickException(exception,GetMagickModule(),
1821 OptionError,"ColorSeparatedImageRequired","`%s'",
1822 image->filename);
1823 return(0.0);
1824 }
1825 return(QuantumScale*pixel.index);
1826 }
1827 default:
1828 break;
1829 }
1830 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1831 "UnableToParseExpression","`%s'",p);
1832 return(0.0);
1833 }
1834 switch (*symbol)
1835 {
1836 case 'A':
1837 case 'a':
1838 {
1839 if (LocaleCompare(symbol,"a") == 0)
cristy46f08202010-01-10 04:04:21 +00001840 return((MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel)));
cristy3ed852e2009-09-05 21:47:34 +00001841 break;
1842 }
1843 case 'B':
1844 case 'b':
1845 {
1846 if (LocaleCompare(symbol,"b") == 0)
1847 return(QuantumScale*pixel.blue);
1848 break;
1849 }
1850 case 'C':
1851 case 'c':
1852 {
1853 if (LocaleNCompare(symbol,"channel",7) == 0)
1854 {
1855 GeometryInfo
1856 channel_info;
1857
1858 MagickStatusType
1859 flags;
1860
1861 flags=ParseGeometry(symbol+7,&channel_info);
1862 if (image->colorspace == CMYKColorspace)
1863 switch (channel)
1864 {
1865 case CyanChannel:
1866 {
1867 if ((flags & RhoValue) == 0)
1868 return(0.0);
1869 return(channel_info.rho);
1870 }
1871 case MagentaChannel:
1872 {
1873 if ((flags & SigmaValue) == 0)
1874 return(0.0);
1875 return(channel_info.sigma);
1876 }
1877 case YellowChannel:
1878 {
1879 if ((flags & XiValue) == 0)
1880 return(0.0);
1881 return(channel_info.xi);
1882 }
1883 case BlackChannel:
1884 {
1885 if ((flags & PsiValue) == 0)
1886 return(0.0);
1887 return(channel_info.psi);
1888 }
1889 case OpacityChannel:
1890 {
1891 if ((flags & ChiValue) == 0)
1892 return(0.0);
1893 return(channel_info.chi);
1894 }
1895 default:
1896 return(0.0);
1897 }
1898 switch (channel)
1899 {
1900 case RedChannel:
1901 {
1902 if ((flags & RhoValue) == 0)
1903 return(0.0);
1904 return(channel_info.rho);
1905 }
1906 case GreenChannel:
1907 {
1908 if ((flags & SigmaValue) == 0)
1909 return(0.0);
1910 return(channel_info.sigma);
1911 }
1912 case BlueChannel:
1913 {
1914 if ((flags & XiValue) == 0)
1915 return(0.0);
1916 return(channel_info.xi);
1917 }
1918 case OpacityChannel:
1919 {
1920 if ((flags & PsiValue) == 0)
1921 return(0.0);
1922 return(channel_info.psi);
1923 }
1924 case IndexChannel:
1925 {
1926 if ((flags & ChiValue) == 0)
1927 return(0.0);
1928 return(channel_info.chi);
1929 }
1930 default:
1931 return(0.0);
1932 }
1933 return(0.0);
1934 }
1935 if (LocaleCompare(symbol,"c") == 0)
1936 return(QuantumScale*pixel.red);
1937 break;
1938 }
1939 case 'D':
1940 case 'd':
1941 {
1942 if (LocaleNCompare(symbol,"depth",5) == 0)
1943 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1944 break;
1945 }
1946 case 'G':
1947 case 'g':
1948 {
1949 if (LocaleCompare(symbol,"g") == 0)
1950 return(QuantumScale*pixel.green);
1951 break;
1952 }
1953 case 'K':
1954 case 'k':
1955 {
1956 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1957 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1958 if (LocaleCompare(symbol,"k") == 0)
1959 {
1960 if (image->colorspace != CMYKColorspace)
1961 {
1962 (void) ThrowMagickException(exception,GetMagickModule(),
1963 OptionError,"ColorSeparatedImageRequired","`%s'",
1964 image->filename);
1965 return(0.0);
1966 }
1967 return(QuantumScale*pixel.index);
1968 }
1969 break;
1970 }
1971 case 'H':
1972 case 'h':
1973 {
1974 if (LocaleCompare(symbol,"h") == 0)
1975 return((MagickRealType) image->rows);
1976 if (LocaleCompare(symbol,"hue") == 0)
1977 {
1978 double
1979 hue,
1980 lightness,
1981 saturation;
1982
cristyce70c172010-01-07 17:15:30 +00001983 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1984 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00001985 return(hue);
1986 }
1987 break;
1988 }
1989 case 'I':
1990 case 'i':
1991 {
1992 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1993 (LocaleCompare(symbol,"image.minima") == 0) ||
1994 (LocaleCompare(symbol,"image.maxima") == 0) ||
1995 (LocaleCompare(symbol,"image.mean") == 0) ||
1996 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1997 (LocaleCompare(symbol,"image.skewness") == 0) ||
1998 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1999 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
2000 if (LocaleCompare(symbol,"image.resolution.x") == 0)
2001 return(image->x_resolution);
2002 if (LocaleCompare(symbol,"image.resolution.y") == 0)
2003 return(image->y_resolution);
2004 if (LocaleCompare(symbol,"intensity") == 0)
2005 return(QuantumScale*MagickPixelIntensityToQuantum(&pixel));
2006 if (LocaleCompare(symbol,"i") == 0)
2007 return((MagickRealType) x);
2008 break;
2009 }
2010 case 'J':
2011 case 'j':
2012 {
2013 if (LocaleCompare(symbol,"j") == 0)
2014 return((MagickRealType) y);
2015 break;
2016 }
2017 case 'L':
2018 case 'l':
2019 {
2020 if (LocaleCompare(symbol,"lightness") == 0)
2021 {
2022 double
2023 hue,
2024 lightness,
2025 saturation;
2026
cristyce70c172010-01-07 17:15:30 +00002027 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
2028 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00002029 return(lightness);
2030 }
2031 if (LocaleCompare(symbol,"luminance") == 0)
2032 {
2033 double
2034 luminence;
2035
2036 luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
2037 return(QuantumScale*luminence);
2038 }
2039 break;
2040 }
2041 case 'M':
2042 case 'm':
2043 {
2044 if (LocaleNCompare(symbol,"maxima",6) == 0)
2045 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2046 if (LocaleNCompare(symbol,"mean",4) == 0)
2047 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2048 if (LocaleNCompare(symbol,"minima",6) == 0)
2049 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2050 if (LocaleCompare(symbol,"m") == 0)
2051 return(QuantumScale*pixel.blue);
2052 break;
2053 }
2054 case 'N':
2055 case 'n':
2056 {
2057 if (LocaleCompare(symbol,"n") == 0)
2058 return((MagickRealType) GetImageListLength(fx_info->images));
2059 break;
2060 }
2061 case 'O':
2062 case 'o':
2063 {
2064 if (LocaleCompare(symbol,"o") == 0)
2065 return(QuantumScale*pixel.opacity);
2066 break;
2067 }
2068 case 'P':
2069 case 'p':
2070 {
2071 if (LocaleCompare(symbol,"page.height") == 0)
2072 return((MagickRealType) image->page.height);
2073 if (LocaleCompare(symbol,"page.width") == 0)
2074 return((MagickRealType) image->page.width);
2075 if (LocaleCompare(symbol,"page.x") == 0)
2076 return((MagickRealType) image->page.x);
2077 if (LocaleCompare(symbol,"page.y") == 0)
2078 return((MagickRealType) image->page.y);
2079 break;
2080 }
2081 case 'R':
2082 case 'r':
2083 {
2084 if (LocaleCompare(symbol,"resolution.x") == 0)
2085 return(image->x_resolution);
2086 if (LocaleCompare(symbol,"resolution.y") == 0)
2087 return(image->y_resolution);
2088 if (LocaleCompare(symbol,"r") == 0)
2089 return(QuantumScale*pixel.red);
2090 break;
2091 }
2092 case 'S':
2093 case 's':
2094 {
2095 if (LocaleCompare(symbol,"saturation") == 0)
2096 {
2097 double
2098 hue,
2099 lightness,
2100 saturation;
2101
cristyce70c172010-01-07 17:15:30 +00002102 ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
2103 ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
cristy3ed852e2009-09-05 21:47:34 +00002104 return(saturation);
2105 }
2106 if (LocaleNCompare(symbol,"skewness",8) == 0)
2107 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2108 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
2109 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
2110 break;
2111 }
2112 case 'T':
2113 case 't':
2114 {
2115 if (LocaleCompare(symbol,"t") == 0)
2116 return((MagickRealType) fx_info->images->scene);
2117 break;
2118 }
2119 case 'W':
2120 case 'w':
2121 {
2122 if (LocaleCompare(symbol,"w") == 0)
2123 return((MagickRealType) image->columns);
2124 break;
2125 }
2126 case 'Y':
2127 case 'y':
2128 {
2129 if (LocaleCompare(symbol,"y") == 0)
2130 return(QuantumScale*pixel.green);
2131 break;
2132 }
2133 case 'Z':
2134 case 'z':
2135 {
2136 if (LocaleCompare(symbol,"z") == 0)
2137 {
2138 MagickRealType
2139 depth;
2140
2141 depth=(MagickRealType) GetImageChannelDepth(image,channel,
2142 fx_info->exception);
2143 return(depth);
2144 }
2145 break;
2146 }
2147 default:
2148 break;
2149 }
2150 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
2151 if (value != (const char *) NULL)
cristyf2f27272009-12-17 14:48:46 +00002152 return((MagickRealType) StringToDouble(value));
cristy3ed852e2009-09-05 21:47:34 +00002153 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2154 "UnableToParseExpression","`%s'",symbol);
2155 return(0.0);
2156}
2157
2158static const char *FxOperatorPrecedence(const char *expression,
2159 ExceptionInfo *exception)
2160{
2161 typedef enum
2162 {
2163 UndefinedPrecedence,
2164 NullPrecedence,
2165 BitwiseComplementPrecedence,
2166 ExponentPrecedence,
2167 MultiplyPrecedence,
2168 AdditionPrecedence,
2169 ShiftPrecedence,
2170 RelationalPrecedence,
2171 EquivalencyPrecedence,
2172 BitwiseAndPrecedence,
2173 BitwiseOrPrecedence,
2174 LogicalAndPrecedence,
2175 LogicalOrPrecedence,
2176 TernaryPrecedence,
2177 AssignmentPrecedence,
2178 CommaPrecedence,
2179 SeparatorPrecedence
2180 } FxPrecedence;
2181
2182 FxPrecedence
2183 precedence,
2184 target;
2185
2186 register const char
2187 *subexpression;
2188
2189 register int
2190 c;
2191
2192 unsigned long
2193 level;
2194
2195 c=0;
2196 level=0;
2197 subexpression=(const char *) NULL;
2198 target=NullPrecedence;
2199 while (*expression != '\0')
2200 {
2201 precedence=UndefinedPrecedence;
2202 if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
2203 {
2204 expression++;
2205 continue;
2206 }
2207 if (LocaleNCompare(expression,"atan2",5) == 0)
2208 {
2209 expression+=5;
2210 continue;
2211 }
2212 if ((c == (int) '{') || (c == (int) '['))
2213 level++;
2214 else
2215 if ((c == (int) '}') || (c == (int) ']'))
2216 level--;
2217 if (level == 0)
2218 switch ((unsigned char) *expression)
2219 {
2220 case '~':
2221 case '!':
2222 {
2223 precedence=BitwiseComplementPrecedence;
2224 break;
2225 }
2226 case '^':
2227 {
2228 precedence=ExponentPrecedence;
2229 break;
2230 }
2231 default:
2232 {
2233 if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
2234 (strchr(")",c) != (char *) NULL))) &&
2235 (((islower((int) ((char) *expression)) != 0) ||
2236 (strchr("(",(int) *expression) != (char *) NULL)) ||
2237 ((isdigit((int) ((char) c)) == 0) &&
2238 (isdigit((int) ((char) *expression)) != 0))) &&
2239 (strchr("xy",(int) *expression) == (char *) NULL))
2240 precedence=MultiplyPrecedence;
2241 break;
2242 }
2243 case '*':
2244 case '/':
2245 case '%':
2246 {
2247 precedence=MultiplyPrecedence;
2248 break;
2249 }
2250 case '+':
2251 case '-':
2252 {
2253 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
2254 (isalpha(c) != 0))
2255 precedence=AdditionPrecedence;
2256 break;
2257 }
2258 case LeftShiftOperator:
2259 case RightShiftOperator:
2260 {
2261 precedence=ShiftPrecedence;
2262 break;
2263 }
2264 case '<':
2265 case LessThanEqualOperator:
2266 case GreaterThanEqualOperator:
2267 case '>':
2268 {
2269 precedence=RelationalPrecedence;
2270 break;
2271 }
2272 case EqualOperator:
2273 case NotEqualOperator:
2274 {
2275 precedence=EquivalencyPrecedence;
2276 break;
2277 }
2278 case '&':
2279 {
2280 precedence=BitwiseAndPrecedence;
2281 break;
2282 }
2283 case '|':
2284 {
2285 precedence=BitwiseOrPrecedence;
2286 break;
2287 }
2288 case LogicalAndOperator:
2289 {
2290 precedence=LogicalAndPrecedence;
2291 break;
2292 }
2293 case LogicalOrOperator:
2294 {
2295 precedence=LogicalOrPrecedence;
2296 break;
2297 }
2298 case ':':
2299 case '?':
2300 {
2301 precedence=TernaryPrecedence;
2302 break;
2303 }
2304 case '=':
2305 {
2306 precedence=AssignmentPrecedence;
2307 break;
2308 }
2309 case ',':
2310 {
2311 precedence=CommaPrecedence;
2312 break;
2313 }
2314 case ';':
2315 {
2316 precedence=SeparatorPrecedence;
2317 break;
2318 }
2319 }
2320 if ((precedence == BitwiseComplementPrecedence) ||
2321 (precedence == TernaryPrecedence) ||
2322 (precedence == AssignmentPrecedence))
2323 {
2324 if (precedence > target)
2325 {
2326 /*
2327 Right-to-left associativity.
2328 */
2329 target=precedence;
2330 subexpression=expression;
2331 }
2332 }
2333 else
2334 if (precedence >= target)
2335 {
2336 /*
2337 Left-to-right associativity.
2338 */
2339 target=precedence;
2340 subexpression=expression;
2341 }
2342 if (strchr("(",(int) *expression) != (char *) NULL)
2343 expression=FxSubexpression(expression,exception);
2344 c=(int) (*expression++);
2345 }
2346 return(subexpression);
2347}
2348
2349static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
2350 const ChannelType channel,const long x,const long y,const char *expression,
2351 MagickRealType *beta,ExceptionInfo *exception)
2352{
2353 char
2354 *q,
2355 subexpression[MaxTextExtent];
2356
2357 MagickRealType
2358 alpha,
2359 gamma;
2360
2361 register const char
2362 *p;
2363
2364 *beta=0.0;
2365 if (exception->severity != UndefinedException)
2366 return(0.0);
2367 while (isspace((int) *expression) != 0)
2368 expression++;
2369 if (*expression == '\0')
2370 {
2371 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2372 "MissingExpression","`%s'",expression);
2373 return(0.0);
2374 }
2375 p=FxOperatorPrecedence(expression,exception);
2376 if (p != (const char *) NULL)
2377 {
2378 (void) CopyMagickString(subexpression,expression,(size_t)
2379 (p-expression+1));
2380 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2381 exception);
2382 switch ((unsigned char) *p)
2383 {
2384 case '~':
2385 {
2386 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2387 *beta=(MagickRealType) (~(unsigned long) *beta);
2388 return(*beta);
2389 }
2390 case '!':
2391 {
2392 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2393 return(*beta == 0.0 ? 1.0 : 0.0);
2394 }
2395 case '^':
2396 {
2397 *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2398 channel,x,y,++p,beta,exception));
2399 return(*beta);
2400 }
2401 case '*':
2402 {
2403 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2404 return(alpha*(*beta));
2405 }
2406 case '/':
2407 {
2408 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2409 if (*beta == 0.0)
2410 {
2411 if (exception->severity == UndefinedException)
2412 (void) ThrowMagickException(exception,GetMagickModule(),
2413 OptionError,"DivideByZero","`%s'",expression);
2414 return(0.0);
2415 }
2416 return(alpha/(*beta));
2417 }
2418 case '%':
2419 {
2420 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2421 *beta=fabs(floor(((double) *beta)+0.5));
2422 if (*beta == 0.0)
2423 {
2424 (void) ThrowMagickException(exception,GetMagickModule(),
2425 OptionError,"DivideByZero","`%s'",expression);
2426 return(0.0);
2427 }
2428 return(fmod((double) alpha,(double) *beta));
2429 }
2430 case '+':
2431 {
2432 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2433 return(alpha+(*beta));
2434 }
2435 case '-':
2436 {
2437 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2438 return(alpha-(*beta));
2439 }
2440 case LeftShiftOperator:
2441 {
2442 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2443 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) << (unsigned long)
2444 (gamma+0.5));
2445 return(*beta);
2446 }
2447 case RightShiftOperator:
2448 {
2449 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2450 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) >> (unsigned long)
2451 (gamma+0.5));
2452 return(*beta);
2453 }
2454 case '<':
2455 {
2456 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2457 return(alpha < *beta ? 1.0 : 0.0);
2458 }
2459 case LessThanEqualOperator:
2460 {
2461 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2462 return(alpha <= *beta ? 1.0 : 0.0);
2463 }
2464 case '>':
2465 {
2466 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2467 return(alpha > *beta ? 1.0 : 0.0);
2468 }
2469 case GreaterThanEqualOperator:
2470 {
2471 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2472 return(alpha >= *beta ? 1.0 : 0.0);
2473 }
2474 case EqualOperator:
2475 {
2476 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2477 return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2478 }
2479 case NotEqualOperator:
2480 {
2481 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2482 return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2483 }
2484 case '&':
2485 {
2486 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2487 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) & (unsigned long)
2488 (gamma+0.5));
2489 return(*beta);
2490 }
2491 case '|':
2492 {
2493 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2494 *beta=(MagickRealType) ((unsigned long) (alpha+0.5) | (unsigned long)
2495 (gamma+0.5));
2496 return(*beta);
2497 }
2498 case LogicalAndOperator:
2499 {
2500 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2501 *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2502 return(*beta);
2503 }
2504 case LogicalOrOperator:
2505 {
2506 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2507 *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2508 return(*beta);
2509 }
2510 case '?':
2511 {
2512 MagickRealType
2513 gamma;
2514
2515 (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2516 q=subexpression;
2517 p=StringToken(":",&q);
2518 if (q == (char *) NULL)
2519 {
2520 (void) ThrowMagickException(exception,GetMagickModule(),
2521 OptionError,"UnableToParseExpression","`%s'",subexpression);
2522 return(0.0);
2523 }
2524 if (fabs((double) alpha) > MagickEpsilon)
2525 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2526 else
2527 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2528 return(gamma);
2529 }
2530 case '=':
2531 {
2532 char
2533 numeric[MaxTextExtent];
2534
2535 q=subexpression;
2536 while (isalpha((int) ((unsigned char) *q)) != 0)
2537 q++;
2538 if (*q != '\0')
2539 {
2540 (void) ThrowMagickException(exception,GetMagickModule(),
2541 OptionError,"UnableToParseExpression","`%s'",subexpression);
2542 return(0.0);
2543 }
2544 ClearMagickException(exception);
2545 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
cristy8cd5b312010-01-07 01:10:24 +00002546 (void) FormatMagickString(numeric,MaxTextExtent,"%.15g",(double)
2547 *beta);
cristy3ed852e2009-09-05 21:47:34 +00002548 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2549 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2550 subexpression),ConstantString(numeric));
2551 return(*beta);
2552 }
2553 case ',':
2554 {
2555 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2556 return(alpha);
2557 }
2558 case ';':
2559 {
2560 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2561 return(*beta);
2562 }
2563 default:
2564 {
2565 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2566 exception);
2567 return(gamma);
2568 }
2569 }
2570 }
2571 if (strchr("(",(int) *expression) != (char *) NULL)
2572 {
2573 (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2574 subexpression[strlen(subexpression)-1]='\0';
2575 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2576 exception);
2577 return(gamma);
2578 }
2579 switch (*expression)
2580 {
2581 case '+':
2582 {
2583 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2584 exception);
2585 return(1.0*gamma);
2586 }
2587 case '-':
2588 {
2589 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2590 exception);
2591 return(-1.0*gamma);
2592 }
2593 case '~':
2594 {
2595 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2596 exception);
2597 return((MagickRealType) (~(unsigned long) (gamma+0.5)));
2598 }
2599 case 'A':
2600 case 'a':
2601 {
2602 if (LocaleNCompare(expression,"abs",3) == 0)
2603 {
2604 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2605 exception);
2606 return((MagickRealType) fabs((double) alpha));
2607 }
2608 if (LocaleNCompare(expression,"acos",4) == 0)
2609 {
2610 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2611 exception);
2612 return((MagickRealType) acos((double) alpha));
2613 }
2614 if (LocaleNCompare(expression,"asin",4) == 0)
2615 {
2616 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2617 exception);
2618 return((MagickRealType) asin((double) alpha));
2619 }
2620 if (LocaleNCompare(expression,"alt",3) == 0)
2621 {
2622 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2623 exception);
2624 return(((long) alpha) & 0x01 ? -1.0 : 1.0);
2625 }
2626 if (LocaleNCompare(expression,"atan2",5) == 0)
2627 {
2628 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2629 exception);
2630 return((MagickRealType) atan2((double) alpha,(double) *beta));
2631 }
2632 if (LocaleNCompare(expression,"atan",4) == 0)
2633 {
2634 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2635 exception);
2636 return((MagickRealType) atan((double) alpha));
2637 }
2638 if (LocaleCompare(expression,"a") == 0)
2639 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2640 break;
2641 }
2642 case 'B':
2643 case 'b':
2644 {
2645 if (LocaleCompare(expression,"b") == 0)
2646 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2647 break;
2648 }
2649 case 'C':
2650 case 'c':
2651 {
2652 if (LocaleNCompare(expression,"ceil",4) == 0)
2653 {
2654 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2655 exception);
2656 return((MagickRealType) ceil((double) alpha));
2657 }
2658 if (LocaleNCompare(expression,"cosh",4) == 0)
2659 {
2660 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2661 exception);
2662 return((MagickRealType) cosh((double) alpha));
2663 }
2664 if (LocaleNCompare(expression,"cos",3) == 0)
2665 {
2666 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2667 exception);
2668 return((MagickRealType) cos((double) alpha));
2669 }
2670 if (LocaleCompare(expression,"c") == 0)
2671 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2672 break;
2673 }
2674 case 'D':
2675 case 'd':
2676 {
2677 if (LocaleNCompare(expression,"debug",5) == 0)
2678 {
2679 const char
2680 *type;
2681
2682 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2683 exception);
2684 if (fx_info->images->colorspace == CMYKColorspace)
2685 switch (channel)
2686 {
2687 case CyanChannel: type="cyan"; break;
2688 case MagentaChannel: type="magenta"; break;
2689 case YellowChannel: type="yellow"; break;
2690 case OpacityChannel: type="opacity"; break;
2691 case BlackChannel: type="black"; break;
2692 default: type="unknown"; break;
2693 }
2694 else
2695 switch (channel)
2696 {
2697 case RedChannel: type="red"; break;
2698 case GreenChannel: type="green"; break;
2699 case BlueChannel: type="blue"; break;
2700 case OpacityChannel: type="opacity"; break;
2701 default: type="unknown"; break;
2702 }
2703 (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2704 if (strlen(subexpression) > 1)
2705 subexpression[strlen(subexpression)-1]='\0';
2706 if (fx_info->file != (FILE *) NULL)
cristy8cd5b312010-01-07 01:10:24 +00002707 (void) fprintf(fx_info->file,"%s[%ld,%ld].%s: %s=%.15g\n",
cristy3ed852e2009-09-05 21:47:34 +00002708 fx_info->images->filename,x,y,type,subexpression,(double) alpha);
2709 return(0.0);
2710 }
2711 break;
2712 }
2713 case 'E':
2714 case 'e':
2715 {
2716 if (LocaleCompare(expression,"epsilon") == 0)
2717 return((MagickRealType) MagickEpsilon);
2718 if (LocaleNCompare(expression,"exp",3) == 0)
2719 {
2720 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2721 exception);
2722 return((MagickRealType) exp((double) alpha));
2723 }
2724 if (LocaleCompare(expression,"e") == 0)
2725 return((MagickRealType) 2.7182818284590452354);
2726 break;
2727 }
2728 case 'F':
2729 case 'f':
2730 {
2731 if (LocaleNCompare(expression,"floor",5) == 0)
2732 {
2733 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2734 exception);
2735 return((MagickRealType) floor((double) alpha));
2736 }
2737 break;
2738 }
2739 case 'G':
2740 case 'g':
2741 {
2742 if (LocaleCompare(expression,"g") == 0)
2743 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2744 break;
2745 }
2746 case 'H':
2747 case 'h':
2748 {
2749 if (LocaleCompare(expression,"h") == 0)
2750 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2751 if (LocaleCompare(expression,"hue") == 0)
2752 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2753 if (LocaleNCompare(expression,"hypot",5) == 0)
2754 {
2755 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2756 exception);
2757 return((MagickRealType) hypot((double) alpha,(double) *beta));
2758 }
2759 break;
2760 }
2761 case 'K':
2762 case 'k':
2763 {
2764 if (LocaleCompare(expression,"k") == 0)
2765 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2766 break;
2767 }
2768 case 'I':
2769 case 'i':
2770 {
2771 if (LocaleCompare(expression,"intensity") == 0)
2772 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2773 if (LocaleNCompare(expression,"int",3) == 0)
2774 {
2775 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2776 exception);
2777 return((MagickRealType) floor(alpha+0.5));
2778 }
2779 if (LocaleCompare(expression,"i") == 0)
2780 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2781 break;
2782 }
2783 case 'J':
2784 case 'j':
2785 {
2786 if (LocaleCompare(expression,"j") == 0)
2787 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2788 break;
2789 }
2790 case 'L':
2791 case 'l':
2792 {
2793 if (LocaleNCompare(expression,"ln",2) == 0)
2794 {
2795 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2796 exception);
2797 return((MagickRealType) log((double) alpha));
2798 }
2799 if (LocaleNCompare(expression,"logtwo",4) == 0)
2800 {
2801 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2802 exception);
2803 return((MagickRealType) log10((double) alpha))/log10(2.0);
2804 }
2805 if (LocaleNCompare(expression,"log",3) == 0)
2806 {
2807 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2808 exception);
2809 return((MagickRealType) log10((double) alpha));
2810 }
2811 if (LocaleCompare(expression,"lightness") == 0)
2812 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2813 break;
2814 }
2815 case 'M':
2816 case 'm':
2817 {
2818 if (LocaleCompare(expression,"MaxRGB") == 0)
2819 return((MagickRealType) QuantumRange);
2820 if (LocaleNCompare(expression,"maxima",6) == 0)
2821 break;
2822 if (LocaleNCompare(expression,"max",3) == 0)
2823 return(FxMax(fx_info,channel,x,y,expression+3,exception));
2824 if (LocaleNCompare(expression,"minima",6) == 0)
2825 break;
2826 if (LocaleNCompare(expression,"min",3) == 0)
2827 return(FxMin(fx_info,channel,x,y,expression+3,exception));
2828 if (LocaleNCompare(expression,"mod",3) == 0)
2829 {
2830 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2831 exception);
2832 return((MagickRealType) fmod((double) alpha,(double) *beta));
2833 }
2834 if (LocaleCompare(expression,"m") == 0)
2835 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2836 break;
2837 }
2838 case 'N':
2839 case 'n':
2840 {
2841 if (LocaleCompare(expression,"n") == 0)
2842 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2843 break;
2844 }
2845 case 'O':
2846 case 'o':
2847 {
2848 if (LocaleCompare(expression,"Opaque") == 0)
2849 return(1.0);
2850 if (LocaleCompare(expression,"o") == 0)
2851 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2852 break;
2853 }
2854 case 'P':
2855 case 'p':
2856 {
2857 if (LocaleCompare(expression,"pi") == 0)
2858 return((MagickRealType) MagickPI);
2859 if (LocaleNCompare(expression,"pow",3) == 0)
2860 {
2861 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2862 exception);
2863 return((MagickRealType) pow((double) alpha,(double) *beta));
2864 }
2865 if (LocaleCompare(expression,"p") == 0)
2866 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2867 break;
2868 }
2869 case 'Q':
2870 case 'q':
2871 {
2872 if (LocaleCompare(expression,"QuantumRange") == 0)
2873 return((MagickRealType) QuantumRange);
2874 if (LocaleCompare(expression,"QuantumScale") == 0)
2875 return((MagickRealType) QuantumScale);
2876 break;
2877 }
2878 case 'R':
2879 case 'r':
2880 {
2881 if (LocaleNCompare(expression,"rand",4) == 0)
2882 return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2883 if (LocaleNCompare(expression,"round",5) == 0)
2884 {
2885 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2886 exception);
2887 if (alpha >= 0.0)
2888 return((MagickRealType) floor((double) alpha+0.5));
2889 return((MagickRealType) ceil((double) alpha-0.5));
2890 }
2891 if (LocaleCompare(expression,"r") == 0)
2892 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2893 break;
2894 }
2895 case 'S':
2896 case 's':
2897 {
2898 if (LocaleCompare(expression,"saturation") == 0)
2899 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2900 if (LocaleNCompare(expression,"sign",4) == 0)
2901 {
2902 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2903 exception);
2904 return(alpha < 0.0 ? -1.0 : 1.0);
2905 }
2906 if (LocaleNCompare(expression,"sinh",4) == 0)
2907 {
2908 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2909 exception);
2910 return((MagickRealType) sinh((double) alpha));
2911 }
2912 if (LocaleNCompare(expression,"sin",3) == 0)
2913 {
2914 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2915 exception);
2916 return((MagickRealType) sin((double) alpha));
2917 }
2918 if (LocaleNCompare(expression,"sqrt",4) == 0)
2919 {
2920 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2921 exception);
2922 return((MagickRealType) sqrt((double) alpha));
2923 }
2924 if (LocaleCompare(expression,"s") == 0)
2925 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2926 break;
2927 }
2928 case 'T':
2929 case 't':
2930 {
2931 if (LocaleNCompare(expression,"tanh",4) == 0)
2932 {
2933 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2934 exception);
2935 return((MagickRealType) tanh((double) alpha));
2936 }
2937 if (LocaleNCompare(expression,"tan",3) == 0)
2938 {
2939 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2940 exception);
2941 return((MagickRealType) tan((double) alpha));
2942 }
2943 if (LocaleCompare(expression,"Transparent") == 0)
2944 return(0.0);
2945 if (LocaleCompare(expression,"t") == 0)
2946 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2947 break;
2948 }
2949 case 'U':
2950 case 'u':
2951 {
2952 if (LocaleCompare(expression,"u") == 0)
2953 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2954 break;
2955 }
2956 case 'V':
2957 case 'v':
2958 {
2959 if (LocaleCompare(expression,"v") == 0)
2960 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2961 break;
2962 }
2963 case 'W':
2964 case 'w':
2965 {
2966 if (LocaleCompare(expression,"w") == 0)
2967 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2968 break;
2969 }
2970 case 'Y':
2971 case 'y':
2972 {
2973 if (LocaleCompare(expression,"y") == 0)
2974 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2975 break;
2976 }
2977 case 'Z':
2978 case 'z':
2979 {
2980 if (LocaleCompare(expression,"z") == 0)
2981 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2982 break;
2983 }
2984 default:
2985 break;
2986 }
2987 q=(char *) expression;
2988 alpha=strtod(expression,&q);
2989 if (q == expression)
2990 return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2991 return(alpha);
2992}
2993
2994MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2995 MagickRealType *alpha,ExceptionInfo *exception)
2996{
2997 MagickBooleanType
2998 status;
2999
3000 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
3001 return(status);
3002}
3003
3004MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
3005 MagickRealType *alpha,ExceptionInfo *exception)
3006{
3007 FILE
3008 *file;
3009
3010 MagickBooleanType
3011 status;
3012
3013 file=fx_info->file;
3014 fx_info->file=(FILE *) NULL;
3015 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
3016 fx_info->file=file;
3017 return(status);
3018}
3019
3020MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
3021 const ChannelType channel,const long x,const long y,MagickRealType *alpha,
3022 ExceptionInfo *exception)
3023{
3024 MagickRealType
3025 beta;
3026
3027 beta=0.0;
3028 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
3029 exception);
3030 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
3031}
3032
3033/*
3034%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3035% %
3036% %
3037% %
3038% F x I m a g e %
3039% %
3040% %
3041% %
3042%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3043%
3044% FxImage() applies a mathematical expression to the specified image.
3045%
3046% The format of the FxImage method is:
3047%
3048% Image *FxImage(const Image *image,const char *expression,
3049% ExceptionInfo *exception)
3050% Image *FxImageChannel(const Image *image,const ChannelType channel,
3051% const char *expression,ExceptionInfo *exception)
3052%
3053% A description of each parameter follows:
3054%
3055% o image: the image.
3056%
3057% o channel: the channel.
3058%
3059% o expression: A mathematical expression.
3060%
3061% o exception: return any errors or warnings in this structure.
3062%
3063*/
3064
3065static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
3066{
3067 register long
3068 i;
3069
3070 assert(fx_info != (FxInfo **) NULL);
3071 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
3072 if (fx_info[i] != (FxInfo *) NULL)
3073 fx_info[i]=DestroyFxInfo(fx_info[i]);
3074 fx_info=(FxInfo **) RelinquishAlignedMemory(fx_info);
3075 return(fx_info);
3076}
3077
3078static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
3079 ExceptionInfo *exception)
3080{
3081 char
3082 *fx_expression;
3083
3084 FxInfo
3085 **fx_info;
3086
3087 MagickRealType
3088 alpha;
3089
3090 register long
3091 i;
3092
3093 unsigned long
3094 number_threads;
3095
3096 number_threads=GetOpenMPMaximumThreads();
3097 fx_info=(FxInfo **) AcquireAlignedMemory(number_threads,sizeof(*fx_info));
3098 if (fx_info == (FxInfo **) NULL)
3099 return((FxInfo **) NULL);
3100 (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
3101 if (*expression != '@')
3102 fx_expression=ConstantString(expression);
3103 else
3104 fx_expression=FileToString(expression+1,~0,exception);
3105 for (i=0; i < (long) number_threads; i++)
3106 {
3107 fx_info[i]=AcquireFxInfo(image,fx_expression);
3108 if (fx_info[i] == (FxInfo *) NULL)
3109 return(DestroyFxThreadSet(fx_info));
3110 (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
3111 }
3112 fx_expression=DestroyString(fx_expression);
3113 return(fx_info);
3114}
3115
3116MagickExport Image *FxImage(const Image *image,const char *expression,
3117 ExceptionInfo *exception)
3118{
3119 Image
3120 *fx_image;
3121
3122 fx_image=FxImageChannel(image,GrayChannel,expression,exception);
3123 return(fx_image);
3124}
3125
3126MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
3127 const char *expression,ExceptionInfo *exception)
3128{
3129#define FxImageTag "Fx/Image"
3130
cristyfa112112010-01-04 17:48:07 +00003131 CacheView
3132 *fx_view;
3133
cristy3ed852e2009-09-05 21:47:34 +00003134 FxInfo
cristyfa112112010-01-04 17:48:07 +00003135 **restrict fx_info;
cristy3ed852e2009-09-05 21:47:34 +00003136
3137 Image
3138 *fx_image;
3139
3140 long
3141 progress,
3142 y;
3143
3144 MagickBooleanType
3145 status;
3146
3147 MagickRealType
3148 alpha;
3149
cristy3ed852e2009-09-05 21:47:34 +00003150 assert(image != (Image *) NULL);
3151 assert(image->signature == MagickSignature);
3152 if (image->debug != MagickFalse)
3153 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3154 fx_image=CloneImage(image,0,0,MagickTrue,exception);
3155 if (fx_image == (Image *) NULL)
3156 return((Image *) NULL);
3157 if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
3158 {
3159 InheritException(exception,&fx_image->exception);
3160 fx_image=DestroyImage(fx_image);
3161 return((Image *) NULL);
3162 }
3163 fx_info=AcquireFxThreadSet(image,expression,exception);
3164 if (fx_info == (FxInfo **) NULL)
3165 {
3166 fx_image=DestroyImage(fx_image);
3167 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3168 }
3169 status=FxPreprocessExpression(fx_info[0],&alpha,exception);
3170 if (status == MagickFalse)
3171 {
3172 fx_image=DestroyImage(fx_image);
3173 fx_info=DestroyFxThreadSet(fx_info);
3174 return((Image *) NULL);
3175 }
3176 /*
3177 Fx image.
3178 */
3179 status=MagickTrue;
3180 progress=0;
3181 fx_view=AcquireCacheView(fx_image);
cristyb5d5f722009-11-04 03:03:49 +00003182#if defined(MAGICKCORE_OPENMP_SUPPORT)
3183 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003184#endif
3185 for (y=0; y < (long) fx_image->rows; y++)
3186 {
3187 MagickRealType
3188 alpha;
3189
3190 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003191 *restrict fx_indexes;
cristy3ed852e2009-09-05 21:47:34 +00003192
3193 register long
3194 id,
3195 x;
3196
3197 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003198 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003199
3200 if (status == MagickFalse)
3201 continue;
3202 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
3203 if (q == (PixelPacket *) NULL)
3204 {
3205 status=MagickFalse;
3206 continue;
3207 }
3208 fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
3209 id=GetOpenMPThreadId();
3210 alpha=0.0;
3211 for (x=0; x < (long) fx_image->columns; x++)
3212 {
3213 if ((channel & RedChannel) != 0)
3214 {
3215 (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
3216 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00003217 q->red=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00003218 }
3219 if ((channel & GreenChannel) != 0)
3220 {
3221 (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
3222 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00003223 q->green=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00003224 }
3225 if ((channel & BlueChannel) != 0)
3226 {
3227 (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
3228 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00003229 q->blue=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00003230 }
3231 if ((channel & OpacityChannel) != 0)
3232 {
3233 (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
3234 &alpha,exception);
3235 if (image->matte == MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003236 q->opacity=ClampToQuantum((MagickRealType) QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +00003237 else
cristyce70c172010-01-07 17:15:30 +00003238 q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
cristy3ed852e2009-09-05 21:47:34 +00003239 QuantumRange*alpha));
3240 }
3241 if (((channel & IndexChannel) != 0) &&
3242 (fx_image->colorspace == CMYKColorspace))
3243 {
3244 (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
3245 &alpha,exception);
cristyce70c172010-01-07 17:15:30 +00003246 fx_indexes[x]=(IndexPacket) ClampToQuantum((MagickRealType)
cristy3ed852e2009-09-05 21:47:34 +00003247 QuantumRange*alpha);
3248 }
3249 q++;
3250 }
3251 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3252 status=MagickFalse;
3253 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3254 {
3255 MagickBooleanType
3256 proceed;
3257
cristyb5d5f722009-11-04 03:03:49 +00003258#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003259 #pragma omp critical (MagickCore_FxImageChannel)
3260#endif
3261 proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3262 if (proceed == MagickFalse)
3263 status=MagickFalse;
3264 }
3265 }
3266 fx_image->matte=fx_info[0]->matte;
3267 fx_view=DestroyCacheView(fx_view);
3268 fx_info=DestroyFxThreadSet(fx_info);
3269 if (status == MagickFalse)
3270 fx_image=DestroyImage(fx_image);
3271 return(fx_image);
3272}
3273
3274/*
3275%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3276% %
3277% %
3278% %
3279% I m p l o d e I m a g e %
3280% %
3281% %
3282% %
3283%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3284%
3285% ImplodeImage() creates a new image that is a copy of an existing
3286% one with the image pixels "implode" by the specified percentage. It
3287% allocates the memory necessary for the new Image structure and returns a
3288% pointer to the new image.
3289%
3290% The format of the ImplodeImage method is:
3291%
3292% Image *ImplodeImage(const Image *image,const double amount,
3293% ExceptionInfo *exception)
3294%
3295% A description of each parameter follows:
3296%
3297% o implode_image: Method ImplodeImage returns a pointer to the image
3298% after it is implode. A null image is returned if there is a memory
3299% shortage.
3300%
3301% o image: the image.
3302%
3303% o amount: Define the extent of the implosion.
3304%
3305% o exception: return any errors or warnings in this structure.
3306%
3307*/
3308MagickExport Image *ImplodeImage(const Image *image,const double amount,
3309 ExceptionInfo *exception)
3310{
3311#define ImplodeImageTag "Implode/Image"
3312
cristyfa112112010-01-04 17:48:07 +00003313 CacheView
3314 *image_view,
3315 *implode_view;
3316
cristy3ed852e2009-09-05 21:47:34 +00003317 Image
3318 *implode_image;
3319
3320 long
3321 progress,
3322 y;
3323
3324 MagickBooleanType
3325 status;
3326
3327 MagickPixelPacket
3328 zero;
3329
3330 MagickRealType
3331 radius;
3332
3333 PointInfo
3334 center,
3335 scale;
3336
3337 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00003338 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00003339
3340 /*
3341 Initialize implode image attributes.
3342 */
3343 assert(image != (Image *) NULL);
3344 assert(image->signature == MagickSignature);
3345 if (image->debug != MagickFalse)
3346 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3347 assert(exception != (ExceptionInfo *) NULL);
3348 assert(exception->signature == MagickSignature);
3349 implode_image=CloneImage(image,0,0,MagickTrue,exception);
3350 if (implode_image == (Image *) NULL)
3351 return((Image *) NULL);
3352 if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
3353 {
3354 InheritException(exception,&implode_image->exception);
3355 implode_image=DestroyImage(implode_image);
3356 return((Image *) NULL);
3357 }
3358 if (implode_image->background_color.opacity != OpaqueOpacity)
3359 implode_image->matte=MagickTrue;
3360 /*
3361 Compute scaling factor.
3362 */
3363 scale.x=1.0;
3364 scale.y=1.0;
3365 center.x=0.5*image->columns;
3366 center.y=0.5*image->rows;
3367 radius=center.x;
3368 if (image->columns > image->rows)
3369 scale.y=(double) image->columns/(double) image->rows;
3370 else
3371 if (image->columns < image->rows)
3372 {
3373 scale.x=(double) image->rows/(double) image->columns;
3374 radius=center.y;
3375 }
3376 /*
3377 Implode image.
3378 */
3379 status=MagickTrue;
3380 progress=0;
3381 GetMagickPixelPacket(implode_image,&zero);
3382 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
3383 image_view=AcquireCacheView(image);
3384 implode_view=AcquireCacheView(implode_image);
cristyb5d5f722009-11-04 03:03:49 +00003385#if defined(MAGICKCORE_OPENMP_SUPPORT)
3386 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003387#endif
3388 for (y=0; y < (long) image->rows; y++)
3389 {
3390 MagickPixelPacket
3391 pixel;
3392
3393 MagickRealType
3394 distance;
3395
3396 PointInfo
3397 delta;
3398
3399 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003400 *restrict implode_indexes;
cristy3ed852e2009-09-05 21:47:34 +00003401
3402 register long
3403 id,
3404 x;
3405
3406 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003407 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003408
3409 if (status == MagickFalse)
3410 continue;
3411 q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3412 exception);
3413 if (q == (PixelPacket *) NULL)
3414 {
3415 status=MagickFalse;
3416 continue;
3417 }
3418 implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
3419 delta.y=scale.y*(double) (y-center.y);
3420 pixel=zero;
3421 id=GetOpenMPThreadId();
3422 for (x=0; x < (long) image->columns; x++)
3423 {
3424 /*
3425 Determine if the pixel is within an ellipse.
3426 */
3427 delta.x=scale.x*(double) (x-center.x);
3428 distance=delta.x*delta.x+delta.y*delta.y;
3429 if (distance < (radius*radius))
3430 {
3431 double
3432 factor;
3433
3434 /*
3435 Implode the pixel.
3436 */
3437 factor=1.0;
3438 if (distance > 0.0)
3439 factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3440 radius/2)),-amount);
3441 (void) ResamplePixelColor(resample_filter[id],(double)
3442 (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3443 scale.y+center.y),&pixel);
3444 SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
3445 }
3446 q++;
3447 }
3448 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3449 status=MagickFalse;
3450 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3451 {
3452 MagickBooleanType
3453 proceed;
3454
cristyb5d5f722009-11-04 03:03:49 +00003455#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003456 #pragma omp critical (MagickCore_ImplodeImage)
3457#endif
3458 proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3459 if (proceed == MagickFalse)
3460 status=MagickFalse;
3461 }
3462 }
3463 implode_view=DestroyCacheView(implode_view);
3464 image_view=DestroyCacheView(image_view);
3465 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
3466 if (status == MagickFalse)
3467 implode_image=DestroyImage(implode_image);
3468 return(implode_image);
3469}
3470
3471/*
3472%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3473% %
3474% %
3475% %
3476% M o r p h I m a g e s %
3477% %
3478% %
3479% %
3480%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3481%
3482% The MorphImages() method requires a minimum of two images. The first
3483% image is transformed into the second by a number of intervening images
3484% as specified by frames.
3485%
3486% The format of the MorphImage method is:
3487%
3488% Image *MorphImages(const Image *image,const unsigned long number_frames,
3489% ExceptionInfo *exception)
3490%
3491% A description of each parameter follows:
3492%
3493% o image: the image.
3494%
3495% o number_frames: Define the number of in-between image to generate.
3496% The more in-between frames, the smoother the morph.
3497%
3498% o exception: return any errors or warnings in this structure.
3499%
3500*/
3501MagickExport Image *MorphImages(const Image *image,
3502 const unsigned long number_frames,ExceptionInfo *exception)
3503{
3504#define MorphImageTag "Morph/Image"
3505
3506 Image
3507 *morph_image,
3508 *morph_images;
3509
3510 long
3511 y;
3512
3513 MagickOffsetType
3514 scene;
3515
3516 MagickRealType
3517 alpha,
3518 beta;
3519
3520 register const Image
3521 *next;
3522
3523 register long
3524 i;
3525
3526 MagickBooleanType
3527 status;
3528
3529 /*
3530 Clone first frame in sequence.
3531 */
3532 assert(image != (Image *) NULL);
3533 assert(image->signature == MagickSignature);
3534 if (image->debug != MagickFalse)
3535 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3536 assert(exception != (ExceptionInfo *) NULL);
3537 assert(exception->signature == MagickSignature);
3538 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3539 if (morph_images == (Image *) NULL)
3540 return((Image *) NULL);
3541 if (GetNextImageInList(image) == (Image *) NULL)
3542 {
3543 /*
3544 Morph single image.
3545 */
3546 for (i=1; i < (long) number_frames; i++)
3547 {
3548 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3549 if (morph_image == (Image *) NULL)
3550 {
3551 morph_images=DestroyImageList(morph_images);
3552 return((Image *) NULL);
3553 }
3554 AppendImageToList(&morph_images,morph_image);
3555 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
3556 (QuantumTick(i,number_frames) != MagickFalse))
3557 {
3558 status=image->progress_monitor(MorphImageTag,i,number_frames,
3559 image->client_data);
3560 if (status == MagickFalse)
3561 break;
3562 }
3563 }
3564 return(GetFirstImageInList(morph_images));
3565 }
3566 /*
3567 Morph image sequence.
3568 */
3569 status=MagickTrue;
3570 scene=0;
3571 next=image;
3572 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3573 {
3574 for (i=0; i < (long) number_frames; i++)
3575 {
3576 CacheView
3577 *image_view,
3578 *morph_view;
3579
3580 beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3581 alpha=1.0-beta;
3582 morph_image=ZoomImage(next,(unsigned long) (alpha*next->columns+beta*
3583 GetNextImageInList(next)->columns+0.5),(unsigned long) (alpha*
3584 next->rows+beta*GetNextImageInList(next)->rows+0.5),exception);
3585 if (morph_image == (Image *) NULL)
3586 {
3587 morph_images=DestroyImageList(morph_images);
3588 return((Image *) NULL);
3589 }
3590 if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
3591 {
3592 InheritException(exception,&morph_image->exception);
3593 morph_image=DestroyImage(morph_image);
3594 return((Image *) NULL);
3595 }
3596 AppendImageToList(&morph_images,morph_image);
3597 morph_images=GetLastImageInList(morph_images);
3598 morph_image=ZoomImage(GetNextImageInList(next),morph_images->columns,
3599 morph_images->rows,exception);
3600 if (morph_image == (Image *) NULL)
3601 {
3602 morph_images=DestroyImageList(morph_images);
3603 return((Image *) NULL);
3604 }
3605 image_view=AcquireCacheView(morph_image);
3606 morph_view=AcquireCacheView(morph_images);
cristyb5d5f722009-11-04 03:03:49 +00003607#if defined(MAGICKCORE_OPENMP_SUPPORT)
3608 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003609#endif
3610 for (y=0; y < (long) morph_images->rows; y++)
3611 {
3612 MagickBooleanType
3613 sync;
3614
3615 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003616 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003617
3618 register long
3619 x;
3620
3621 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003622 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003623
3624 if (status == MagickFalse)
3625 continue;
3626 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3627 exception);
3628 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3629 exception);
3630 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3631 {
3632 status=MagickFalse;
3633 continue;
3634 }
3635 for (x=0; x < (long) morph_images->columns; x++)
3636 {
cristyce70c172010-01-07 17:15:30 +00003637 q->red=ClampToQuantum(alpha*q->red+beta*GetRedPixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00003638 q->green=ClampToQuantum(alpha*q->green+beta*
3639 GetGreenPixelComponent(p));
cristyce70c172010-01-07 17:15:30 +00003640 q->blue=ClampToQuantum(alpha*q->blue+beta*GetBluePixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00003641 q->opacity=ClampToQuantum(alpha*q->opacity+beta*
3642 GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00003643 p++;
3644 q++;
3645 }
3646 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3647 if (sync == MagickFalse)
3648 status=MagickFalse;
3649 }
3650 morph_view=DestroyCacheView(morph_view);
3651 image_view=DestroyCacheView(image_view);
3652 morph_image=DestroyImage(morph_image);
3653 }
3654 if (i < (long) number_frames)
3655 break;
3656 /*
3657 Clone last frame in sequence.
3658 */
3659 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3660 if (morph_image == (Image *) NULL)
3661 {
3662 morph_images=DestroyImageList(morph_images);
3663 return((Image *) NULL);
3664 }
3665 AppendImageToList(&morph_images,morph_image);
3666 morph_images=GetLastImageInList(morph_images);
3667 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3668 {
3669 MagickBooleanType
3670 proceed;
3671
cristyb5d5f722009-11-04 03:03:49 +00003672#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003673 #pragma omp critical (MagickCore_MorphImages)
3674#endif
3675 proceed=SetImageProgress(image,MorphImageTag,scene,
3676 GetImageListLength(image));
3677 if (proceed == MagickFalse)
3678 status=MagickFalse;
3679 }
3680 scene++;
3681 }
3682 if (GetNextImageInList(next) != (Image *) NULL)
3683 {
3684 morph_images=DestroyImageList(morph_images);
3685 return((Image *) NULL);
3686 }
3687 return(GetFirstImageInList(morph_images));
3688}
3689
3690/*
3691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3692% %
3693% %
3694% %
3695% P l a s m a I m a g e %
3696% %
3697% %
3698% %
3699%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3700%
3701% PlasmaImage() initializes an image with plasma fractal values. The image
3702% must be initialized with a base color and the random number generator
3703% seeded before this method is called.
3704%
3705% The format of the PlasmaImage method is:
3706%
3707% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
3708% unsigned long attenuate,unsigned long depth)
3709%
3710% A description of each parameter follows:
3711%
3712% o image: the image.
3713%
3714% o segment: Define the region to apply plasma fractals values.
3715%
3716% o attenuate: Define the plasmattenuation factor.
3717%
3718% o depth: Limit the plasma recursion depth.
3719%
3720*/
3721
3722static inline Quantum PlasmaPixel(RandomInfo *random_info,
3723 const MagickRealType pixel,const MagickRealType noise)
3724{
3725 Quantum
3726 plasma;
3727
cristyce70c172010-01-07 17:15:30 +00003728 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
cristy3ed852e2009-09-05 21:47:34 +00003729 noise/2.0);
3730 return(plasma);
3731}
3732
3733MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
3734 RandomInfo *random_info,const SegmentInfo *segment,unsigned long attenuate,
3735 unsigned long depth)
3736{
3737 ExceptionInfo
3738 *exception;
3739
3740 long
3741 x,
3742 x_mid,
3743 y,
3744 y_mid;
3745
3746 MagickRealType
3747 plasma;
3748
3749 PixelPacket
3750 u,
3751 v;
3752
3753 if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3754 return(MagickTrue);
3755 if (depth != 0)
3756 {
3757 SegmentInfo
3758 local_info;
3759
3760 /*
3761 Divide the area into quadrants and recurse.
3762 */
3763 depth--;
3764 attenuate++;
3765 x_mid=(long) (segment->x1+segment->x2+0.5)/2;
3766 y_mid=(long) (segment->y1+segment->y2+0.5)/2;
3767 local_info=(*segment);
3768 local_info.x2=(double) x_mid;
3769 local_info.y2=(double) y_mid;
3770 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3771 local_info=(*segment);
3772 local_info.y1=(double) y_mid;
3773 local_info.x2=(double) x_mid;
3774 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3775 local_info=(*segment);
3776 local_info.x1=(double) x_mid;
3777 local_info.y2=(double) y_mid;
3778 (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3779 local_info=(*segment);
3780 local_info.x1=(double) x_mid;
3781 local_info.y1=(double) y_mid;
3782 return(PlasmaImageProxy(image,random_info,&local_info,attenuate,depth));
3783 }
3784 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
3785 return(MagickFalse);
3786 x_mid=(long) (segment->x1+segment->x2+0.5)/2;
3787 y_mid=(long) (segment->y1+segment->y2+0.5)/2;
3788 if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3789 (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3790 return(MagickFalse);
3791 /*
3792 Average pixels and apply plasma.
3793 */
3794 exception=(&image->exception);
3795 plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3796 if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3797 {
3798 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003799 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003800
3801 /*
3802 Left pixel.
3803 */
3804 x=(long) (segment->x1+0.5);
3805 (void) GetOneVirtualPixel(image,x,(long) (segment->y1+0.5),&u,exception);
3806 (void) GetOneVirtualPixel(image,x,(long) (segment->y2+0.5),&v,exception);
3807 q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
3808 if (q == (PixelPacket *) NULL)
3809 return(MagickTrue);
3810 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3811 plasma);
3812 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3813 plasma);
3814 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3815 plasma);
3816 (void) SyncAuthenticPixels(image,exception);
3817 if (segment->x1 != segment->x2)
3818 {
3819 /*
3820 Right pixel.
3821 */
3822 x=(long) (segment->x2+0.5);
3823 (void) GetOneVirtualPixel(image,x,(long) (segment->y1+0.5),&u,
3824 exception);
3825 (void) GetOneVirtualPixel(image,x,(long) (segment->y2+0.5),&v,
3826 exception);
3827 q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
3828 if (q == (PixelPacket *) NULL)
3829 return(MagickTrue);
3830 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3831 plasma);
3832 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3833 2.0,plasma);
3834 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3835 plasma);
3836 (void) SyncAuthenticPixels(image,exception);
3837 }
3838 }
3839 if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3840 {
3841 if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3842 {
3843 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003844 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003845
3846 /*
3847 Bottom pixel.
3848 */
3849 y=(long) (segment->y2+0.5);
3850 (void) GetOneVirtualPixel(image,(long) (segment->x1+0.5),y,&u,
3851 exception);
3852 (void) GetOneVirtualPixel(image,(long) (segment->x2+0.5),y,&v,
3853 exception);
3854 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3855 if (q == (PixelPacket *) NULL)
3856 return(MagickTrue);
3857 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3858 plasma);
3859 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3860 2.0,plasma);
3861 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3862 plasma);
3863 (void) SyncAuthenticPixels(image,exception);
3864 }
3865 if (segment->y1 != segment->y2)
3866 {
3867 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003868 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003869
3870 /*
3871 Top pixel.
3872 */
3873 y=(long) (segment->y1+0.5);
3874 (void) GetOneVirtualPixel(image,(long) (segment->x1+0.5),y,&u,
3875 exception);
3876 (void) GetOneVirtualPixel(image,(long) (segment->x2+0.5),y,&v,
3877 exception);
3878 q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3879 if (q == (PixelPacket *) NULL)
3880 return(MagickTrue);
3881 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3882 plasma);
3883 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3884 2.0,plasma);
3885 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3886 plasma);
3887 (void) SyncAuthenticPixels(image,exception);
3888 }
3889 }
3890 if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3891 {
3892 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003893 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003894
3895 /*
3896 Middle pixel.
3897 */
3898 x=(long) (segment->x1+0.5);
3899 y=(long) (segment->y1+0.5);
3900 (void) GetOneVirtualPixel(image,x,y,&u,exception);
3901 x=(long) (segment->x2+0.5);
3902 y=(long) (segment->y2+0.5);
3903 (void) GetOneVirtualPixel(image,x,y,&v,exception);
3904 q=QueueAuthenticPixels(image,x_mid,y_mid,1,1,exception);
3905 if (q == (PixelPacket *) NULL)
3906 return(MagickTrue);
3907 q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3908 plasma);
3909 q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3910 plasma);
3911 q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3912 plasma);
3913 (void) SyncAuthenticPixels(image,exception);
3914 }
3915 if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3916 return(MagickTrue);
3917 return(MagickFalse);
3918}
3919
3920MagickExport MagickBooleanType PlasmaImage(Image *image,
3921 const SegmentInfo *segment,unsigned long attenuate,unsigned long depth)
3922{
3923 MagickBooleanType
3924 status;
3925
3926 RandomInfo
3927 *random_info;
3928
3929 if (image->debug != MagickFalse)
3930 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3931 assert(image != (Image *) NULL);
3932 assert(image->signature == MagickSignature);
3933 if (image->debug != MagickFalse)
3934 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3935 random_info=AcquireRandomInfo();
3936 status=PlasmaImageProxy(image,random_info,segment,attenuate,depth);
3937 random_info=DestroyRandomInfo(random_info);
3938 return(status);
3939}
3940
3941/*
3942%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3943% %
3944% %
3945% %
3946% P o l a r o i d I m a g e %
3947% %
3948% %
3949% %
3950%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3951%
3952% PolaroidImage() simulates a Polaroid picture.
3953%
3954% The format of the AnnotateImage method is:
3955%
3956% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3957% const double angle,ExceptionInfo exception)
3958%
3959% A description of each parameter follows:
3960%
3961% o image: the image.
3962%
3963% o draw_info: the draw info.
3964%
3965% o angle: Apply the effect along this angle.
3966%
3967% o exception: return any errors or warnings in this structure.
3968%
3969*/
3970MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3971 const double angle,ExceptionInfo *exception)
3972{
3973 const char
3974 *value;
3975
3976 long
3977 quantum;
3978
3979 Image
3980 *bend_image,
3981 *caption_image,
3982 *flop_image,
3983 *picture_image,
3984 *polaroid_image,
3985 *rotate_image,
3986 *trim_image;
3987
3988 unsigned long
3989 height;
3990
3991 /*
3992 Simulate a Polaroid picture.
3993 */
3994 assert(image != (Image *) NULL);
3995 assert(image->signature == MagickSignature);
3996 if (image->debug != MagickFalse)
3997 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3998 assert(exception != (ExceptionInfo *) NULL);
3999 assert(exception->signature == MagickSignature);
4000 quantum=(long) MagickMax(MagickMax((double) image->columns,(double)
4001 image->rows)/25.0,10.0);
4002 height=image->rows+2*quantum;
4003 caption_image=(Image *) NULL;
4004 value=GetImageProperty(image,"Caption");
4005 if (value != (const char *) NULL)
4006 {
4007 char
4008 *caption,
4009 geometry[MaxTextExtent];
4010
4011 DrawInfo
4012 *annotate_info;
4013
4014 long
4015 count;
4016
4017 MagickBooleanType
4018 status;
4019
4020 TypeMetric
4021 metrics;
4022
4023 /*
4024 Generate caption image.
4025 */
4026 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
4027 if (caption_image == (Image *) NULL)
4028 return((Image *) NULL);
4029 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
4030 caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
4031 value);
4032 (void) CloneString(&annotate_info->text,caption);
4033 count=FormatMagickCaption(caption_image,annotate_info,&metrics,&caption);
4034 status=SetImageExtent(caption_image,image->columns,(unsigned long)
4035 ((count+1)*(metrics.ascent-metrics.descent)+0.5));
4036 if (status == MagickFalse)
4037 caption_image=DestroyImage(caption_image);
4038 else
4039 {
4040 caption_image->background_color=image->border_color;
4041 (void) SetImageBackgroundColor(caption_image);
4042 (void) CloneString(&annotate_info->text,caption);
cristy8cd5b312010-01-07 01:10:24 +00004043 (void) FormatMagickString(geometry,MaxTextExtent,"+0+%.15g",
cristy3ed852e2009-09-05 21:47:34 +00004044 metrics.ascent);
4045 if (annotate_info->gravity == UndefinedGravity)
4046 (void) CloneString(&annotate_info->geometry,AcquireString(
4047 geometry));
4048 (void) AnnotateImage(caption_image,annotate_info);
4049 height+=caption_image->rows;
4050 }
4051 annotate_info=DestroyDrawInfo(annotate_info);
4052 caption=DestroyString(caption);
4053 }
4054 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4055 exception);
4056 if (picture_image == (Image *) NULL)
4057 {
4058 if (caption_image != (Image *) NULL)
4059 caption_image=DestroyImage(caption_image);
4060 return((Image *) NULL);
4061 }
4062 picture_image->background_color=image->border_color;
4063 (void) SetImageBackgroundColor(picture_image);
4064 (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
4065 if (caption_image != (Image *) NULL)
4066 {
4067 (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
4068 quantum,(long) (image->rows+3*quantum/2));
4069 caption_image=DestroyImage(caption_image);
4070 }
4071 (void) QueryColorDatabase("none",&picture_image->background_color,exception);
4072 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
4073 rotate_image=RotateImage(picture_image,90.0,exception);
4074 picture_image=DestroyImage(picture_image);
4075 if (rotate_image == (Image *) NULL)
4076 return((Image *) NULL);
4077 picture_image=rotate_image;
4078 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
4079 picture_image->columns,exception);
4080 picture_image=DestroyImage(picture_image);
4081 if (bend_image == (Image *) NULL)
4082 return((Image *) NULL);
4083 InheritException(&bend_image->exception,exception);
4084 picture_image=bend_image;
4085 rotate_image=RotateImage(picture_image,-90.0,exception);
4086 picture_image=DestroyImage(picture_image);
4087 if (rotate_image == (Image *) NULL)
4088 return((Image *) NULL);
4089 picture_image=rotate_image;
4090 picture_image->background_color=image->background_color;
4091 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
4092 exception);
4093 if (polaroid_image == (Image *) NULL)
4094 {
4095 picture_image=DestroyImage(picture_image);
4096 return(picture_image);
4097 }
4098 flop_image=FlopImage(polaroid_image,exception);
4099 polaroid_image=DestroyImage(polaroid_image);
4100 if (flop_image == (Image *) NULL)
4101 {
4102 picture_image=DestroyImage(picture_image);
4103 return(picture_image);
4104 }
4105 polaroid_image=flop_image;
4106 (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
4107 (long) (-0.01*picture_image->columns/2.0),0L);
4108 picture_image=DestroyImage(picture_image);
4109 (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
4110 rotate_image=RotateImage(polaroid_image,angle,exception);
4111 polaroid_image=DestroyImage(polaroid_image);
4112 if (rotate_image == (Image *) NULL)
4113 return((Image *) NULL);
4114 polaroid_image=rotate_image;
4115 trim_image=TrimImage(polaroid_image,exception);
4116 polaroid_image=DestroyImage(polaroid_image);
4117 if (trim_image == (Image *) NULL)
4118 return((Image *) NULL);
4119 polaroid_image=trim_image;
4120 return(polaroid_image);
4121}
4122
4123/*
4124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4125% %
4126% %
4127% %
4128% R e c o l o r I m a g e %
4129% %
4130% %
4131% %
4132%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4133%
4134% RecolorImage() translate, scale, shear, or rotate image colors. Although
4135% you can use variable sized matrices, typically you use a 5 x 5 for an RGBA
4136% image and a 6x6 for CMYKA. Populate the last row with normalized values to
4137% translate.
4138%
4139% The format of the RecolorImage method is:
4140%
4141% Image *RecolorImage(const Image *image,const unsigned long order,
4142% const double *color_matrix,ExceptionInfo *exception)
4143%
4144% A description of each parameter follows:
4145%
4146% o image: the image.
4147%
4148% o order: the number of columns and rows in the recolor matrix.
4149%
4150% o color_matrix: An array of double representing the recolor matrix.
4151%
4152% o exception: return any errors or warnings in this structure.
4153%
4154*/
4155MagickExport Image *RecolorImage(const Image *image,const unsigned long order,
4156 const double *color_matrix,ExceptionInfo *exception)
4157{
4158#define RecolorImageTag "Recolor/Image"
4159
cristy42500932009-11-07 18:44:47 +00004160 CacheView
4161 *image_view,
4162 *recolor_view;
4163
cristy3ed852e2009-09-05 21:47:34 +00004164 Image
4165 *recolor_image;
4166
4167 long
4168 progress,
4169 y;
4170
4171 MagickBooleanType
4172 status;
4173
cristy3ed852e2009-09-05 21:47:34 +00004174 /*
4175 Initialize image attributes.
4176 */
4177 assert(image != (Image *) NULL);
4178 assert(image->signature == MagickSignature);
4179 if (image->debug != MagickFalse)
4180 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4181 assert(exception != (ExceptionInfo *) NULL);
4182 assert(exception->signature == MagickSignature);
cristy42500932009-11-07 18:44:47 +00004183 recolor_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00004184 if (recolor_image == (Image *) NULL)
4185 return((Image *) NULL);
4186 if (SetImageStorageClass(recolor_image,DirectClass) == MagickFalse)
4187 {
4188 InheritException(exception,&recolor_image->exception);
4189 recolor_image=DestroyImage(recolor_image);
4190 return((Image *) NULL);
4191 }
4192 if (image->debug != MagickFalse)
4193 {
4194 char
4195 format[MaxTextExtent],
4196 *message;
4197
4198 long
4199 u,
4200 v;
4201
cristy1c01ffd2009-11-07 18:32:17 +00004202 register const double
4203 *k;
4204
cristy3ed852e2009-09-05 21:47:34 +00004205 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4206 " Recolor image with %ldx%ld color matrix:",order,order);
4207 message=AcquireString("");
4208 k=color_matrix;
4209 for (v=0; v < (long) order; v++)
4210 {
4211 *message='\0';
4212 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
4213 (void) ConcatenateString(&message,format);
4214 for (u=0; u < (long) order; u++)
4215 {
4216 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4217 (void) ConcatenateString(&message,format);
4218 }
4219 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4220 }
4221 message=DestroyString(message);
4222 }
4223 /*
4224 Recolor image.
4225 */
4226 status=MagickTrue;
4227 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00004228 image_view=AcquireCacheView(image);
4229 recolor_view=AcquireCacheView(recolor_image);
cristyb5d5f722009-11-04 03:03:49 +00004230#if defined(MAGICKCORE_OPENMP_SUPPORT)
4231 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004232#endif
4233 for (y=0; y < (long) image->rows; y++)
4234 {
cristy1c01ffd2009-11-07 18:32:17 +00004235 register const double
cristyc47d1f82009-11-26 01:44:43 +00004236 *restrict k;
cristy1c01ffd2009-11-07 18:32:17 +00004237
cristy3ed852e2009-09-05 21:47:34 +00004238 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004239 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00004240
4241 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004242 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004243
4244 register long
4245 x;
4246
4247 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004248 *restrict recolor_indexes;
cristy3ed852e2009-09-05 21:47:34 +00004249
4250 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004251 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004252
4253 if (status == MagickFalse)
4254 continue;
4255 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy42500932009-11-07 18:44:47 +00004256 q=GetCacheViewAuthenticPixels(recolor_view,0,y,recolor_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00004257 exception);
4258 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4259 {
4260 status=MagickFalse;
4261 continue;
4262 }
4263 indexes=GetCacheViewAuthenticIndexQueue(image_view);
4264 recolor_indexes=GetCacheViewAuthenticIndexQueue(recolor_view);
cristy1c01ffd2009-11-07 18:32:17 +00004265 k=color_matrix;
cristyaec08b42009-11-07 20:09:08 +00004266 switch (order)
cristy3ed852e2009-09-05 21:47:34 +00004267 {
cristyaec08b42009-11-07 20:09:08 +00004268 case 0:
4269 break;
4270 case 1:
cristy3ed852e2009-09-05 21:47:34 +00004271 {
cristyaec08b42009-11-07 20:09:08 +00004272 for (x=0; x < (long) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004273 {
cristyce70c172010-01-07 17:15:30 +00004274 q->red=ClampToQuantum(k[0]*GetRedPixelComponent(p));
cristyaec08b42009-11-07 20:09:08 +00004275 p++;
4276 q++;
cristy3ed852e2009-09-05 21:47:34 +00004277 }
cristyaec08b42009-11-07 20:09:08 +00004278 break;
cristy3ed852e2009-09-05 21:47:34 +00004279 }
cristyaec08b42009-11-07 20:09:08 +00004280 case 2:
4281 {
4282 for (x=0; x < (long) image->columns; x++)
4283 {
cristyce70c172010-01-07 17:15:30 +00004284 q->red=ClampToQuantum(k[0]*p->red+k[1]*GetGreenPixelComponent(p));
4285 q->green=ClampToQuantum(k[2]*p->red+k[3]*GetGreenPixelComponent(p));
cristyaec08b42009-11-07 20:09:08 +00004286 p++;
4287 q++;
4288 }
4289 break;
4290 }
4291 case 3:
4292 {
4293 for (x=0; x < (long) image->columns; x++)
4294 {
cristyce70c172010-01-07 17:15:30 +00004295 q->red=ClampToQuantum(k[0]*p->red+k[1]*p->green+k[2]*GetBluePixelComponent(p));
4296 q->green=ClampToQuantum(k[3]*p->red+k[4]*p->green+k[5]*GetBluePixelComponent(p));
4297 q->blue=ClampToQuantum(k[6]*p->red+k[7]*p->green+k[8]*GetBluePixelComponent(p));
cristyaec08b42009-11-07 20:09:08 +00004298 p++;
4299 q++;
4300 }
4301 break;
4302 }
4303 case 4:
4304 {
4305 for (x=0; x < (long) image->columns; x++)
4306 {
cristyce70c172010-01-07 17:15:30 +00004307 q->red=ClampToQuantum(k[0]*p->red+k[1]*p->green+k[2]*p->blue+
cristyaec08b42009-11-07 20:09:08 +00004308 k[12]*QuantumRange);
cristyce70c172010-01-07 17:15:30 +00004309 q->green=ClampToQuantum(k[4]*p->red+k[5]*p->green+k[6]*p->blue+
cristyaec08b42009-11-07 20:09:08 +00004310 k[13]*QuantumRange);
cristyce70c172010-01-07 17:15:30 +00004311 q->blue=ClampToQuantum(k[8]*p->red+k[9]*p->green+k[10]*p->blue+
cristyaec08b42009-11-07 20:09:08 +00004312 k[14]*QuantumRange);
4313 p++;
4314 q++;
4315 }
4316 break;
4317 }
4318 case 5:
4319 {
4320 for (x=0; x < (long) image->columns; x++)
4321 {
cristyce70c172010-01-07 17:15:30 +00004322 q->red=ClampToQuantum(k[0]*p->red+k[1]*p->green+k[2]*p->blue+
cristy46f08202010-01-10 04:04:21 +00004323 k[3]*(GetAlphaPixelComponent(p))+k[20]*QuantumRange);
cristyce70c172010-01-07 17:15:30 +00004324 q->green=ClampToQuantum(k[5]*p->red+k[6]*p->green+k[7]*p->blue+
cristy46f08202010-01-10 04:04:21 +00004325 k[8]*(GetAlphaPixelComponent(p))+k[21]*QuantumRange);
cristyce70c172010-01-07 17:15:30 +00004326 q->blue=ClampToQuantum(k[10]*p->red+k[11]*p->green+k[12]*p->blue+
cristy46f08202010-01-10 04:04:21 +00004327 k[13]*(GetAlphaPixelComponent(p))+k[22]*QuantumRange);
cristyaec08b42009-11-07 20:09:08 +00004328 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00004329 q->opacity=ClampToQuantum((MagickRealType) QuantumRange-(k[15]*
cristy46f08202010-01-10 04:04:21 +00004330 p->red+k[16]*p->green+k[17]*p->blue+k[18]*
4331 GetAlphaPixelComponent(p)+k[23]*QuantumRange));
cristyaec08b42009-11-07 20:09:08 +00004332 p++;
4333 q++;
4334 }
4335 break;
4336 }
4337 default:
4338 {
4339 for (x=0; x < (long) image->columns; x++)
4340 {
cristyce70c172010-01-07 17:15:30 +00004341 q->red=ClampToQuantum(k[0]*p->red+k[1]*p->green+k[2]*p->blue+
cristy46f08202010-01-10 04:04:21 +00004342 k[3]*indexes[x]+k[4]*((Quantum) GetAlphaPixelComponent(p))+
cristyaec08b42009-11-07 20:09:08 +00004343 k[30]*QuantumRange);
cristyce70c172010-01-07 17:15:30 +00004344 q->green=ClampToQuantum(k[6]*p->red+k[7]*p->green+k[8]*p->blue+
cristy46f08202010-01-10 04:04:21 +00004345 k[9]*indexes[x]+k[10]*((Quantum) GetAlphaPixelComponent(p))+
cristyaec08b42009-11-07 20:09:08 +00004346 k[31]*QuantumRange);
cristyce70c172010-01-07 17:15:30 +00004347 q->blue=ClampToQuantum(k[12]*p->red+k[13]*p->green+k[14]*p->blue+
cristy46f08202010-01-10 04:04:21 +00004348 k[15]*indexes[x]+k[16]*((Quantum) GetAlphaPixelComponent(p))+
cristyaec08b42009-11-07 20:09:08 +00004349 k[32]*QuantumRange);
4350 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00004351 q->opacity=ClampToQuantum((MagickRealType) QuantumRange-(k[24]*
cristyaec08b42009-11-07 20:09:08 +00004352 p->red+k[25]*p->green+k[26]*p->blue+k[27]*indexes[x]+
cristy46f08202010-01-10 04:04:21 +00004353 k[28]*(GetAlphaPixelComponent(p))+k[34]*QuantumRange));
cristyaec08b42009-11-07 20:09:08 +00004354 if (image->colorspace == CMYKColorspace)
cristyce70c172010-01-07 17:15:30 +00004355 recolor_indexes[x]=ClampToQuantum(k[18]*p->red+k[19]*p->green+k[20]*
cristyaec08b42009-11-07 20:09:08 +00004356 p->blue+k[21]*indexes[x]+k[22]*((Quantum) QuantumRange-
cristyce70c172010-01-07 17:15:30 +00004357 GetOpacityPixelComponent(p))+k[33]*QuantumRange);
cristyaec08b42009-11-07 20:09:08 +00004358 p++;
4359 q++;
4360 }
4361 break;
4362 }
cristy3ed852e2009-09-05 21:47:34 +00004363 }
4364 if (SyncCacheViewAuthenticPixels(recolor_view,exception) == MagickFalse)
4365 status=MagickFalse;
4366 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4367 {
4368 MagickBooleanType
4369 proceed;
4370
cristyb5d5f722009-11-04 03:03:49 +00004371#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004372 #pragma omp critical (MagickCore_RecolorImage)
4373#endif
4374 proceed=SetImageProgress(image,RecolorImageTag,progress++,image->rows);
4375 if (proceed == MagickFalse)
4376 status=MagickFalse;
4377 }
4378 }
4379 recolor_view=DestroyCacheView(recolor_view);
4380 image_view=DestroyCacheView(image_view);
4381 if (status == MagickFalse)
4382 recolor_image=DestroyImage(recolor_image);
4383 return(recolor_image);
4384}
4385
4386/*
4387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4388% %
4389% %
4390% %
4391% S e p i a T o n e I m a g e %
4392% %
4393% %
4394% %
4395%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4396%
4397% MagickSepiaToneImage() applies a special effect to the image, similar to the
4398% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4399% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4400% threshold of 80% is a good starting point for a reasonable tone.
4401%
4402% The format of the SepiaToneImage method is:
4403%
4404% Image *SepiaToneImage(const Image *image,const double threshold,
4405% ExceptionInfo *exception)
4406%
4407% A description of each parameter follows:
4408%
4409% o image: the image.
4410%
4411% o threshold: the tone threshold.
4412%
4413% o exception: return any errors or warnings in this structure.
4414%
4415*/
4416MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4417 ExceptionInfo *exception)
4418{
4419#define SepiaToneImageTag "SepiaTone/Image"
4420
cristyc4c8d132010-01-07 01:58:38 +00004421 CacheView
4422 *image_view,
4423 *sepia_view;
4424
cristy3ed852e2009-09-05 21:47:34 +00004425 Image
4426 *sepia_image;
4427
4428 long
4429 progress,
4430 y;
4431
4432 MagickBooleanType
4433 status;
4434
cristy3ed852e2009-09-05 21:47:34 +00004435 /*
4436 Initialize sepia-toned image attributes.
4437 */
4438 assert(image != (const Image *) NULL);
4439 assert(image->signature == MagickSignature);
4440 if (image->debug != MagickFalse)
4441 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4442 assert(exception != (ExceptionInfo *) NULL);
4443 assert(exception->signature == MagickSignature);
4444 sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4445 if (sepia_image == (Image *) NULL)
4446 return((Image *) NULL);
4447 if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
4448 {
4449 InheritException(exception,&sepia_image->exception);
4450 sepia_image=DestroyImage(sepia_image);
4451 return((Image *) NULL);
4452 }
4453 /*
4454 Tone each row of the image.
4455 */
4456 status=MagickTrue;
4457 progress=0;
4458 image_view=AcquireCacheView(image);
4459 sepia_view=AcquireCacheView(sepia_image);
cristyb5d5f722009-11-04 03:03:49 +00004460#if defined(MAGICKCORE_OPENMP_SUPPORT)
4461 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004462#endif
4463 for (y=0; y < (long) image->rows; y++)
4464 {
4465 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004466 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004467
4468 register long
4469 x;
4470
4471 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004472 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004473
4474 if (status == MagickFalse)
4475 continue;
4476 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4477 q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4478 exception);
4479 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4480 {
4481 status=MagickFalse;
4482 continue;
4483 }
4484 for (x=0; x < (long) image->columns; x++)
4485 {
4486 MagickRealType
4487 intensity,
4488 tone;
4489
4490 intensity=(MagickRealType) PixelIntensityToQuantum(p);
4491 tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4492 (MagickRealType) QuantumRange-threshold;
cristyce70c172010-01-07 17:15:30 +00004493 q->red=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004494 tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4495 intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
cristyce70c172010-01-07 17:15:30 +00004496 q->green=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004497 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
cristyce70c172010-01-07 17:15:30 +00004498 q->blue=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004499 tone=threshold/7.0;
4500 if ((MagickRealType) q->green < tone)
cristyce70c172010-01-07 17:15:30 +00004501 q->green=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004502 if ((MagickRealType) q->blue < tone)
cristyce70c172010-01-07 17:15:30 +00004503 q->blue=ClampToQuantum(tone);
cristy3ed852e2009-09-05 21:47:34 +00004504 p++;
4505 q++;
4506 }
4507 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4508 status=MagickFalse;
4509 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4510 {
4511 MagickBooleanType
4512 proceed;
4513
cristyb5d5f722009-11-04 03:03:49 +00004514#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004515 #pragma omp critical (MagickCore_SepiaToneImage)
4516#endif
4517 proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4518 image->rows);
4519 if (proceed == MagickFalse)
4520 status=MagickFalse;
4521 }
4522 }
4523 sepia_view=DestroyCacheView(sepia_view);
4524 image_view=DestroyCacheView(image_view);
4525 (void) NormalizeImage(sepia_image);
4526 (void) ContrastImage(sepia_image,MagickTrue);
4527 if (status == MagickFalse)
4528 sepia_image=DestroyImage(sepia_image);
4529 return(sepia_image);
4530}
4531
4532/*
4533%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4534% %
4535% %
4536% %
4537% S h a d o w I m a g e %
4538% %
4539% %
4540% %
4541%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4542%
4543% ShadowImage() simulates a shadow from the specified image and returns it.
4544%
4545% The format of the ShadowImage method is:
4546%
4547% Image *ShadowImage(const Image *image,const double opacity,
4548% const double sigma,const long x_offset,const long y_offset,
4549% ExceptionInfo *exception)
4550%
4551% A description of each parameter follows:
4552%
4553% o image: the image.
4554%
4555% o opacity: percentage transparency.
4556%
4557% o sigma: the standard deviation of the Gaussian, in pixels.
4558%
4559% o x_offset: the shadow x-offset.
4560%
4561% o y_offset: the shadow y-offset.
4562%
4563% o exception: return any errors or warnings in this structure.
4564%
4565*/
4566MagickExport Image *ShadowImage(const Image *image,const double opacity,
4567 const double sigma,const long x_offset,const long y_offset,
4568 ExceptionInfo *exception)
4569{
4570#define ShadowImageTag "Shadow/Image"
4571
cristyc4c8d132010-01-07 01:58:38 +00004572 CacheView
4573 *image_view;
4574
cristy3ed852e2009-09-05 21:47:34 +00004575 Image
4576 *border_image,
4577 *clone_image,
4578 *shadow_image;
4579
4580 long
4581 progress,
4582 y;
4583
4584 MagickBooleanType
4585 status;
4586
4587 RectangleInfo
4588 border_info;
4589
cristy3ed852e2009-09-05 21:47:34 +00004590 assert(image != (Image *) NULL);
4591 assert(image->signature == MagickSignature);
4592 if (image->debug != MagickFalse)
4593 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4594 assert(exception != (ExceptionInfo *) NULL);
4595 assert(exception->signature == MagickSignature);
4596 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4597 if (clone_image == (Image *) NULL)
4598 return((Image *) NULL);
4599 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4600 clone_image->compose=OverCompositeOp;
4601 border_info.width=(unsigned long) (2.0*sigma+0.5);
4602 border_info.height=(unsigned long) (2.0*sigma+0.5);
4603 border_info.x=0;
4604 border_info.y=0;
4605 (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4606 border_image=BorderImage(clone_image,&border_info,exception);
4607 clone_image=DestroyImage(clone_image);
4608 if (border_image == (Image *) NULL)
4609 return((Image *) NULL);
4610 if (border_image->matte == MagickFalse)
4611 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4612 /*
4613 Shadow image.
4614 */
4615 status=MagickTrue;
4616 progress=0;
4617 image_view=AcquireCacheView(border_image);
cristyb5d5f722009-11-04 03:03:49 +00004618#if defined(MAGICKCORE_OPENMP_SUPPORT)
4619 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004620#endif
4621 for (y=0; y < (long) border_image->rows; y++)
4622 {
4623 register long
4624 x;
4625
4626 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004627 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004628
4629 if (status == MagickFalse)
4630 continue;
4631 q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4632 exception);
4633 if (q == (PixelPacket *) NULL)
4634 {
4635 status=MagickFalse;
4636 continue;
4637 }
4638 for (x=0; x < (long) border_image->columns; x++)
4639 {
4640 q->red=border_image->background_color.red;
4641 q->green=border_image->background_color.green;
4642 q->blue=border_image->background_color.blue;
4643 if (border_image->matte == MagickFalse)
4644 q->opacity=border_image->background_color.opacity;
4645 else
cristy46f08202010-01-10 04:04:21 +00004646 q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
4647 GetAlphaPixelComponent(q)*opacity/100.0));
cristy3ed852e2009-09-05 21:47:34 +00004648 q++;
4649 }
4650 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4651 status=MagickFalse;
4652 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4653 {
4654 MagickBooleanType
4655 proceed;
4656
cristyb5d5f722009-11-04 03:03:49 +00004657#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004658 #pragma omp critical (MagickCore_ShadowImage)
4659#endif
4660 proceed=SetImageProgress(image,ShadowImageTag,progress++,
4661 border_image->rows);
4662 if (proceed == MagickFalse)
4663 status=MagickFalse;
4664 }
4665 }
4666 image_view=DestroyCacheView(image_view);
4667 shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
4668 border_image=DestroyImage(border_image);
4669 if (shadow_image == (Image *) NULL)
4670 return((Image *) NULL);
4671 if (shadow_image->page.width == 0)
4672 shadow_image->page.width=shadow_image->columns;
4673 if (shadow_image->page.height == 0)
4674 shadow_image->page.height=shadow_image->rows;
4675 shadow_image->page.width+=x_offset-(long) border_info.width;
4676 shadow_image->page.height+=y_offset-(long) border_info.height;
4677 shadow_image->page.x+=x_offset-(long) border_info.width;
4678 shadow_image->page.y+=y_offset-(long) border_info.height;
4679 return(shadow_image);
4680}
4681
4682/*
4683%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4684% %
4685% %
4686% %
4687% S k e t c h I m a g e %
4688% %
4689% %
4690% %
4691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4692%
4693% SketchImage() simulates a pencil sketch. We convolve the image with a
4694% Gaussian operator of the given radius and standard deviation (sigma). For
4695% reasonable results, radius should be larger than sigma. Use a radius of 0
4696% and SketchImage() selects a suitable radius for you. Angle gives the angle
4697% of the sketch.
4698%
4699% The format of the SketchImage method is:
4700%
4701% Image *SketchImage(const Image *image,const double radius,
4702% const double sigma,const double angle,ExceptionInfo *exception)
4703%
4704% A description of each parameter follows:
4705%
4706% o image: the image.
4707%
4708% o radius: the radius of the Gaussian, in pixels, not counting
4709% the center pixel.
4710%
4711% o sigma: the standard deviation of the Gaussian, in pixels.
4712%
4713% o angle: Apply the effect along this angle.
4714%
4715% o exception: return any errors or warnings in this structure.
4716%
4717*/
4718MagickExport Image *SketchImage(const Image *image,const double radius,
4719 const double sigma,const double angle,ExceptionInfo *exception)
4720{
cristyfa112112010-01-04 17:48:07 +00004721 CacheView
4722 *random_view;
4723
cristy3ed852e2009-09-05 21:47:34 +00004724 Image
4725 *blend_image,
4726 *blur_image,
4727 *dodge_image,
4728 *random_image,
4729 *sketch_image;
4730
4731 long
4732 y;
4733
4734 MagickBooleanType
4735 status;
4736
4737 MagickPixelPacket
4738 zero;
4739
4740 RandomInfo
cristyfa112112010-01-04 17:48:07 +00004741 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00004742
4743 /*
4744 Sketch image.
4745 */
4746 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4747 MagickTrue,exception);
4748 if (random_image == (Image *) NULL)
4749 return((Image *) NULL);
4750 status=MagickTrue;
4751 GetMagickPixelPacket(random_image,&zero);
cristy1b784432009-12-19 02:20:40 +00004752 random_info=AcquireRandomInfoThreadSet();
cristy3ed852e2009-09-05 21:47:34 +00004753 random_view=AcquireCacheView(random_image);
cristy1b784432009-12-19 02:20:40 +00004754#if defined(MAGICKCORE_OPENMP_SUPPORT)
4755 #pragma omp parallel for schedule(dynamic,4) shared(status)
4756#endif
cristy3ed852e2009-09-05 21:47:34 +00004757 for (y=0; y < (long) random_image->rows; y++)
4758 {
4759 MagickPixelPacket
4760 pixel;
4761
4762 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00004763 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00004764
4765 register long
cristy1b784432009-12-19 02:20:40 +00004766 id,
cristy3ed852e2009-09-05 21:47:34 +00004767 x;
4768
4769 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004770 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004771
cristy1b784432009-12-19 02:20:40 +00004772 if (status == MagickFalse)
4773 continue;
cristy3ed852e2009-09-05 21:47:34 +00004774 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4775 exception);
4776 if (q == (PixelPacket *) NULL)
4777 {
4778 status=MagickFalse;
4779 continue;
4780 }
4781 indexes=GetCacheViewAuthenticIndexQueue(random_view);
4782 pixel=zero;
cristy1b784432009-12-19 02:20:40 +00004783 id=GetOpenMPThreadId();
cristy3ed852e2009-09-05 21:47:34 +00004784 for (x=0; x < (long) random_image->columns; x++)
4785 {
4786 pixel.red=(MagickRealType) (QuantumRange*
cristy1b784432009-12-19 02:20:40 +00004787 GetPseudoRandomValue(random_info[id]));
cristy3ed852e2009-09-05 21:47:34 +00004788 pixel.green=pixel.red;
4789 pixel.blue=pixel.red;
4790 if (image->colorspace == CMYKColorspace)
4791 pixel.index=pixel.red;
4792 SetPixelPacket(random_image,&pixel,q,indexes+x);
4793 q++;
4794 }
4795 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4796 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004797 }
4798 random_view=DestroyCacheView(random_view);
cristy1b784432009-12-19 02:20:40 +00004799 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00004800 if (status == MagickFalse)
4801 {
4802 random_image=DestroyImage(random_image);
4803 return(random_image);
4804 }
4805 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4806 random_image=DestroyImage(random_image);
4807 if (blur_image == (Image *) NULL)
4808 return((Image *) NULL);
4809 dodge_image=EdgeImage(blur_image,radius,exception);
4810 blur_image=DestroyImage(blur_image);
4811 if (dodge_image == (Image *) NULL)
4812 return((Image *) NULL);
4813 (void) NormalizeImage(dodge_image);
4814 (void) NegateImage(dodge_image,MagickFalse);
4815 (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4816 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4817 if (sketch_image == (Image *) NULL)
4818 {
4819 dodge_image=DestroyImage(dodge_image);
4820 return((Image *) NULL);
4821 }
4822 (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4823 dodge_image=DestroyImage(dodge_image);
4824 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4825 if (blend_image == (Image *) NULL)
4826 {
4827 sketch_image=DestroyImage(sketch_image);
4828 return((Image *) NULL);
4829 }
4830 (void) SetImageArtifact(blend_image,"compose:args","20x80");
4831 (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4832 blend_image=DestroyImage(blend_image);
4833 return(sketch_image);
4834}
4835
4836/*
4837%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4838% %
4839% %
4840% %
4841% S o l a r i z e I m a g e %
4842% %
4843% %
4844% %
4845%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4846%
4847% SolarizeImage() applies a special effect to the image, similar to the effect
4848% achieved in a photo darkroom by selectively exposing areas of photo
4849% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4850% measure of the extent of the solarization.
4851%
4852% The format of the SolarizeImage method is:
4853%
4854% MagickBooleanType SolarizeImage(Image *image,const double threshold)
4855%
4856% A description of each parameter follows:
4857%
4858% o image: the image.
4859%
4860% o threshold: Define the extent of the solarization.
4861%
4862*/
4863MagickExport MagickBooleanType SolarizeImage(Image *image,
4864 const double threshold)
4865{
4866#define SolarizeImageTag "Solarize/Image"
4867
cristyc4c8d132010-01-07 01:58:38 +00004868 CacheView
4869 *image_view;
4870
cristy3ed852e2009-09-05 21:47:34 +00004871 ExceptionInfo
4872 *exception;
4873
4874 long
4875 progress,
4876 y;
4877
4878 MagickBooleanType
4879 status;
4880
cristy3ed852e2009-09-05 21:47:34 +00004881 assert(image != (Image *) NULL);
4882 assert(image->signature == MagickSignature);
4883 if (image->debug != MagickFalse)
4884 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4885 if (image->storage_class == PseudoClass)
4886 {
4887 register long
4888 i;
4889
4890 /*
4891 Solarize colormap.
4892 */
4893 for (i=0; i < (long) image->colors; i++)
4894 {
4895 if ((MagickRealType) image->colormap[i].red > threshold)
4896 image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4897 if ((MagickRealType) image->colormap[i].green > threshold)
4898 image->colormap[i].green=(Quantum) QuantumRange-
4899 image->colormap[i].green;
4900 if ((MagickRealType) image->colormap[i].blue > threshold)
4901 image->colormap[i].blue=(Quantum) QuantumRange-
4902 image->colormap[i].blue;
4903 }
4904 }
4905 /*
4906 Solarize image.
4907 */
4908 status=MagickTrue;
4909 progress=0;
4910 exception=(&image->exception);
4911 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00004912#if defined(MAGICKCORE_OPENMP_SUPPORT)
4913 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004914#endif
4915 for (y=0; y < (long) image->rows; y++)
4916 {
4917 register long
4918 x;
4919
4920 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00004921 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004922
4923 if (status == MagickFalse)
4924 continue;
4925 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4926 exception);
4927 if (q == (PixelPacket *) NULL)
4928 {
4929 status=MagickFalse;
4930 continue;
4931 }
4932 for (x=0; x < (long) image->columns; x++)
4933 {
4934 if ((MagickRealType) q->red > threshold)
4935 q->red=(Quantum) QuantumRange-q->red;
4936 if ((MagickRealType) q->green > threshold)
4937 q->green=(Quantum) QuantumRange-q->green;
4938 if ((MagickRealType) q->blue > threshold)
4939 q->blue=(Quantum) QuantumRange-q->blue;
4940 q++;
4941 }
4942 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4943 status=MagickFalse;
4944 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4945 {
4946 MagickBooleanType
4947 proceed;
4948
cristyb5d5f722009-11-04 03:03:49 +00004949#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00004950 #pragma omp critical (MagickCore_SolarizeImage)
4951#endif
4952 proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4953 if (proceed == MagickFalse)
4954 status=MagickFalse;
4955 }
4956 }
4957 image_view=DestroyCacheView(image_view);
4958 return(status);
4959}
4960
4961/*
4962%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4963% %
4964% %
4965% %
4966% S t e g a n o I m a g e %
4967% %
4968% %
4969% %
4970%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4971%
4972% SteganoImage() hides a digital watermark within the image. Recover
4973% the hidden watermark later to prove that the authenticity of an image.
4974% Offset defines the start position within the image to hide the watermark.
4975%
4976% The format of the SteganoImage method is:
4977%
4978% Image *SteganoImage(const Image *image,Image *watermark,
4979% ExceptionInfo *exception)
4980%
4981% A description of each parameter follows:
4982%
4983% o image: the image.
4984%
4985% o watermark: the watermark image.
4986%
4987% o exception: return any errors or warnings in this structure.
4988%
4989*/
4990MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4991 ExceptionInfo *exception)
4992{
4993#define GetBit(alpha,i) ((((unsigned long) (alpha) >> (unsigned long) \
4994 (i)) & 0x01) != 0)
4995#define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) ? (unsigned long) (alpha) \
4996 | (1UL << (unsigned long) (i)) : (unsigned long) (alpha) & \
4997 ~(1UL << (unsigned long) (i)))
4998#define SteganoImageTag "Stegano/Image"
4999
5000 Image
5001 *stegano_image;
5002
5003 int
5004 c;
5005
5006 long
5007 i,
5008 j,
5009 k,
5010 y;
5011
5012 MagickBooleanType
5013 status;
5014
5015 PixelPacket
5016 pixel;
5017
5018 register long
5019 x;
5020
5021 register PixelPacket
5022 *q;
5023
5024 unsigned long
5025 depth;
5026
5027 /*
5028 Initialize steganographic image attributes.
5029 */
5030 assert(image != (const Image *) NULL);
5031 assert(image->signature == MagickSignature);
5032 if (image->debug != MagickFalse)
5033 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5034 assert(watermark != (const Image *) NULL);
5035 assert(watermark->signature == MagickSignature);
5036 assert(exception != (ExceptionInfo *) NULL);
5037 assert(exception->signature == MagickSignature);
5038 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
5039 if (stegano_image == (Image *) NULL)
5040 return((Image *) NULL);
5041 if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
5042 {
5043 InheritException(exception,&stegano_image->exception);
5044 stegano_image=DestroyImage(stegano_image);
5045 return((Image *) NULL);
5046 }
5047 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
5048 /*
5049 Hide watermark in low-order bits of image.
5050 */
5051 c=0;
5052 i=0;
5053 j=0;
5054 depth=stegano_image->depth;
5055 k=image->offset;
5056 for (i=(long) depth-1; (i >= 0) && (j < (long) depth); i--)
5057 {
5058 for (y=0; (y < (long) watermark->rows) && (j < (long) depth); y++)
5059 {
5060 for (x=0; (x < (long) watermark->columns) && (j < (long) depth); x++)
5061 {
5062 (void) GetOneVirtualPixel(watermark,x,y,&pixel,exception);
5063 if ((k/(long) stegano_image->columns) >= (long) stegano_image->rows)
5064 break;
5065 q=GetAuthenticPixels(stegano_image,k % (long) stegano_image->columns,
5066 k/(long) stegano_image->columns,1,1,exception);
5067 if (q == (PixelPacket *) NULL)
5068 break;
5069 switch (c)
5070 {
5071 case 0:
5072 {
5073 SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5074 break;
5075 }
5076 case 1:
5077 {
5078 SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5079 break;
5080 }
5081 case 2:
5082 {
5083 SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5084 break;
5085 }
5086 }
5087 if (SyncAuthenticPixels(stegano_image,exception) == MagickFalse)
5088 break;
5089 c++;
5090 if (c == 3)
5091 c=0;
5092 k++;
5093 if (k == (long) (stegano_image->columns*stegano_image->columns))
5094 k=0;
5095 if (k == image->offset)
5096 j++;
5097 }
5098 }
5099 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
5100 (QuantumTick((MagickOffsetType) depth-i,depth) != MagickFalse))
5101 {
5102 status=image->progress_monitor(SteganoImageTag,(MagickOffsetType) depth-
5103 i,depth,image->client_data);
5104 if (status == MagickFalse)
5105 break;
5106 }
5107 }
5108 if (stegano_image->storage_class == PseudoClass)
5109 (void) SyncImage(stegano_image);
5110 return(stegano_image);
5111}
5112
5113/*
5114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5115% %
5116% %
5117% %
5118% S t e r e o A n a g l y p h I m a g e %
5119% %
5120% %
5121% %
5122%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5123%
5124% StereoAnaglyphImage() combines two images and produces a single image that
5125% is the composite of a left and right image of a stereo pair. Special
5126% red-green stereo glasses are required to view this effect.
5127%
5128% The format of the StereoAnaglyphImage method is:
5129%
5130% Image *StereoImage(const Image *left_image,const Image *right_image,
5131% ExceptionInfo *exception)
5132% Image *StereoAnaglyphImage(const Image *left_image,
5133% const Image *right_image,const long x_offset,const long y_offset,
5134% ExceptionInfo *exception)
5135%
5136% A description of each parameter follows:
5137%
5138% o left_image: the left image.
5139%
5140% o right_image: the right image.
5141%
5142% o exception: return any errors or warnings in this structure.
5143%
5144% o x_offset: amount, in pixels, by which the left image is offset to the
5145% right of the right image.
5146%
5147% o y_offset: amount, in pixels, by which the left image is offset to the
5148% bottom of the right image.
5149%
5150%
5151*/
5152MagickExport Image *StereoImage(const Image *left_image,
5153 const Image *right_image,ExceptionInfo *exception)
5154{
5155 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
5156}
5157
5158MagickExport Image *StereoAnaglyphImage(const Image *left_image,
5159 const Image *right_image,const long x_offset,const long y_offset,
5160 ExceptionInfo *exception)
5161{
5162#define StereoImageTag "Stereo/Image"
5163
5164 const Image
5165 *image;
5166
5167 Image
5168 *stereo_image;
5169
5170 long
5171 y;
5172
5173 MagickBooleanType
5174 status;
5175
5176 assert(left_image != (const Image *) NULL);
5177 assert(left_image->signature == MagickSignature);
5178 if (left_image->debug != MagickFalse)
5179 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5180 left_image->filename);
5181 assert(right_image != (const Image *) NULL);
5182 assert(right_image->signature == MagickSignature);
5183 assert(exception != (ExceptionInfo *) NULL);
5184 assert(exception->signature == MagickSignature);
5185 assert(right_image != (const Image *) NULL);
5186 image=left_image;
5187 if ((left_image->columns != right_image->columns) ||
5188 (left_image->rows != right_image->rows))
5189 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
5190 /*
5191 Initialize stereo image attributes.
5192 */
5193 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
5194 MagickTrue,exception);
5195 if (stereo_image == (Image *) NULL)
5196 return((Image *) NULL);
5197 if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
5198 {
5199 InheritException(exception,&stereo_image->exception);
5200 stereo_image=DestroyImage(stereo_image);
5201 return((Image *) NULL);
5202 }
5203 /*
5204 Copy left image to red channel and right image to blue channel.
5205 */
5206 for (y=0; y < (long) stereo_image->rows; y++)
5207 {
5208 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005209 *restrict p,
5210 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005211
5212 register long
5213 x;
5214
5215 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005216 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +00005217
5218 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
5219 exception);
5220 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
5221 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
5222 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
5223 (r == (PixelPacket *) NULL))
5224 break;
5225 for (x=0; x < (long) stereo_image->columns; x++)
5226 {
cristyce70c172010-01-07 17:15:30 +00005227 r->red=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00005228 r->green=q->green;
5229 r->blue=q->blue;
5230 r->opacity=(Quantum) ((p->opacity+q->opacity)/2);
5231 p++;
5232 q++;
5233 r++;
5234 }
5235 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
5236 break;
5237 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
5238 (QuantumTick(y,image->rows) != MagickFalse))
5239 {
5240 status=image->progress_monitor(StereoImageTag,y,stereo_image->rows,
5241 stereo_image->client_data);
5242 if (status == MagickFalse)
5243 break;
5244 }
5245 }
5246 return(stereo_image);
5247}
5248
5249/*
5250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5251% %
5252% %
5253% %
5254% S w i r l I m a g e %
5255% %
5256% %
5257% %
5258%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5259%
5260% SwirlImage() swirls the pixels about the center of the image, where
5261% degrees indicates the sweep of the arc through which each pixel is moved.
5262% You get a more dramatic effect as the degrees move from 1 to 360.
5263%
5264% The format of the SwirlImage method is:
5265%
5266% Image *SwirlImage(const Image *image,double degrees,
5267% ExceptionInfo *exception)
5268%
5269% A description of each parameter follows:
5270%
5271% o image: the image.
5272%
5273% o degrees: Define the tightness of the swirling effect.
5274%
5275% o exception: return any errors or warnings in this structure.
5276%
5277*/
5278MagickExport Image *SwirlImage(const Image *image,double degrees,
5279 ExceptionInfo *exception)
5280{
5281#define SwirlImageTag "Swirl/Image"
5282
cristyfa112112010-01-04 17:48:07 +00005283 CacheView
5284 *image_view,
5285 *swirl_view;
5286
cristy3ed852e2009-09-05 21:47:34 +00005287 Image
5288 *swirl_image;
5289
5290 long
5291 progress,
5292 y;
5293
5294 MagickBooleanType
5295 status;
5296
5297 MagickPixelPacket
5298 zero;
5299
5300 MagickRealType
5301 radius;
5302
5303 PointInfo
5304 center,
5305 scale;
5306
5307 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00005308 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00005309
5310 /*
5311 Initialize swirl image attributes.
5312 */
5313 assert(image != (const Image *) NULL);
5314 assert(image->signature == MagickSignature);
5315 if (image->debug != MagickFalse)
5316 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5317 assert(exception != (ExceptionInfo *) NULL);
5318 assert(exception->signature == MagickSignature);
5319 swirl_image=CloneImage(image,0,0,MagickTrue,exception);
5320 if (swirl_image == (Image *) NULL)
5321 return((Image *) NULL);
5322 if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
5323 {
5324 InheritException(exception,&swirl_image->exception);
5325 swirl_image=DestroyImage(swirl_image);
5326 return((Image *) NULL);
5327 }
5328 if (swirl_image->background_color.opacity != OpaqueOpacity)
5329 swirl_image->matte=MagickTrue;
5330 /*
5331 Compute scaling factor.
5332 */
5333 center.x=(double) image->columns/2.0;
5334 center.y=(double) image->rows/2.0;
5335 radius=MagickMax(center.x,center.y);
5336 scale.x=1.0;
5337 scale.y=1.0;
5338 if (image->columns > image->rows)
5339 scale.y=(double) image->columns/(double) image->rows;
5340 else
5341 if (image->columns < image->rows)
5342 scale.x=(double) image->rows/(double) image->columns;
5343 degrees=(double) DegreesToRadians(degrees);
5344 /*
5345 Swirl image.
5346 */
5347 status=MagickTrue;
5348 progress=0;
5349 GetMagickPixelPacket(swirl_image,&zero);
5350 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
5351 image_view=AcquireCacheView(image);
5352 swirl_view=AcquireCacheView(swirl_image);
cristyb5d5f722009-11-04 03:03:49 +00005353#if defined(MAGICKCORE_OPENMP_SUPPORT)
5354 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005355#endif
5356 for (y=0; y < (long) image->rows; y++)
5357 {
5358 MagickPixelPacket
5359 pixel;
5360
5361 MagickRealType
5362 distance;
5363
5364 PointInfo
5365 delta;
5366
5367 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00005368 *restrict swirl_indexes;
cristy3ed852e2009-09-05 21:47:34 +00005369
5370 register long
5371 id,
5372 x;
5373
5374 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005375 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005376
5377 if (status == MagickFalse)
5378 continue;
5379 q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
5380 exception);
5381 if (q == (PixelPacket *) NULL)
5382 {
5383 status=MagickFalse;
5384 continue;
5385 }
5386 swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
5387 delta.y=scale.y*(double) (y-center.y);
5388 pixel=zero;
5389 id=GetOpenMPThreadId();
5390 for (x=0; x < (long) image->columns; x++)
5391 {
5392 /*
5393 Determine if the pixel is within an ellipse.
5394 */
5395 delta.x=scale.x*(double) (x-center.x);
5396 distance=delta.x*delta.x+delta.y*delta.y;
5397 if (distance < (radius*radius))
5398 {
5399 MagickRealType
5400 cosine,
5401 factor,
5402 sine;
5403
5404 /*
5405 Swirl the pixel.
5406 */
5407 factor=1.0-sqrt((double) distance)/radius;
5408 sine=sin((double) (degrees*factor*factor));
5409 cosine=cos((double) (degrees*factor*factor));
5410 (void) ResamplePixelColor(resample_filter[id],(double) ((cosine*
5411 delta.x-sine*delta.y)/scale.x+center.x),(double) ((sine*delta.x+
5412 cosine*delta.y)/scale.y+center.y),&pixel);
5413 SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
5414 }
5415 q++;
5416 }
5417 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5418 status=MagickFalse;
5419 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5420 {
5421 MagickBooleanType
5422 proceed;
5423
cristyb5d5f722009-11-04 03:03:49 +00005424#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005425 #pragma omp critical (MagickCore_SwirlImage)
5426#endif
5427 proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5428 if (proceed == MagickFalse)
5429 status=MagickFalse;
5430 }
5431 }
5432 swirl_view=DestroyCacheView(swirl_view);
5433 image_view=DestroyCacheView(image_view);
5434 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5435 if (status == MagickFalse)
5436 swirl_image=DestroyImage(swirl_image);
5437 return(swirl_image);
5438}
5439
5440/*
5441%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5442% %
5443% %
5444% %
5445% T i n t I m a g e %
5446% %
5447% %
5448% %
5449%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5450%
5451% TintImage() applies a color vector to each pixel in the image. The length
5452% of the vector is 0 for black and white and at its maximum for the midtones.
5453% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5454%
5455% The format of the TintImage method is:
5456%
5457% Image *TintImage(const Image *image,const char *opacity,
5458% const PixelPacket tint,ExceptionInfo *exception)
5459%
5460% A description of each parameter follows:
5461%
5462% o image: the image.
5463%
5464% o opacity: A color value used for tinting.
5465%
5466% o tint: A color value used for tinting.
5467%
5468% o exception: return any errors or warnings in this structure.
5469%
5470*/
5471MagickExport Image *TintImage(const Image *image,const char *opacity,
5472 const PixelPacket tint,ExceptionInfo *exception)
5473{
5474#define TintImageTag "Tint/Image"
5475
cristyc4c8d132010-01-07 01:58:38 +00005476 CacheView
5477 *image_view,
5478 *tint_view;
5479
cristy3ed852e2009-09-05 21:47:34 +00005480 GeometryInfo
5481 geometry_info;
5482
5483 Image
5484 *tint_image;
5485
5486 long
5487 progress,
5488 y;
5489
5490 MagickBooleanType
5491 status;
5492
5493 MagickStatusType
5494 flags;
5495
5496 MagickPixelPacket
5497 color_vector,
5498 pixel;
5499
cristy3ed852e2009-09-05 21:47:34 +00005500 /*
5501 Allocate tint image.
5502 */
5503 assert(image != (const Image *) NULL);
5504 assert(image->signature == MagickSignature);
5505 if (image->debug != MagickFalse)
5506 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5507 assert(exception != (ExceptionInfo *) NULL);
5508 assert(exception->signature == MagickSignature);
5509 tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5510 if (tint_image == (Image *) NULL)
5511 return((Image *) NULL);
5512 if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5513 {
5514 InheritException(exception,&tint_image->exception);
5515 tint_image=DestroyImage(tint_image);
5516 return((Image *) NULL);
5517 }
5518 if (opacity == (const char *) NULL)
5519 return(tint_image);
5520 /*
5521 Determine RGB values of the color.
5522 */
5523 flags=ParseGeometry(opacity,&geometry_info);
5524 pixel.red=geometry_info.rho;
5525 if ((flags & SigmaValue) != 0)
5526 pixel.green=geometry_info.sigma;
5527 else
5528 pixel.green=pixel.red;
5529 if ((flags & XiValue) != 0)
5530 pixel.blue=geometry_info.xi;
5531 else
5532 pixel.blue=pixel.red;
5533 if ((flags & PsiValue) != 0)
5534 pixel.opacity=geometry_info.psi;
5535 else
5536 pixel.opacity=(MagickRealType) OpaqueOpacity;
5537 color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5538 PixelIntensity(&tint));
5539 color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5540 PixelIntensity(&tint));
5541 color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5542 PixelIntensity(&tint));
5543 /*
5544 Tint image.
5545 */
5546 status=MagickTrue;
5547 progress=0;
5548 image_view=AcquireCacheView(image);
5549 tint_view=AcquireCacheView(tint_image);
cristyb5d5f722009-11-04 03:03:49 +00005550#if defined(MAGICKCORE_OPENMP_SUPPORT)
5551 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005552#endif
5553 for (y=0; y < (long) image->rows; y++)
5554 {
5555 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005556 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00005557
5558 register long
5559 x;
5560
5561 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005562 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005563
5564 if (status == MagickFalse)
5565 continue;
5566 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5567 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5568 exception);
5569 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5570 {
5571 status=MagickFalse;
5572 continue;
5573 }
5574 for (x=0; x < (long) image->columns; x++)
5575 {
5576 MagickPixelPacket
5577 pixel;
5578
5579 MagickRealType
5580 weight;
5581
5582 weight=QuantumScale*p->red-0.5;
5583 pixel.red=(MagickRealType) p->red+color_vector.red*(1.0-(4.0*
5584 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005585 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00005586 weight=QuantumScale*p->green-0.5;
5587 pixel.green=(MagickRealType) p->green+color_vector.green*(1.0-(4.0*
5588 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005589 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00005590 weight=QuantumScale*p->blue-0.5;
5591 pixel.blue=(MagickRealType) p->blue+color_vector.blue*(1.0-(4.0*
5592 (weight*weight)));
cristyce70c172010-01-07 17:15:30 +00005593 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5594 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00005595 p++;
5596 q++;
5597 }
5598 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5599 status=MagickFalse;
5600 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5601 {
5602 MagickBooleanType
5603 proceed;
5604
cristyb5d5f722009-11-04 03:03:49 +00005605#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005606 #pragma omp critical (MagickCore_TintImage)
5607#endif
5608 proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5609 if (proceed == MagickFalse)
5610 status=MagickFalse;
5611 }
5612 }
5613 tint_view=DestroyCacheView(tint_view);
5614 image_view=DestroyCacheView(image_view);
5615 if (status == MagickFalse)
5616 tint_image=DestroyImage(tint_image);
5617 return(tint_image);
5618}
5619
5620/*
5621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5622% %
5623% %
5624% %
5625% V i g n e t t e I m a g e %
5626% %
5627% %
5628% %
5629%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5630%
5631% VignetteImage() softens the edges of the image in vignette style.
5632%
5633% The format of the VignetteImage method is:
5634%
5635% Image *VignetteImage(const Image *image,const double radius,
5636% const double sigma,const long x,const long y,ExceptionInfo *exception)
5637%
5638% A description of each parameter follows:
5639%
5640% o image: the image.
5641%
5642% o radius: the radius of the pixel neighborhood.
5643%
5644% o sigma: the standard deviation of the Gaussian, in pixels.
5645%
5646% o x, y: Define the x and y ellipse offset.
5647%
5648% o exception: return any errors or warnings in this structure.
5649%
5650*/
5651MagickExport Image *VignetteImage(const Image *image,const double radius,
5652 const double sigma,const long x,const long y,ExceptionInfo *exception)
5653{
5654 char
5655 ellipse[MaxTextExtent];
5656
5657 DrawInfo
5658 *draw_info;
5659
5660 Image
5661 *canvas_image,
5662 *blur_image,
5663 *oval_image,
5664 *vignette_image;
5665
5666 assert(image != (Image *) NULL);
5667 assert(image->signature == MagickSignature);
5668 if (image->debug != MagickFalse)
5669 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5670 assert(exception != (ExceptionInfo *) NULL);
5671 assert(exception->signature == MagickSignature);
5672 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5673 if (canvas_image == (Image *) NULL)
5674 return((Image *) NULL);
5675 if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
5676 {
5677 InheritException(exception,&canvas_image->exception);
5678 canvas_image=DestroyImage(canvas_image);
5679 return((Image *) NULL);
5680 }
5681 canvas_image->matte=MagickTrue;
5682 oval_image=CloneImage(canvas_image,canvas_image->columns,
5683 canvas_image->rows,MagickTrue,exception);
5684 if (oval_image == (Image *) NULL)
5685 {
5686 canvas_image=DestroyImage(canvas_image);
5687 return((Image *) NULL);
5688 }
5689 (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5690 (void) SetImageBackgroundColor(oval_image);
5691 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5692 (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5693 (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
5694 (void) FormatMagickString(ellipse,MaxTextExtent,
cristy8cd5b312010-01-07 01:10:24 +00005695 "ellipse %.15g,%.15g,%.15g,%.15g,0.0,360.0",image->columns/2.0,
5696 image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
cristy3ed852e2009-09-05 21:47:34 +00005697 draw_info->primitive=AcquireString(ellipse);
5698 (void) DrawImage(oval_image,draw_info);
5699 draw_info=DestroyDrawInfo(draw_info);
5700 blur_image=BlurImage(oval_image,radius,sigma,exception);
5701 oval_image=DestroyImage(oval_image);
5702 if (blur_image == (Image *) NULL)
5703 {
5704 canvas_image=DestroyImage(canvas_image);
5705 return((Image *) NULL);
5706 }
5707 blur_image->matte=MagickFalse;
5708 (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5709 blur_image=DestroyImage(blur_image);
5710 vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5711 canvas_image=DestroyImage(canvas_image);
5712 return(vignette_image);
5713}
5714
5715/*
5716%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5717% %
5718% %
5719% %
5720% W a v e I m a g e %
5721% %
5722% %
5723% %
5724%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5725%
5726% WaveImage() creates a "ripple" effect in the image by shifting the pixels
5727% vertically along a sine wave whose amplitude and wavelength is specified
5728% by the given parameters.
5729%
5730% The format of the WaveImage method is:
5731%
5732% Image *WaveImage(const Image *image,const double amplitude,
5733% const double wave_length,ExceptionInfo *exception)
5734%
5735% A description of each parameter follows:
5736%
5737% o image: the image.
5738%
5739% o amplitude, wave_length: Define the amplitude and wave length of the
5740% sine wave.
5741%
5742% o exception: return any errors or warnings in this structure.
5743%
5744*/
5745MagickExport Image *WaveImage(const Image *image,const double amplitude,
5746 const double wave_length,ExceptionInfo *exception)
5747{
5748#define WaveImageTag "Wave/Image"
5749
cristyfa112112010-01-04 17:48:07 +00005750 CacheView
5751 *wave_view;
5752
cristy3ed852e2009-09-05 21:47:34 +00005753 Image
5754 *wave_image;
5755
5756 long
5757 progress,
5758 y;
5759
5760 MagickBooleanType
5761 status;
5762
5763 MagickPixelPacket
5764 zero;
5765
5766 MagickRealType
5767 *sine_map;
5768
5769 register long
5770 i;
5771
5772 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00005773 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00005774
5775 /*
5776 Initialize wave image attributes.
5777 */
5778 assert(image != (Image *) NULL);
5779 assert(image->signature == MagickSignature);
5780 if (image->debug != MagickFalse)
5781 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5782 assert(exception != (ExceptionInfo *) NULL);
5783 assert(exception->signature == MagickSignature);
5784 wave_image=CloneImage(image,image->columns,(unsigned long) (image->rows+2.0*
5785 fabs(amplitude)),MagickTrue,exception);
5786 if (wave_image == (Image *) NULL)
5787 return((Image *) NULL);
5788 if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
5789 {
5790 InheritException(exception,&wave_image->exception);
5791 wave_image=DestroyImage(wave_image);
5792 return((Image *) NULL);
5793 }
5794 if (wave_image->background_color.opacity != OpaqueOpacity)
5795 wave_image->matte=MagickTrue;
5796 /*
5797 Allocate sine map.
5798 */
5799 sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5800 sizeof(*sine_map));
5801 if (sine_map == (MagickRealType *) NULL)
5802 {
5803 wave_image=DestroyImage(wave_image);
5804 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5805 }
5806 for (i=0; i < (long) wave_image->columns; i++)
5807 sine_map[i]=fabs(amplitude)+amplitude*sin((2*MagickPI*i)/wave_length);
5808 /*
5809 Wave image.
5810 */
5811 status=MagickTrue;
5812 progress=0;
5813 GetMagickPixelPacket(wave_image,&zero);
5814 resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
5815 wave_view=AcquireCacheView(wave_image);
cristyb5d5f722009-11-04 03:03:49 +00005816#if defined(MAGICKCORE_OPENMP_SUPPORT)
5817 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00005818#endif
5819 for (y=0; y < (long) wave_image->rows; y++)
5820 {
5821 MagickPixelPacket
5822 pixel;
5823
5824 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00005825 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00005826
5827 register long
5828 id,
5829 x;
5830
5831 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00005832 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00005833
5834 if (status == MagickFalse)
5835 continue;
5836 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5837 exception);
5838 if (q == (PixelPacket *) NULL)
5839 {
5840 status=MagickFalse;
5841 continue;
5842 }
5843 indexes=GetCacheViewAuthenticIndexQueue(wave_view);
5844 pixel=zero;
5845 id=GetOpenMPThreadId();
5846 (void) SetResampleFilterVirtualPixelMethod(resample_filter[id],
5847 BackgroundVirtualPixelMethod);
5848 for (x=0; x < (long) wave_image->columns; x++)
5849 {
5850 (void) ResamplePixelColor(resample_filter[id],(double) x,(double) (y-
5851 sine_map[x]),&pixel);
5852 SetPixelPacket(wave_image,&pixel,q,indexes+x);
5853 q++;
5854 }
5855 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5856 status=MagickFalse;
5857 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5858 {
5859 MagickBooleanType
5860 proceed;
5861
cristyb5d5f722009-11-04 03:03:49 +00005862#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00005863 #pragma omp critical (MagickCore_WaveImage)
5864#endif
5865 proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5866 if (proceed == MagickFalse)
5867 status=MagickFalse;
5868 }
5869 }
5870 wave_view=DestroyCacheView(wave_view);
5871 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5872 sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5873 if (status == MagickFalse)
5874 wave_image=DestroyImage(wave_image);
5875 return(wave_image);
5876}