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