1 /*
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 % Cristy %
17 % October 1996 %
18 % %
19 % %
20 % %
21 % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
23 % %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
26 % %
27 % https://imagemagick.org/script/license.php %
28 % %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
34 % %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 %
39 */
40
41 /*
42 Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/accelerate-private.h"
46 #include "MagickCore/annotate.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/cache-view.h"
51 #include "MagickCore/channel.h"
52 #include "MagickCore/color.h"
53 #include "MagickCore/color-private.h"
54 #include "MagickCore/colorspace-private.h"
55 #include "MagickCore/composite.h"
56 #include "MagickCore/decorate.h"
57 #include "MagickCore/distort.h"
58 #include "MagickCore/draw.h"
59 #include "MagickCore/effect.h"
60 #include "MagickCore/enhance.h"
61 #include "MagickCore/exception.h"
62 #include "MagickCore/exception-private.h"
63 #include "MagickCore/fx.h"
64 #include "MagickCore/fx-private.h"
65 #include "MagickCore/gem.h"
66 #include "MagickCore/gem-private.h"
67 #include "MagickCore/geometry.h"
68 #include "MagickCore/layer.h"
69 #include "MagickCore/list.h"
70 #include "MagickCore/log.h"
71 #include "MagickCore/image.h"
72 #include "MagickCore/image-private.h"
73 #include "MagickCore/magick.h"
74 #include "MagickCore/memory_.h"
75 #include "MagickCore/memory-private.h"
76 #include "MagickCore/monitor.h"
77 #include "MagickCore/monitor-private.h"
78 #include "MagickCore/option.h"
79 #include "MagickCore/pixel.h"
80 #include "MagickCore/pixel-accessor.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum.h"
83 #include "MagickCore/quantum-private.h"
84 #include "MagickCore/random_.h"
85 #include "MagickCore/random-private.h"
86 #include "MagickCore/resample.h"
87 #include "MagickCore/resample-private.h"
88 #include "MagickCore/resize.h"
89 #include "MagickCore/resource_.h"
90 #include "MagickCore/splay-tree.h"
91 #include "MagickCore/statistic.h"
92 #include "MagickCore/string_.h"
93 #include "MagickCore/string-private.h"
94 #include "MagickCore/thread-private.h"
95 #include "MagickCore/threshold.h"
96 #include "MagickCore/transform.h"
97 #include "MagickCore/transform-private.h"
98 #include "MagickCore/utility.h"
99
100 /*
101 Define declarations.
102 */
103 #define LeftShiftOperator 0xf5U
104 #define RightShiftOperator 0xf6U
105 #define LessThanEqualOperator 0xf7U
106 #define GreaterThanEqualOperator 0xf8U
107 #define EqualOperator 0xf9U
108 #define NotEqualOperator 0xfaU
109 #define LogicalAndOperator 0xfbU
110 #define LogicalOrOperator 0xfcU
111 #define ExponentialNotation 0xfdU
112
113 struct _FxInfo
114 {
115 const Image
116 *images;
117
118 char
119 *expression;
120
121 FILE
122 *file;
123
124 SplayTreeInfo
125 *colors,
126 *symbols;
127
128 CacheView
129 **view;
130
131 RandomInfo
132 *random_info;
133
134 ExceptionInfo
135 *exception;
136 };
137
138 /*
139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140 % %
141 % %
142 % %
143 + A c q u i r e F x I n f o %
144 % %
145 % %
146 % %
147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148 %
149 % AcquireFxInfo() allocates the FxInfo structure.
150 %
151 % The format of the AcquireFxInfo method is:
152 %
153 % FxInfo *AcquireFxInfo(Image *images,const char *expression,
154 % ExceptionInfo *exception)
155 %
156 % A description of each parameter follows:
157 %
158 % o images: the image sequence.
159 %
160 % o expression: the expression.
161 %
162 % o exception: return any errors or warnings in this structure.
163 %
164 */
AcquireFxInfo(const Image * images,const char * expression,ExceptionInfo * exception)165 MagickPrivate FxInfo *AcquireFxInfo(const Image *images,const char *expression,
166 ExceptionInfo *exception)
167 {
168 char
169 fx_op[2];
170
171 const Image
172 *next;
173
174 FxInfo
175 *fx_info;
176
177 register ssize_t
178 i;
179
180 fx_info=(FxInfo *) AcquireCriticalMemory(sizeof(*fx_info));
181 (void) memset(fx_info,0,sizeof(*fx_info));
182 fx_info->exception=AcquireExceptionInfo();
183 fx_info->images=images;
184 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
185 RelinquishMagickMemory);
186 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
187 RelinquishMagickMemory);
188 fx_info->view=(CacheView **) AcquireQuantumMemory(GetImageListLength(
189 fx_info->images),sizeof(*fx_info->view));
190 if (fx_info->view == (CacheView **) NULL)
191 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
192 i=0;
193 next=GetFirstImageInList(fx_info->images);
194 for ( ; next != (Image *) NULL; next=next->next)
195 {
196 fx_info->view[i]=AcquireVirtualCacheView(next,exception);
197 i++;
198 }
199 fx_info->random_info=AcquireRandomInfo();
200 fx_info->expression=ConstantString(expression);
201 fx_info->file=stderr;
202 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
203 /*
204 Force right-to-left associativity for unary negation.
205 */
206 (void) SubstituteString(&fx_info->expression,"-","-1.0*");
207 (void) SubstituteString(&fx_info->expression,"^-1.0*","^-");
208 (void) SubstituteString(&fx_info->expression,"E-1.0*","E-");
209 (void) SubstituteString(&fx_info->expression,"e-1.0*","e-");
210 /*
211 Convert compound to simple operators.
212 */
213 fx_op[1]='\0';
214 *fx_op=(char) LeftShiftOperator;
215 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
216 *fx_op=(char) RightShiftOperator;
217 (void) SubstituteString(&fx_info->expression,">>",fx_op);
218 *fx_op=(char) LessThanEqualOperator;
219 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
220 *fx_op=(char) GreaterThanEqualOperator;
221 (void) SubstituteString(&fx_info->expression,">=",fx_op);
222 *fx_op=(char) EqualOperator;
223 (void) SubstituteString(&fx_info->expression,"==",fx_op);
224 *fx_op=(char) NotEqualOperator;
225 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
226 *fx_op=(char) LogicalAndOperator;
227 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
228 *fx_op=(char) LogicalOrOperator;
229 (void) SubstituteString(&fx_info->expression,"||",fx_op);
230 *fx_op=(char) ExponentialNotation;
231 (void) SubstituteString(&fx_info->expression,"**",fx_op);
232 return(fx_info);
233 }
234
235 /*
236 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
237 % %
238 % %
239 % %
240 % A d d N o i s e I m a g e %
241 % %
242 % %
243 % %
244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245 %
246 % AddNoiseImage() adds random noise to the image.
247 %
248 % The format of the AddNoiseImage method is:
249 %
250 % Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
251 % const double attenuate,ExceptionInfo *exception)
252 %
253 % A description of each parameter follows:
254 %
255 % o image: the image.
256 %
257 % o channel: the channel type.
258 %
259 % o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
260 % Impulse, Laplacian, or Poisson.
261 %
262 % o attenuate: attenuate the random distribution.
263 %
264 % o exception: return any errors or warnings in this structure.
265 %
266 */
AddNoiseImage(const Image * image,const NoiseType noise_type,const double attenuate,ExceptionInfo * exception)267 MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
268 const double attenuate,ExceptionInfo *exception)
269 {
270 #define AddNoiseImageTag "AddNoise/Image"
271
272 CacheView
273 *image_view,
274 *noise_view;
275
276 Image
277 *noise_image;
278
279 MagickBooleanType
280 status;
281
282 MagickOffsetType
283 progress;
284
285 RandomInfo
286 **magick_restrict random_info;
287
288 ssize_t
289 y;
290
291 #if defined(MAGICKCORE_OPENMP_SUPPORT)
292 unsigned long
293 key;
294 #endif
295
296 /*
297 Initialize noise image attributes.
298 */
299 assert(image != (const Image *) NULL);
300 assert(image->signature == MagickCoreSignature);
301 if (image->debug != MagickFalse)
302 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
303 assert(exception != (ExceptionInfo *) NULL);
304 assert(exception->signature == MagickCoreSignature);
305 #if defined(MAGICKCORE_OPENCL_SUPPORT)
306 noise_image=AccelerateAddNoiseImage(image,noise_type,attenuate,exception);
307 if (noise_image != (Image *) NULL)
308 return(noise_image);
309 #endif
310 noise_image=CloneImage(image,0,0,MagickTrue,exception);
311 if (noise_image == (Image *) NULL)
312 return((Image *) NULL);
313 if (SetImageStorageClass(noise_image,DirectClass,exception) == MagickFalse)
314 {
315 noise_image=DestroyImage(noise_image);
316 return((Image *) NULL);
317 }
318 /*
319 Add noise in each row.
320 */
321 status=MagickTrue;
322 progress=0;
323 random_info=AcquireRandomInfoThreadSet();
324 image_view=AcquireVirtualCacheView(image,exception);
325 noise_view=AcquireAuthenticCacheView(noise_image,exception);
326 #if defined(MAGICKCORE_OPENMP_SUPPORT)
327 key=GetRandomSecretKey(random_info[0]);
328 #pragma omp parallel for schedule(static) shared(progress,status) \
329 magick_number_threads(image,noise_image,image->rows,key == ~0UL)
330 #endif
331 for (y=0; y < (ssize_t) image->rows; y++)
332 {
333 const int
334 id = GetOpenMPThreadId();
335
336 MagickBooleanType
337 sync;
338
339 register const Quantum
340 *magick_restrict p;
341
342 register ssize_t
343 x;
344
345 register Quantum
346 *magick_restrict q;
347
348 if (status == MagickFalse)
349 continue;
350 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
351 q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
352 exception);
353 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
354 {
355 status=MagickFalse;
356 continue;
357 }
358 for (x=0; x < (ssize_t) image->columns; x++)
359 {
360 register ssize_t
361 i;
362
363 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
364 {
365 PixelChannel channel = GetPixelChannelChannel(image,i);
366 PixelTrait traits = GetPixelChannelTraits(image,channel);
367 PixelTrait noise_traits=GetPixelChannelTraits(noise_image,channel);
368 if ((traits == UndefinedPixelTrait) ||
369 (noise_traits == UndefinedPixelTrait))
370 continue;
371 if ((noise_traits & CopyPixelTrait) != 0)
372 {
373 SetPixelChannel(noise_image,channel,p[i],q);
374 continue;
375 }
376 SetPixelChannel(noise_image,channel,ClampToQuantum(
377 GenerateDifferentialNoise(random_info[id],p[i],noise_type,attenuate)),
378 q);
379 }
380 p+=GetPixelChannels(image);
381 q+=GetPixelChannels(noise_image);
382 }
383 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
384 if (sync == MagickFalse)
385 status=MagickFalse;
386 if (image->progress_monitor != (MagickProgressMonitor) NULL)
387 {
388 MagickBooleanType
389 proceed;
390
391 #if defined(MAGICKCORE_OPENMP_SUPPORT)
392 #pragma omp atomic
393 #endif
394 progress++;
395 proceed=SetImageProgress(image,AddNoiseImageTag,progress,image->rows);
396 if (proceed == MagickFalse)
397 status=MagickFalse;
398 }
399 }
400 noise_view=DestroyCacheView(noise_view);
401 image_view=DestroyCacheView(image_view);
402 random_info=DestroyRandomInfoThreadSet(random_info);
403 if (status == MagickFalse)
404 noise_image=DestroyImage(noise_image);
405 return(noise_image);
406 }
407
408 /*
409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
410 % %
411 % %
412 % %
413 % B l u e S h i f t I m a g e %
414 % %
415 % %
416 % %
417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418 %
419 % BlueShiftImage() mutes the colors of the image to simulate a scene at
420 % nighttime in the moonlight.
421 %
422 % The format of the BlueShiftImage method is:
423 %
424 % Image *BlueShiftImage(const Image *image,const double factor,
425 % ExceptionInfo *exception)
426 %
427 % A description of each parameter follows:
428 %
429 % o image: the image.
430 %
431 % o factor: the shift factor.
432 %
433 % o exception: return any errors or warnings in this structure.
434 %
435 */
BlueShiftImage(const Image * image,const double factor,ExceptionInfo * exception)436 MagickExport Image *BlueShiftImage(const Image *image,const double factor,
437 ExceptionInfo *exception)
438 {
439 #define BlueShiftImageTag "BlueShift/Image"
440
441 CacheView
442 *image_view,
443 *shift_view;
444
445 Image
446 *shift_image;
447
448 MagickBooleanType
449 status;
450
451 MagickOffsetType
452 progress;
453
454 ssize_t
455 y;
456
457 /*
458 Allocate blue shift image.
459 */
460 assert(image != (const Image *) NULL);
461 assert(image->signature == MagickCoreSignature);
462 if (image->debug != MagickFalse)
463 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
464 assert(exception != (ExceptionInfo *) NULL);
465 assert(exception->signature == MagickCoreSignature);
466 shift_image=CloneImage(image,0,0,MagickTrue,exception);
467 if (shift_image == (Image *) NULL)
468 return((Image *) NULL);
469 if (SetImageStorageClass(shift_image,DirectClass,exception) == MagickFalse)
470 {
471 shift_image=DestroyImage(shift_image);
472 return((Image *) NULL);
473 }
474 /*
475 Blue-shift DirectClass image.
476 */
477 status=MagickTrue;
478 progress=0;
479 image_view=AcquireVirtualCacheView(image,exception);
480 shift_view=AcquireAuthenticCacheView(shift_image,exception);
481 #if defined(MAGICKCORE_OPENMP_SUPPORT)
482 #pragma omp parallel for schedule(static) shared(progress,status) \
483 magick_number_threads(image,shift_image,image->rows,1)
484 #endif
485 for (y=0; y < (ssize_t) image->rows; y++)
486 {
487 MagickBooleanType
488 sync;
489
490 PixelInfo
491 pixel;
492
493 Quantum
494 quantum;
495
496 register const Quantum
497 *magick_restrict p;
498
499 register ssize_t
500 x;
501
502 register Quantum
503 *magick_restrict q;
504
505 if (status == MagickFalse)
506 continue;
507 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
508 q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
509 exception);
510 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
511 {
512 status=MagickFalse;
513 continue;
514 }
515 for (x=0; x < (ssize_t) image->columns; x++)
516 {
517 quantum=GetPixelRed(image,p);
518 if (GetPixelGreen(image,p) < quantum)
519 quantum=GetPixelGreen(image,p);
520 if (GetPixelBlue(image,p) < quantum)
521 quantum=GetPixelBlue(image,p);
522 pixel.red=0.5*(GetPixelRed(image,p)+factor*quantum);
523 pixel.green=0.5*(GetPixelGreen(image,p)+factor*quantum);
524 pixel.blue=0.5*(GetPixelBlue(image,p)+factor*quantum);
525 quantum=GetPixelRed(image,p);
526 if (GetPixelGreen(image,p) > quantum)
527 quantum=GetPixelGreen(image,p);
528 if (GetPixelBlue(image,p) > quantum)
529 quantum=GetPixelBlue(image,p);
530 pixel.red=0.5*(pixel.red+factor*quantum);
531 pixel.green=0.5*(pixel.green+factor*quantum);
532 pixel.blue=0.5*(pixel.blue+factor*quantum);
533 SetPixelRed(shift_image,ClampToQuantum(pixel.red),q);
534 SetPixelGreen(shift_image,ClampToQuantum(pixel.green),q);
535 SetPixelBlue(shift_image,ClampToQuantum(pixel.blue),q);
536 p+=GetPixelChannels(image);
537 q+=GetPixelChannels(shift_image);
538 }
539 sync=SyncCacheViewAuthenticPixels(shift_view,exception);
540 if (sync == MagickFalse)
541 status=MagickFalse;
542 if (image->progress_monitor != (MagickProgressMonitor) NULL)
543 {
544 MagickBooleanType
545 proceed;
546
547 #if defined(MAGICKCORE_OPENMP_SUPPORT)
548 #pragma omp atomic
549 #endif
550 progress++;
551 proceed=SetImageProgress(image,BlueShiftImageTag,progress,image->rows);
552 if (proceed == MagickFalse)
553 status=MagickFalse;
554 }
555 }
556 image_view=DestroyCacheView(image_view);
557 shift_view=DestroyCacheView(shift_view);
558 if (status == MagickFalse)
559 shift_image=DestroyImage(shift_image);
560 return(shift_image);
561 }
562
563 /*
564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
565 % %
566 % %
567 % %
568 % C h a r c o a l I m a g e %
569 % %
570 % %
571 % %
572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
573 %
574 % CharcoalImage() creates a new image that is a copy of an existing one with
575 % the edge highlighted. It allocates the memory necessary for the new Image
576 % structure and returns a pointer to the new image.
577 %
578 % The format of the CharcoalImage method is:
579 %
580 % Image *CharcoalImage(const Image *image,const double radius,
581 % const double sigma,ExceptionInfo *exception)
582 %
583 % A description of each parameter follows:
584 %
585 % o image: the image.
586 %
587 % o radius: the radius of the pixel neighborhood.
588 %
589 % o sigma: the standard deviation of the Gaussian, in pixels.
590 %
591 % o exception: return any errors or warnings in this structure.
592 %
593 */
CharcoalImage(const Image * image,const double radius,const double sigma,ExceptionInfo * exception)594 MagickExport Image *CharcoalImage(const Image *image,const double radius,
595 const double sigma,ExceptionInfo *exception)
596 {
597 Image
598 *charcoal_image,
599 *edge_image;
600
601 MagickBooleanType
602 status;
603
604 assert(image != (Image *) NULL);
605 assert(image->signature == MagickCoreSignature);
606 if (image->debug != MagickFalse)
607 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
608 assert(exception != (ExceptionInfo *) NULL);
609 assert(exception->signature == MagickCoreSignature);
610 edge_image=EdgeImage(image,radius,exception);
611 if (edge_image == (Image *) NULL)
612 return((Image *) NULL);
613 charcoal_image=(Image *) NULL;
614 status=ClampImage(edge_image,exception);
615 if (status != MagickFalse)
616 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
617 edge_image=DestroyImage(edge_image);
618 if (charcoal_image == (Image *) NULL)
619 return((Image *) NULL);
620 status=NormalizeImage(charcoal_image,exception);
621 if (status != MagickFalse)
622 status=NegateImage(charcoal_image,MagickFalse,exception);
623 if (status != MagickFalse)
624 status=GrayscaleImage(charcoal_image,image->intensity,exception);
625 if (status == MagickFalse)
626 charcoal_image=DestroyImage(charcoal_image);
627 return(charcoal_image);
628 }
629
630 /*
631 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
632 % %
633 % %
634 % %
635 % C o l o r i z e I m a g e %
636 % %
637 % %
638 % %
639 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
640 %
641 % ColorizeImage() blends the fill color with each pixel in the image.
642 % A percentage blend is specified with opacity. Control the application
643 % of different color components by specifying a different percentage for
644 % each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
645 %
646 % The format of the ColorizeImage method is:
647 %
648 % Image *ColorizeImage(const Image *image,const char *blend,
649 % const PixelInfo *colorize,ExceptionInfo *exception)
650 %
651 % A description of each parameter follows:
652 %
653 % o image: the image.
654 %
655 % o blend: A character string indicating the level of blending as a
656 % percentage.
657 %
658 % o colorize: A color value.
659 %
660 % o exception: return any errors or warnings in this structure.
661 %
662 */
ColorizeImage(const Image * image,const char * blend,const PixelInfo * colorize,ExceptionInfo * exception)663 MagickExport Image *ColorizeImage(const Image *image,const char *blend,
664 const PixelInfo *colorize,ExceptionInfo *exception)
665 {
666 #define ColorizeImageTag "Colorize/Image"
667 #define Colorize(pixel,blend_percentage,colorize) \
668 (((pixel)*(100.0-(blend_percentage))+(colorize)*(blend_percentage))/100.0)
669
670 CacheView
671 *image_view;
672
673 GeometryInfo
674 geometry_info;
675
676 Image
677 *colorize_image;
678
679 MagickBooleanType
680 status;
681
682 MagickOffsetType
683 progress;
684
685 MagickStatusType
686 flags;
687
688 PixelInfo
689 blend_percentage;
690
691 ssize_t
692 y;
693
694 /*
695 Allocate colorized image.
696 */
697 assert(image != (const Image *) NULL);
698 assert(image->signature == MagickCoreSignature);
699 if (image->debug != MagickFalse)
700 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
701 assert(exception != (ExceptionInfo *) NULL);
702 assert(exception->signature == MagickCoreSignature);
703 colorize_image=CloneImage(image,0,0,MagickTrue,exception);
704 if (colorize_image == (Image *) NULL)
705 return((Image *) NULL);
706 if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
707 {
708 colorize_image=DestroyImage(colorize_image);
709 return((Image *) NULL);
710 }
711 if ((IsGrayColorspace(colorize_image->colorspace) != MagickFalse) ||
712 (IsPixelInfoGray(colorize) != MagickFalse))
713 (void) SetImageColorspace(colorize_image,sRGBColorspace,exception);
714 if ((colorize_image->alpha_trait == UndefinedPixelTrait) &&
715 (colorize->alpha_trait != UndefinedPixelTrait))
716 (void) SetImageAlpha(colorize_image,OpaqueAlpha,exception);
717 if (blend == (const char *) NULL)
718 return(colorize_image);
719 GetPixelInfo(colorize_image,&blend_percentage);
720 flags=ParseGeometry(blend,&geometry_info);
721 blend_percentage.red=geometry_info.rho;
722 blend_percentage.green=geometry_info.rho;
723 blend_percentage.blue=geometry_info.rho;
724 blend_percentage.black=geometry_info.rho;
725 blend_percentage.alpha=(MagickRealType) TransparentAlpha;
726 if ((flags & SigmaValue) != 0)
727 blend_percentage.green=geometry_info.sigma;
728 if ((flags & XiValue) != 0)
729 blend_percentage.blue=geometry_info.xi;
730 if ((flags & PsiValue) != 0)
731 blend_percentage.alpha=geometry_info.psi;
732 if (blend_percentage.colorspace == CMYKColorspace)
733 {
734 if ((flags & PsiValue) != 0)
735 blend_percentage.black=geometry_info.psi;
736 if ((flags & ChiValue) != 0)
737 blend_percentage.alpha=geometry_info.chi;
738 }
739 /*
740 Colorize DirectClass image.
741 */
742 status=MagickTrue;
743 progress=0;
744 image_view=AcquireVirtualCacheView(colorize_image,exception);
745 #if defined(MAGICKCORE_OPENMP_SUPPORT)
746 #pragma omp parallel for schedule(static) shared(progress,status) \
747 magick_number_threads(colorize_image,colorize_image,colorize_image->rows,1)
748 #endif
749 for (y=0; y < (ssize_t) colorize_image->rows; y++)
750 {
751 MagickBooleanType
752 sync;
753
754 register Quantum
755 *magick_restrict q;
756
757 register ssize_t
758 x;
759
760 if (status == MagickFalse)
761 continue;
762 q=GetCacheViewAuthenticPixels(image_view,0,y,colorize_image->columns,1,
763 exception);
764 if (q == (Quantum *) NULL)
765 {
766 status=MagickFalse;
767 continue;
768 }
769 for (x=0; x < (ssize_t) colorize_image->columns; x++)
770 {
771 register ssize_t
772 i;
773
774 for (i=0; i < (ssize_t) GetPixelChannels(colorize_image); i++)
775 {
776 PixelTrait traits = GetPixelChannelTraits(colorize_image,
777 (PixelChannel) i);
778 if (traits == UndefinedPixelTrait)
779 continue;
780 if ((traits & CopyPixelTrait) != 0)
781 continue;
782 SetPixelChannel(colorize_image,(PixelChannel) i,ClampToQuantum(
783 Colorize(q[i],GetPixelInfoChannel(&blend_percentage,(PixelChannel) i),
784 GetPixelInfoChannel(colorize,(PixelChannel) i))),q);
785 }
786 q+=GetPixelChannels(colorize_image);
787 }
788 sync=SyncCacheViewAuthenticPixels(image_view,exception);
789 if (sync == MagickFalse)
790 status=MagickFalse;
791 if (image->progress_monitor != (MagickProgressMonitor) NULL)
792 {
793 MagickBooleanType
794 proceed;
795
796 #if defined(MAGICKCORE_OPENMP_SUPPORT)
797 #pragma omp atomic
798 #endif
799 progress++;
800 proceed=SetImageProgress(image,ColorizeImageTag,progress,
801 colorize_image->rows);
802 if (proceed == MagickFalse)
803 status=MagickFalse;
804 }
805 }
806 image_view=DestroyCacheView(image_view);
807 if (status == MagickFalse)
808 colorize_image=DestroyImage(colorize_image);
809 return(colorize_image);
810 }
811
812 /*
813 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814 % %
815 % %
816 % %
817 % C o l o r M a t r i x I m a g e %
818 % %
819 % %
820 % %
821 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
822 %
823 % ColorMatrixImage() applies color transformation to an image. This method
824 % permits saturation changes, hue rotation, luminance to alpha, and various
825 % other effects. Although variable-sized transformation matrices can be used,
826 % typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
827 % (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
828 % except offsets are in column 6 rather than 5 (in support of CMYKA images)
829 % and offsets are normalized (divide Flash offset by 255).
830 %
831 % The format of the ColorMatrixImage method is:
832 %
833 % Image *ColorMatrixImage(const Image *image,
834 % const KernelInfo *color_matrix,ExceptionInfo *exception)
835 %
836 % A description of each parameter follows:
837 %
838 % o image: the image.
839 %
840 % o color_matrix: the color matrix.
841 %
842 % o exception: return any errors or warnings in this structure.
843 %
844 */
845 /* FUTURE: modify to make use of a MagickMatrix Mutliply function
846 That should be provided in "matrix.c"
847 (ASIDE: actually distorts should do this too but currently doesn't)
848 */
849
ColorMatrixImage(const Image * image,const KernelInfo * color_matrix,ExceptionInfo * exception)850 MagickExport Image *ColorMatrixImage(const Image *image,
851 const KernelInfo *color_matrix,ExceptionInfo *exception)
852 {
853 #define ColorMatrixImageTag "ColorMatrix/Image"
854
855 CacheView
856 *color_view,
857 *image_view;
858
859 double
860 ColorMatrix[6][6] =
861 {
862 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
863 { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
864 { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
865 { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
866 { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
867 { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
868 };
869
870 Image
871 *color_image;
872
873 MagickBooleanType
874 status;
875
876 MagickOffsetType
877 progress;
878
879 register ssize_t
880 i;
881
882 ssize_t
883 u,
884 v,
885 y;
886
887 /*
888 Map given color_matrix, into a 6x6 matrix RGBKA and a constant
889 */
890 assert(image != (Image *) NULL);
891 assert(image->signature == MagickCoreSignature);
892 if (image->debug != MagickFalse)
893 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
894 assert(exception != (ExceptionInfo *) NULL);
895 assert(exception->signature == MagickCoreSignature);
896 i=0;
897 for (v=0; v < (ssize_t) color_matrix->height; v++)
898 for (u=0; u < (ssize_t) color_matrix->width; u++)
899 {
900 if ((v < 6) && (u < 6))
901 ColorMatrix[v][u]=color_matrix->values[i];
902 i++;
903 }
904 /*
905 Initialize color image.
906 */
907 color_image=CloneImage(image,0,0,MagickTrue,exception);
908 if (color_image == (Image *) NULL)
909 return((Image *) NULL);
910 if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
911 {
912 color_image=DestroyImage(color_image);
913 return((Image *) NULL);
914 }
915 if (image->debug != MagickFalse)
916 {
917 char
918 format[MagickPathExtent],
919 *message;
920
921 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
922 " ColorMatrix image with color matrix:");
923 message=AcquireString("");
924 for (v=0; v < 6; v++)
925 {
926 *message='\0';
927 (void) FormatLocaleString(format,MagickPathExtent,"%.20g: ",(double) v);
928 (void) ConcatenateString(&message,format);
929 for (u=0; u < 6; u++)
930 {
931 (void) FormatLocaleString(format,MagickPathExtent,"%+f ",
932 ColorMatrix[v][u]);
933 (void) ConcatenateString(&message,format);
934 }
935 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
936 }
937 message=DestroyString(message);
938 }
939 /*
940 Apply the ColorMatrix to image.
941 */
942 status=MagickTrue;
943 progress=0;
944 image_view=AcquireVirtualCacheView(image,exception);
945 color_view=AcquireAuthenticCacheView(color_image,exception);
946 #if defined(MAGICKCORE_OPENMP_SUPPORT)
947 #pragma omp parallel for schedule(static) shared(progress,status) \
948 magick_number_threads(image,color_image,image->rows,1)
949 #endif
950 for (y=0; y < (ssize_t) image->rows; y++)
951 {
952 PixelInfo
953 pixel;
954
955 register const Quantum
956 *magick_restrict p;
957
958 register Quantum
959 *magick_restrict q;
960
961 register ssize_t
962 x;
963
964 if (status == MagickFalse)
965 continue;
966 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
967 q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
968 exception);
969 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
970 {
971 status=MagickFalse;
972 continue;
973 }
974 GetPixelInfo(image,&pixel);
975 for (x=0; x < (ssize_t) image->columns; x++)
976 {
977 register ssize_t
978 v;
979
980 size_t
981 height;
982
983 GetPixelInfoPixel(image,p,&pixel);
984 height=color_matrix->height > 6 ? 6UL : color_matrix->height;
985 for (v=0; v < (ssize_t) height; v++)
986 {
987 double
988 sum;
989
990 sum=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
991 GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
992 if (image->colorspace == CMYKColorspace)
993 sum+=ColorMatrix[v][3]*GetPixelBlack(image,p);
994 if (image->alpha_trait != UndefinedPixelTrait)
995 sum+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
996 sum+=QuantumRange*ColorMatrix[v][5];
997 switch (v)
998 {
999 case 0: pixel.red=sum; break;
1000 case 1: pixel.green=sum; break;
1001 case 2: pixel.blue=sum; break;
1002 case 3: pixel.black=sum; break;
1003 case 4: pixel.alpha=sum; break;
1004 default: break;
1005 }
1006 }
1007 SetPixelViaPixelInfo(color_image,&pixel,q);
1008 p+=GetPixelChannels(image);
1009 q+=GetPixelChannels(color_image);
1010 }
1011 if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1012 status=MagickFalse;
1013 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1014 {
1015 MagickBooleanType
1016 proceed;
1017
1018 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1019 #pragma omp atomic
1020 #endif
1021 progress++;
1022 proceed=SetImageProgress(image,ColorMatrixImageTag,progress,
1023 image->rows);
1024 if (proceed == MagickFalse)
1025 status=MagickFalse;
1026 }
1027 }
1028 color_view=DestroyCacheView(color_view);
1029 image_view=DestroyCacheView(image_view);
1030 if (status == MagickFalse)
1031 color_image=DestroyImage(color_image);
1032 return(color_image);
1033 }
1034
1035 /*
1036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1037 % %
1038 % %
1039 % %
1040 + D e s t r o y F x I n f o %
1041 % %
1042 % %
1043 % %
1044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1045 %
1046 % DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1047 %
1048 % The format of the DestroyFxInfo method is:
1049 %
1050 % ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1051 %
1052 % A description of each parameter follows:
1053 %
1054 % o fx_info: the fx info.
1055 %
1056 */
DestroyFxInfo(FxInfo * fx_info)1057 MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
1058 {
1059 register ssize_t
1060 i;
1061
1062 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1063 fx_info->expression=DestroyString(fx_info->expression);
1064 fx_info->symbols=DestroySplayTree(fx_info->symbols);
1065 fx_info->colors=DestroySplayTree(fx_info->colors);
1066 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
1067 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1068 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
1069 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1070 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1071 return(fx_info);
1072 }
1073
1074 /*
1075 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1076 % %
1077 % %
1078 % %
1079 + 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 %
1080 % %
1081 % %
1082 % %
1083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1084 %
1085 % FxEvaluateChannelExpression() evaluates an expression and returns the
1086 % results.
1087 %
1088 % The format of the FxEvaluateExpression method is:
1089 %
1090 % double FxEvaluateChannelExpression(FxInfo *fx_info,
1091 % const PixelChannel channel,const ssize_t x,const ssize_t y,
1092 % double *alpha,Exceptioninfo *exception)
1093 % double FxEvaluateExpression(FxInfo *fx_info,
1094 % double *alpha,Exceptioninfo *exception)
1095 %
1096 % A description of each parameter follows:
1097 %
1098 % o fx_info: the fx info.
1099 %
1100 % o channel: the channel.
1101 %
1102 % o x,y: the pixel position.
1103 %
1104 % o alpha: the result.
1105 %
1106 % o exception: return any errors or warnings in this structure.
1107 %
1108 */
1109
FxChannelStatistics(FxInfo * fx_info,Image * image,PixelChannel channel,const char * symbol,ExceptionInfo * exception)1110 static double FxChannelStatistics(FxInfo *fx_info,Image *image,
1111 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
1112 {
1113 ChannelType
1114 channel_mask;
1115
1116 char
1117 key[MagickPathExtent],
1118 statistic[MagickPathExtent];
1119
1120 const char
1121 *value;
1122
1123 register const char
1124 *p;
1125
1126 channel_mask=UndefinedChannel;
1127 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1128 if (*p == '.')
1129 {
1130 ssize_t
1131 option;
1132
1133 option=ParseCommandOption(MagickPixelChannelOptions,MagickTrue,p+1);
1134 if (option >= 0)
1135 {
1136 channel=(PixelChannel) option;
1137 channel_mask=SetPixelChannelMask(image,(ChannelType)
1138 (1UL << channel));
1139 }
1140 }
1141 (void) FormatLocaleString(key,MagickPathExtent,"%p.%.20g.%s",(void *) image,
1142 (double) channel,symbol);
1143 value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1144 if (value != (const char *) NULL)
1145 {
1146 if (channel_mask != UndefinedChannel)
1147 (void) SetPixelChannelMask(image,channel_mask);
1148 return(QuantumScale*StringToDouble(value,(char **) NULL));
1149 }
1150 (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1151 if (LocaleNCompare(symbol,"depth",5) == 0)
1152 {
1153 size_t
1154 depth;
1155
1156 depth=GetImageDepth(image,exception);
1157 (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",(double)
1158 depth);
1159 }
1160 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1161 {
1162 double
1163 kurtosis,
1164 skewness;
1165
1166 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
1167 (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",kurtosis);
1168 }
1169 if (LocaleNCompare(symbol,"maxima",6) == 0)
1170 {
1171 double
1172 maxima,
1173 minima;
1174
1175 (void) GetImageRange(image,&minima,&maxima,exception);
1176 (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",maxima);
1177 }
1178 if (LocaleNCompare(symbol,"mean",4) == 0)
1179 {
1180 double
1181 mean,
1182 standard_deviation;
1183
1184 (void) GetImageMean(image,&mean,&standard_deviation,exception);
1185 (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",mean);
1186 }
1187 if (LocaleNCompare(symbol,"minima",6) == 0)
1188 {
1189 double
1190 maxima,
1191 minima;
1192
1193 (void) GetImageRange(image,&minima,&maxima,exception);
1194 (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",minima);
1195 }
1196 if (LocaleNCompare(symbol,"skewness",8) == 0)
1197 {
1198 double
1199 kurtosis,
1200 skewness;
1201
1202 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
1203 (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",skewness);
1204 }
1205 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1206 {
1207 double
1208 mean,
1209 standard_deviation;
1210
1211 (void) GetImageMean(image,&mean,&standard_deviation,exception);
1212 (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",
1213 standard_deviation);
1214 }
1215 if (channel_mask != UndefinedChannel)
1216 (void) SetPixelChannelMask(image,channel_mask);
1217 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1218 ConstantString(statistic));
1219 return(QuantumScale*StringToDouble(statistic,(char **) NULL));
1220 }
1221
1222 static double
1223 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
1224 const ssize_t,const char *,const size_t,double *,ExceptionInfo *);
1225
FxGCD(MagickOffsetType alpha,MagickOffsetType beta)1226 static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
1227 {
1228 if (beta != 0)
1229 return(FxGCD(beta,alpha % beta));
1230 return(alpha);
1231 }
1232
FxSubexpression(const char * expression,ExceptionInfo * exception)1233 static inline const char *FxSubexpression(const char *expression,
1234 ExceptionInfo *exception)
1235 {
1236 const char
1237 *subexpression;
1238
1239 register ssize_t
1240 level;
1241
1242 level=0;
1243 subexpression=expression;
1244 while ((*subexpression != '\0') &&
1245 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1246 {
1247 if (strchr("(",(int) *subexpression) != (char *) NULL)
1248 level++;
1249 else
1250 if (strchr(")",(int) *subexpression) != (char *) NULL)
1251 level--;
1252 subexpression++;
1253 }
1254 if (*subexpression == '\0')
1255 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1256 "UnbalancedParenthesis","`%s'",expression);
1257 return(subexpression);
1258 }
1259
FxGetSymbol(FxInfo * fx_info,const PixelChannel channel,const ssize_t x,const ssize_t y,const char * expression,const size_t depth,ExceptionInfo * exception)1260 static double FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
1261 const ssize_t x,const ssize_t y,const char *expression,const size_t depth,
1262 ExceptionInfo *exception)
1263 {
1264 char
1265 *q,
1266 symbol[MagickPathExtent];
1267
1268 const char
1269 *p,
1270 *value;
1271
1272 Image
1273 *image;
1274
1275 MagickBooleanType
1276 status;
1277
1278 PixelInfo
1279 pixel;
1280
1281 double
1282 alpha,
1283 beta;
1284
1285 PointInfo
1286 point;
1287
1288 register ssize_t
1289 i;
1290
1291 size_t
1292 level;
1293
1294 p=expression;
1295 i=GetImageIndexInList(fx_info->images);
1296 level=0;
1297 point.x=(double) x;
1298 point.y=(double) y;
1299 if (isalpha((int) ((unsigned char) *(p+1))) == 0)
1300 {
1301 char
1302 *subexpression;
1303
1304 subexpression=AcquireString(expression);
1305 if (strchr("suv",(int) *p) != (char *) NULL)
1306 {
1307 switch (*p)
1308 {
1309 case 's':
1310 default:
1311 {
1312 i=GetImageIndexInList(fx_info->images);
1313 break;
1314 }
1315 case 'u': i=0; break;
1316 case 'v': i=1; break;
1317 }
1318 p++;
1319 if (*p == '[')
1320 {
1321 level++;
1322 q=subexpression;
1323 for (p++; *p != '\0'; )
1324 {
1325 if (*p == '[')
1326 level++;
1327 else
1328 if (*p == ']')
1329 {
1330 level--;
1331 if (level == 0)
1332 break;
1333 }
1334 *q++=(*p++);
1335 }
1336 *q='\0';
1337 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1338 depth,&beta,exception);
1339 i=(ssize_t) alpha;
1340 if (*p != '\0')
1341 p++;
1342 }
1343 if (*p == '.')
1344 p++;
1345 }
1346 if ((*p == 'p') && (isalpha((int) ((unsigned char) *(p+1))) == 0))
1347 {
1348 p++;
1349 if (*p == '{')
1350 {
1351 level++;
1352 q=subexpression;
1353 for (p++; *p != '\0'; )
1354 {
1355 if (*p == '{')
1356 level++;
1357 else
1358 if (*p == '}')
1359 {
1360 level--;
1361 if (level == 0)
1362 break;
1363 }
1364 *q++=(*p++);
1365 }
1366 *q='\0';
1367 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1368 depth,&beta,exception);
1369 point.x=alpha;
1370 point.y=beta;
1371 if (*p != '\0')
1372 p++;
1373 }
1374 else
1375 if (*p == '[')
1376 {
1377 level++;
1378 q=subexpression;
1379 for (p++; *p != '\0'; )
1380 {
1381 if (*p == '[')
1382 level++;
1383 else
1384 if (*p == ']')
1385 {
1386 level--;
1387 if (level == 0)
1388 break;
1389 }
1390 *q++=(*p++);
1391 }
1392 *q='\0';
1393 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1394 depth,&beta,exception);
1395 point.x+=alpha;
1396 point.y+=beta;
1397 if (*p != '\0')
1398 p++;
1399 }
1400 if (*p == '.')
1401 p++;
1402 }
1403 subexpression=DestroyString(subexpression);
1404 }
1405 image=GetImageFromList(fx_info->images,i);
1406 if (image == (Image *) NULL)
1407 {
1408 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1409 "NoSuchImage","`%s'",expression);
1410 return(0.0);
1411 }
1412 i=GetImageIndexInList(image);
1413 GetPixelInfo(image,&pixel);
1414 status=InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
1415 point.x,point.y,&pixel,exception);
1416 (void) status;
1417 if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1418 (LocaleCompare(p,"luma") != 0) && (LocaleCompare(p,"luminance") != 0) &&
1419 (LocaleCompare(p,"hue") != 0) && (LocaleCompare(p,"saturation") != 0) &&
1420 (LocaleCompare(p,"lightness") != 0))
1421 {
1422 char
1423 name[MagickPathExtent];
1424
1425 (void) CopyMagickString(name,p,MagickPathExtent);
1426 for (q=name+(strlen(name)-1); q > name; q--)
1427 {
1428 if (*q == ')')
1429 break;
1430 if (*q == '.')
1431 {
1432 *q='\0';
1433 break;
1434 }
1435 }
1436 if ((strlen(name) > 2) &&
1437 (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1438 {
1439 PixelInfo
1440 *color;
1441
1442 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1443 if (color != (PixelInfo *) NULL)
1444 {
1445 pixel=(*color);
1446 p+=strlen(name);
1447 }
1448 else
1449 {
1450 MagickBooleanType
1451 status;
1452
1453 status=QueryColorCompliance(name,AllCompliance,&pixel,
1454 fx_info->exception);
1455 if (status != MagickFalse)
1456 {
1457 (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1458 name),ClonePixelInfo(&pixel));
1459 p+=strlen(name);
1460 }
1461 }
1462 }
1463 }
1464 (void) CopyMagickString(symbol,p,MagickPathExtent);
1465 StripString(symbol);
1466 if (*symbol == '\0')
1467 {
1468 switch (channel)
1469 {
1470 case RedPixelChannel: return(QuantumScale*pixel.red);
1471 case GreenPixelChannel: return(QuantumScale*pixel.green);
1472 case BluePixelChannel: return(QuantumScale*pixel.blue);
1473 case BlackPixelChannel:
1474 {
1475 if (image->colorspace != CMYKColorspace)
1476 {
1477 (void) ThrowMagickException(exception,GetMagickModule(),
1478 ImageError,"ColorSeparatedImageRequired","`%s'",
1479 image->filename);
1480 return(0.0);
1481 }
1482 return(QuantumScale*pixel.black);
1483 }
1484 case AlphaPixelChannel:
1485 {
1486 if (pixel.alpha_trait == UndefinedPixelTrait)
1487 return(1.0);
1488 alpha=(double) (QuantumScale*pixel.alpha);
1489 return(alpha);
1490 }
1491 case CompositePixelChannel:
1492 {
1493 Quantum
1494 quantum_pixel[MaxPixelChannels];
1495
1496 SetPixelViaPixelInfo(image,&pixel,quantum_pixel);
1497 return(QuantumScale*GetPixelIntensity(image,quantum_pixel));
1498 }
1499 case IndexPixelChannel:
1500 return(0.0);
1501 default:
1502 break;
1503 }
1504 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1505 "UnableToParseExpression","`%s'",p);
1506 return(0.0);
1507 }
1508 switch (*symbol)
1509 {
1510 case 'A':
1511 case 'a':
1512 {
1513 if (LocaleCompare(symbol,"a") == 0)
1514 return((QuantumScale*pixel.alpha));
1515 break;
1516 }
1517 case 'B':
1518 case 'b':
1519 {
1520 if (LocaleCompare(symbol,"b") == 0)
1521 return(QuantumScale*pixel.blue);
1522 break;
1523 }
1524 case 'C':
1525 case 'c':
1526 {
1527 if (LocaleNCompare(symbol,"channel",7) == 0)
1528 {
1529 GeometryInfo
1530 channel_info;
1531
1532 MagickStatusType
1533 flags;
1534
1535 flags=ParseGeometry(symbol+7,&channel_info);
1536 if (image->colorspace == CMYKColorspace)
1537 switch (channel)
1538 {
1539 case CyanPixelChannel:
1540 {
1541 if ((flags & RhoValue) == 0)
1542 return(0.0);
1543 return(channel_info.rho);
1544 }
1545 case MagentaPixelChannel:
1546 {
1547 if ((flags & SigmaValue) == 0)
1548 return(0.0);
1549 return(channel_info.sigma);
1550 }
1551 case YellowPixelChannel:
1552 {
1553 if ((flags & XiValue) == 0)
1554 return(0.0);
1555 return(channel_info.xi);
1556 }
1557 case BlackPixelChannel:
1558 {
1559 if ((flags & PsiValue) == 0)
1560 return(0.0);
1561 return(channel_info.psi);
1562 }
1563 case AlphaPixelChannel:
1564 {
1565 if ((flags & ChiValue) == 0)
1566 return(0.0);
1567 return(channel_info.chi);
1568 }
1569 default:
1570 return(0.0);
1571 }
1572 switch (channel)
1573 {
1574 case RedPixelChannel:
1575 {
1576 if ((flags & RhoValue) == 0)
1577 return(0.0);
1578 return(channel_info.rho);
1579 }
1580 case GreenPixelChannel:
1581 {
1582 if ((flags & SigmaValue) == 0)
1583 return(0.0);
1584 return(channel_info.sigma);
1585 }
1586 case BluePixelChannel:
1587 {
1588 if ((flags & XiValue) == 0)
1589 return(0.0);
1590 return(channel_info.xi);
1591 }
1592 case BlackPixelChannel:
1593 {
1594 if ((flags & ChiValue) == 0)
1595 return(0.0);
1596 return(channel_info.chi);
1597 }
1598 case AlphaPixelChannel:
1599 {
1600 if ((flags & PsiValue) == 0)
1601 return(0.0);
1602 return(channel_info.psi);
1603 }
1604 default:
1605 return(0.0);
1606 }
1607 }
1608 if (LocaleCompare(symbol,"c") == 0)
1609 return(QuantumScale*pixel.red);
1610 break;
1611 }
1612 case 'D':
1613 case 'd':
1614 {
1615 if (LocaleNCompare(symbol,"depth",5) == 0)
1616 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1617 break;
1618 }
1619 case 'E':
1620 case 'e':
1621 {
1622 if (LocaleCompare(symbol,"extent") == 0)
1623 {
1624 if (image->extent != 0)
1625 return((double) image->extent);
1626 return((double) GetBlobSize(image));
1627 }
1628 break;
1629 }
1630 case 'G':
1631 case 'g':
1632 {
1633 if (LocaleCompare(symbol,"g") == 0)
1634 return(QuantumScale*pixel.green);
1635 break;
1636 }
1637 case 'K':
1638 case 'k':
1639 {
1640 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1641 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1642 if (LocaleCompare(symbol,"k") == 0)
1643 {
1644 if (image->colorspace != CMYKColorspace)
1645 {
1646 (void) ThrowMagickException(exception,GetMagickModule(),
1647 OptionError,"ColorSeparatedImageRequired","`%s'",
1648 image->filename);
1649 return(0.0);
1650 }
1651 return(QuantumScale*pixel.black);
1652 }
1653 break;
1654 }
1655 case 'H':
1656 case 'h':
1657 {
1658 if (LocaleCompare(symbol,"h") == 0)
1659 return((double) image->rows);
1660 if (LocaleCompare(symbol,"hue") == 0)
1661 {
1662 double
1663 hue,
1664 lightness,
1665 saturation;
1666
1667 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1668 &lightness);
1669 return(hue);
1670 }
1671 break;
1672 }
1673 case 'I':
1674 case 'i':
1675 {
1676 if ((LocaleCompare(symbol,"image.depth") == 0) ||
1677 (LocaleCompare(symbol,"image.minima") == 0) ||
1678 (LocaleCompare(symbol,"image.maxima") == 0) ||
1679 (LocaleCompare(symbol,"image.mean") == 0) ||
1680 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1681 (LocaleCompare(symbol,"image.skewness") == 0) ||
1682 (LocaleCompare(symbol,"image.standard_deviation") == 0))
1683 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1684 if (LocaleCompare(symbol,"image.resolution.x") == 0)
1685 return(image->resolution.x);
1686 if (LocaleCompare(symbol,"image.resolution.y") == 0)
1687 return(image->resolution.y);
1688 if (LocaleCompare(symbol,"intensity") == 0)
1689 {
1690 Quantum
1691 quantum_pixel[MaxPixelChannels];
1692
1693 SetPixelViaPixelInfo(image,&pixel,quantum_pixel);
1694 return(QuantumScale*GetPixelIntensity(image,quantum_pixel));
1695 }
1696 if (LocaleCompare(symbol,"i") == 0)
1697 return((double) x);
1698 break;
1699 }
1700 case 'J':
1701 case 'j':
1702 {
1703 if (LocaleCompare(symbol,"j") == 0)
1704 return((double) y);
1705 break;
1706 }
1707 case 'L':
1708 case 'l':
1709 {
1710 if (LocaleCompare(symbol,"lightness") == 0)
1711 {
1712 double
1713 hue,
1714 lightness,
1715 saturation;
1716
1717 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1718 &lightness);
1719 return(lightness);
1720 }
1721 if (LocaleCompare(symbol,"luma") == 0)
1722 {
1723 double
1724 luma;
1725
1726 luma=0.212656*pixel.red+0.715158*pixel.green+0.072186*pixel.blue;
1727 return(QuantumScale*luma);
1728 }
1729 if (LocaleCompare(symbol,"luminance") == 0)
1730 {
1731 double
1732 luminence;
1733
1734 luminence=0.212656*pixel.red+0.715158*pixel.green+0.072186*pixel.blue;
1735 return(QuantumScale*luminence);
1736 }
1737 break;
1738 }
1739 case 'M':
1740 case 'm':
1741 {
1742 if (LocaleNCompare(symbol,"maxima",6) == 0)
1743 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1744 if (LocaleNCompare(symbol,"mean",4) == 0)
1745 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1746 if (LocaleNCompare(symbol,"minima",6) == 0)
1747 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1748 if (LocaleCompare(symbol,"m") == 0)
1749 return(QuantumScale*pixel.green);
1750 break;
1751 }
1752 case 'N':
1753 case 'n':
1754 {
1755 if (LocaleCompare(symbol,"n") == 0)
1756 return((double) GetImageListLength(fx_info->images));
1757 break;
1758 }
1759 case 'O':
1760 case 'o':
1761 {
1762 if (LocaleCompare(symbol,"o") == 0)
1763 return(QuantumScale*pixel.alpha);
1764 break;
1765 }
1766 case 'P':
1767 case 'p':
1768 {
1769 if (LocaleCompare(symbol,"page.height") == 0)
1770 return((double) image->page.height);
1771 if (LocaleCompare(symbol,"page.width") == 0)
1772 return((double) image->page.width);
1773 if (LocaleCompare(symbol,"page.x") == 0)
1774 return((double) image->page.x);
1775 if (LocaleCompare(symbol,"page.y") == 0)
1776 return((double) image->page.y);
1777 if (LocaleCompare(symbol,"printsize.x") == 0)
1778 return(PerceptibleReciprocal(image->resolution.x)*image->columns);
1779 if (LocaleCompare(symbol,"printsize.y") == 0)
1780 return(PerceptibleReciprocal(image->resolution.y)*image->rows);
1781 break;
1782 }
1783 case 'Q':
1784 case 'q':
1785 {
1786 if (LocaleCompare(symbol,"quality") == 0)
1787 return((double) image->quality);
1788 break;
1789 }
1790 case 'R':
1791 case 'r':
1792 {
1793 if (LocaleCompare(symbol,"resolution.x") == 0)
1794 return(image->resolution.x);
1795 if (LocaleCompare(symbol,"resolution.y") == 0)
1796 return(image->resolution.y);
1797 if (LocaleCompare(symbol,"r") == 0)
1798 return(QuantumScale*pixel.red);
1799 break;
1800 }
1801 case 'S':
1802 case 's':
1803 {
1804 if (LocaleCompare(symbol,"saturation") == 0)
1805 {
1806 double
1807 hue,
1808 lightness,
1809 saturation;
1810
1811 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1812 &lightness);
1813 return(saturation);
1814 }
1815 if (LocaleNCompare(symbol,"skewness",8) == 0)
1816 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1817 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1818 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1819 break;
1820 }
1821 case 'T':
1822 case 't':
1823 {
1824 if (LocaleCompare(symbol,"t") == 0)
1825 return((double) GetImageIndexInList(fx_info->images));
1826 break;
1827 }
1828 case 'W':
1829 case 'w':
1830 {
1831 if (LocaleCompare(symbol,"w") == 0)
1832 return((double) image->columns);
1833 break;
1834 }
1835 case 'Y':
1836 case 'y':
1837 {
1838 if (LocaleCompare(symbol,"y") == 0)
1839 return(QuantumScale*pixel.blue);
1840 break;
1841 }
1842 case 'Z':
1843 case 'z':
1844 {
1845 if (LocaleCompare(symbol,"z") == 0)
1846 return((double) GetImageDepth(image,fx_info->exception));
1847 break;
1848 }
1849 default:
1850 break;
1851 }
1852 value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1853 if (value != (const char *) NULL)
1854 return(StringToDouble(value,(char **) NULL));
1855 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1856 "UnableToParseExpression","`%s'",symbol);
1857 return(0.0);
1858 }
1859
FxOperatorPrecedence(const char * expression,ExceptionInfo * exception)1860 static const char *FxOperatorPrecedence(const char *expression,
1861 ExceptionInfo *exception)
1862 {
1863 typedef enum
1864 {
1865 UndefinedPrecedence,
1866 NullPrecedence,
1867 BitwiseComplementPrecedence,
1868 ExponentPrecedence,
1869 ExponentialNotationPrecedence,
1870 MultiplyPrecedence,
1871 AdditionPrecedence,
1872 ShiftPrecedence,
1873 RelationalPrecedence,
1874 EquivalencyPrecedence,
1875 BitwiseAndPrecedence,
1876 BitwiseOrPrecedence,
1877 LogicalAndPrecedence,
1878 LogicalOrPrecedence,
1879 TernaryPrecedence,
1880 AssignmentPrecedence,
1881 CommaPrecedence,
1882 SeparatorPrecedence
1883 } FxPrecedence;
1884
1885 FxPrecedence
1886 precedence,
1887 target;
1888
1889 register const char
1890 *subexpression;
1891
1892 register int
1893 c;
1894
1895 size_t
1896 level;
1897
1898 c=(-1);
1899 level=0;
1900 subexpression=(const char *) NULL;
1901 target=NullPrecedence;
1902 while ((c != '\0') && (*expression != '\0'))
1903 {
1904 precedence=UndefinedPrecedence;
1905 if ((isspace((int) ((unsigned char) *expression)) != 0) || (c == (int) '@'))
1906 {
1907 expression++;
1908 continue;
1909 }
1910 switch (*expression)
1911 {
1912 case 'A':
1913 case 'a':
1914 {
1915 #if defined(MAGICKCORE_HAVE_ACOSH)
1916 if (LocaleNCompare(expression,"acosh",5) == 0)
1917 {
1918 expression+=5;
1919 break;
1920 }
1921 #endif
1922 #if defined(MAGICKCORE_HAVE_ASINH)
1923 if (LocaleNCompare(expression,"asinh",5) == 0)
1924 {
1925 expression+=5;
1926 break;
1927 }
1928 #endif
1929 #if defined(MAGICKCORE_HAVE_ATANH)
1930 if (LocaleNCompare(expression,"atanh",5) == 0)
1931 {
1932 expression+=5;
1933 break;
1934 }
1935 #endif
1936 if (LocaleNCompare(expression,"atan2",5) == 0)
1937 {
1938 expression+=5;
1939 break;
1940 }
1941 break;
1942 }
1943 case 'E':
1944 case 'e':
1945 {
1946 if ((isdigit(c) != 0) &&
1947 ((LocaleNCompare(expression,"E+",2) == 0) ||
1948 (LocaleNCompare(expression,"E-",2) == 0)))
1949 {
1950 expression+=2; /* scientific notation */
1951 break;
1952 }
1953 }
1954 case 'J':
1955 case 'j':
1956 {
1957 if ((LocaleNCompare(expression,"j0",2) == 0) ||
1958 (LocaleNCompare(expression,"j1",2) == 0))
1959 {
1960 expression+=2;
1961 break;
1962 }
1963 break;
1964 }
1965 case '#':
1966 {
1967 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1968 expression++;
1969 break;
1970 }
1971 default:
1972 break;
1973 }
1974 if ((c == (int) '{') || (c == (int) '['))
1975 level++;
1976 else
1977 if ((c == (int) '}') || (c == (int) ']'))
1978 level--;
1979 if (level == 0)
1980 switch ((unsigned char) *expression)
1981 {
1982 case '~':
1983 case '!':
1984 {
1985 precedence=BitwiseComplementPrecedence;
1986 break;
1987 }
1988 case '^':
1989 case '@':
1990 {
1991 precedence=ExponentPrecedence;
1992 break;
1993 }
1994 default:
1995 {
1996 if (((c != 0) && ((isdigit(c) != 0) ||
1997 (strchr(")",c) != (char *) NULL))) &&
1998 (((islower((int) ((unsigned char) *expression)) != 0) ||
1999 (strchr("(",(int) ((unsigned char) *expression)) != (char *) NULL)) ||
2000 ((isdigit(c) == 0) &&
2001 (isdigit((int) ((unsigned char) *expression)) != 0))) &&
2002 (strchr("xy",(int) ((unsigned char) *expression)) == (char *) NULL))
2003 precedence=MultiplyPrecedence;
2004 break;
2005 }
2006 case '*':
2007 case '/':
2008 case '%':
2009 {
2010 precedence=MultiplyPrecedence;
2011 break;
2012 }
2013 case '+':
2014 case '-':
2015 {
2016 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
2017 (isalpha(c) != 0))
2018 precedence=AdditionPrecedence;
2019 break;
2020 }
2021 case LeftShiftOperator:
2022 case RightShiftOperator:
2023 {
2024 precedence=ShiftPrecedence;
2025 break;
2026 }
2027 case '<':
2028 case LessThanEqualOperator:
2029 case GreaterThanEqualOperator:
2030 case '>':
2031 {
2032 precedence=RelationalPrecedence;
2033 break;
2034 }
2035 case EqualOperator:
2036 case NotEqualOperator:
2037 {
2038 precedence=EquivalencyPrecedence;
2039 break;
2040 }
2041 case '&':
2042 {
2043 precedence=BitwiseAndPrecedence;
2044 break;
2045 }
2046 case '|':
2047 {
2048 precedence=BitwiseOrPrecedence;
2049 break;
2050 }
2051 case LogicalAndOperator:
2052 {
2053 precedence=LogicalAndPrecedence;
2054 break;
2055 }
2056 case LogicalOrOperator:
2057 {
2058 precedence=LogicalOrPrecedence;
2059 break;
2060 }
2061 case ExponentialNotation:
2062 {
2063 precedence=ExponentialNotationPrecedence;
2064 break;
2065 }
2066 case ':':
2067 case '?':
2068 {
2069 precedence=TernaryPrecedence;
2070 break;
2071 }
2072 case '=':
2073 {
2074 precedence=AssignmentPrecedence;
2075 break;
2076 }
2077 case ',':
2078 {
2079 precedence=CommaPrecedence;
2080 break;
2081 }
2082 case ';':
2083 {
2084 precedence=SeparatorPrecedence;
2085 break;
2086 }
2087 }
2088 if ((precedence == BitwiseComplementPrecedence) ||
2089 (precedence == TernaryPrecedence) ||
2090 (precedence == AssignmentPrecedence))
2091 {
2092 if (precedence > target)
2093 {
2094 /*
2095 Right-to-left associativity.
2096 */
2097 target=precedence;
2098 subexpression=expression;
2099 }
2100 }
2101 else
2102 if (precedence >= target)
2103 {
2104 /*
2105 Left-to-right associativity.
2106 */
2107 target=precedence;
2108 subexpression=expression;
2109 }
2110 if (strchr("(",(int) *expression) != (char *) NULL)
2111 expression=FxSubexpression(expression,exception);
2112 c=(int) (*expression++);
2113 }
2114 return(subexpression);
2115 }
2116
FxEvaluateSubexpression(FxInfo * fx_info,const PixelChannel channel,const ssize_t x,const ssize_t y,const char * expression,const size_t depth,double * beta,ExceptionInfo * exception)2117 static double FxEvaluateSubexpression(FxInfo *fx_info,
2118 const PixelChannel channel,const ssize_t x,const ssize_t y,
2119 const char *expression,const size_t depth,double *beta,
2120 ExceptionInfo *exception)
2121 {
2122 #define FxMaxParenthesisDepth 58
2123 #define FxMaxSubexpressionDepth 200
2124 #define FxReturn(value) \
2125 { \
2126 subexpression=DestroyString(subexpression); \
2127 return(value); \
2128 }
2129
2130 char
2131 *q,
2132 *subexpression;
2133
2134 double
2135 alpha,
2136 gamma;
2137
2138 register const char
2139 *p;
2140
2141 *beta=0.0;
2142 subexpression=AcquireString(expression);
2143 *subexpression='\0';
2144 if (depth > FxMaxSubexpressionDepth)
2145 {
2146 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2147 "UnableToParseExpression","`%s'",expression);
2148 FxReturn(0.0);
2149 }
2150 if (exception->severity >= ErrorException)
2151 FxReturn(0.0);
2152 while (isspace((int) ((unsigned char) *expression)) != 0)
2153 expression++;
2154 if (*expression == '\0')
2155 FxReturn(0.0);
2156 p=FxOperatorPrecedence(expression,exception);
2157 if (p != (const char *) NULL)
2158 {
2159 (void) CopyMagickString(subexpression,expression,(size_t)
2160 (p-expression+1));
2161 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,depth+1,
2162 beta,exception);
2163 switch ((unsigned char) *p)
2164 {
2165 case '~':
2166 {
2167 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2168 exception);
2169 *beta=(double) (~(size_t) *beta);
2170 FxReturn(*beta);
2171 }
2172 case '!':
2173 {
2174 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2175 exception);
2176 FxReturn(*beta == 0.0 ? 1.0 : 0.0);
2177 }
2178 case '^':
2179 {
2180 *beta=pow(alpha,FxEvaluateSubexpression(fx_info,channel,x,y,++p,
2181 depth+1,beta,exception));
2182 FxReturn(*beta);
2183 }
2184 case '*':
2185 case ExponentialNotation:
2186 {
2187 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2188 exception);
2189 FxReturn(alpha*(*beta));
2190 }
2191 case '/':
2192 {
2193 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2194 exception);
2195 if (*beta == 0.0)
2196 {
2197 (void) ThrowMagickException(exception,GetMagickModule(),
2198 OptionError,"DivideByZero","`%s'",expression);
2199 FxReturn(0.0);
2200 }
2201 FxReturn(alpha/(*beta));
2202 }
2203 case '%':
2204 {
2205 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2206 exception);
2207 *beta=fabs(floor((*beta)+0.5));
2208 if (*beta == 0.0)
2209 {
2210 (void) ThrowMagickException(exception,GetMagickModule(),
2211 OptionError,"DivideByZero","`%s'",expression);
2212 FxReturn(0.0);
2213 }
2214 FxReturn(fmod(alpha,*beta));
2215 }
2216 case '+':
2217 {
2218 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2219 exception);
2220 FxReturn(alpha+(*beta));
2221 }
2222 case '-':
2223 {
2224 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2225 exception);
2226 FxReturn(alpha-(*beta));
2227 }
2228 case LeftShiftOperator:
2229 {
2230 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2231 exception);
2232 if ((size_t) (gamma+0.5) >= (8*sizeof(size_t)))
2233 {
2234 (void) ThrowMagickException(exception,GetMagickModule(),
2235 OptionError,"ShiftCountOverflow","`%s'",subexpression);
2236 FxReturn(0.0);
2237 }
2238 *beta=(double) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
2239 FxReturn(*beta);
2240 }
2241 case RightShiftOperator:
2242 {
2243 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2244 exception);
2245 if ((size_t) (gamma+0.5) >= (8*sizeof(size_t)))
2246 {
2247 (void) ThrowMagickException(exception,GetMagickModule(),
2248 OptionError,"ShiftCountOverflow","`%s'",subexpression);
2249 FxReturn(0.0);
2250 }
2251 *beta=(double) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
2252 FxReturn(*beta);
2253 }
2254 case '<':
2255 {
2256 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2257 exception);
2258 FxReturn(alpha < *beta ? 1.0 : 0.0);
2259 }
2260 case LessThanEqualOperator:
2261 {
2262 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2263 exception);
2264 FxReturn(alpha <= *beta ? 1.0 : 0.0);
2265 }
2266 case '>':
2267 {
2268 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2269 exception);
2270 FxReturn(alpha > *beta ? 1.0 : 0.0);
2271 }
2272 case GreaterThanEqualOperator:
2273 {
2274 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2275 exception);
2276 FxReturn(alpha >= *beta ? 1.0 : 0.0);
2277 }
2278 case EqualOperator:
2279 {
2280 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2281 exception);
2282 FxReturn(fabs(alpha-(*beta)) < MagickEpsilon ? 1.0 : 0.0);
2283 }
2284 case NotEqualOperator:
2285 {
2286 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2287 exception);
2288 FxReturn(fabs(alpha-(*beta)) >= MagickEpsilon ? 1.0 : 0.0);
2289 }
2290 case '&':
2291 {
2292 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2293 exception);
2294 *beta=(double) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
2295 FxReturn(*beta);
2296 }
2297 case '|':
2298 {
2299 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2300 exception);
2301 *beta=(double) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
2302 FxReturn(*beta);
2303 }
2304 case LogicalAndOperator:
2305 {
2306 p++;
2307 if (alpha <= 0.0)
2308 {
2309 *beta=0.0;
2310 FxReturn(*beta);
2311 }
2312 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
2313 exception);
2314 *beta=(gamma > 0.0) ? 1.0 : 0.0;
2315 FxReturn(*beta);
2316 }
2317 case LogicalOrOperator:
2318 {
2319 p++;
2320 if (alpha > 0.0)
2321 {
2322 *beta=1.0;
2323 FxReturn(*beta);
2324 }
2325 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
2326 exception);
2327 *beta=(gamma > 0.0) ? 1.0 : 0.0;
2328 FxReturn(*beta);
2329 }
2330 case '?':
2331 {
2332 (void) CopyMagickString(subexpression,++p,MagickPathExtent);
2333 q=subexpression;
2334 p=StringToken(":",&q);
2335 if (q == (char *) NULL)
2336 {
2337 (void) ThrowMagickException(exception,GetMagickModule(),
2338 OptionError,"UnableToParseExpression","`%s'",subexpression);
2339 FxReturn(0.0);
2340 }
2341 if (fabs(alpha) >= MagickEpsilon)
2342 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
2343 exception);
2344 else
2345 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,depth+1,beta,
2346 exception);
2347 FxReturn(gamma);
2348 }
2349 case '=':
2350 {
2351 char
2352 numeric[MagickPathExtent];
2353
2354 q=subexpression;
2355 while (isalpha((int) ((unsigned char) *q)) != 0)
2356 q++;
2357 if (*q != '\0')
2358 {
2359 (void) ThrowMagickException(exception,GetMagickModule(),
2360 OptionError,"UnableToParseExpression","`%s'",subexpression);
2361 FxReturn(0.0);
2362 }
2363 ClearMagickException(exception);
2364 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2365 exception);
2366 (void) FormatLocaleString(numeric,MagickPathExtent,"%.20g",*beta);
2367 (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2368 (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2369 subexpression),ConstantString(numeric));
2370 FxReturn(*beta);
2371 }
2372 case ',':
2373 {
2374 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2375 exception);
2376 FxReturn(alpha);
2377 }
2378 case ';':
2379 {
2380 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2381 exception);
2382 FxReturn(*beta);
2383 }
2384 default:
2385 {
2386 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,
2387 beta,exception);
2388 FxReturn(gamma);
2389 }
2390 }
2391 }
2392 if (strchr("(",(int) *expression) != (char *) NULL)
2393 {
2394 if (depth >= FxMaxParenthesisDepth)
2395 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2396 "ParenthesisNestedTooDeeply","`%s'",expression);
2397 (void) CopyMagickString(subexpression,expression+1,MagickPathExtent);
2398 if (strlen(subexpression) != 0)
2399 subexpression[strlen(subexpression)-1]='\0';
2400 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,depth+1,
2401 beta,exception);
2402 FxReturn(gamma);
2403 }
2404 switch (*expression)
2405 {
2406 case '+':
2407 {
2408 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
2409 beta,exception);
2410 FxReturn(1.0*gamma);
2411 }
2412 case '-':
2413 {
2414 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
2415 beta,exception);
2416 FxReturn(-1.0*gamma);
2417 }
2418 case '~':
2419 {
2420 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
2421 beta,exception);
2422 FxReturn((double) (~(size_t) (gamma+0.5)));
2423 }
2424 case 'A':
2425 case 'a':
2426 {
2427 if (LocaleNCompare(expression,"abs",3) == 0)
2428 {
2429 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2430 depth+1,beta,exception);
2431 FxReturn(fabs(alpha));
2432 }
2433 #if defined(MAGICKCORE_HAVE_ACOSH)
2434 if (LocaleNCompare(expression,"acosh",5) == 0)
2435 {
2436 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2437 depth+1,beta,exception);
2438 FxReturn(acosh(alpha));
2439 }
2440 #endif
2441 if (LocaleNCompare(expression,"acos",4) == 0)
2442 {
2443 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2444 depth+1,beta,exception);
2445 FxReturn(acos(alpha));
2446 }
2447 #if defined(MAGICKCORE_HAVE_J1)
2448 if (LocaleNCompare(expression,"airy",4) == 0)
2449 {
2450 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2451 depth+1,beta,exception);
2452 if (alpha == 0.0)
2453 FxReturn(1.0);
2454 gamma=2.0*j1((MagickPI*alpha))/(MagickPI*alpha);
2455 FxReturn(gamma*gamma);
2456 }
2457 #endif
2458 #if defined(MAGICKCORE_HAVE_ASINH)
2459 if (LocaleNCompare(expression,"asinh",5) == 0)
2460 {
2461 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2462 depth+1,beta,exception);
2463 FxReturn(asinh(alpha));
2464 }
2465 #endif
2466 if (LocaleNCompare(expression,"asin",4) == 0)
2467 {
2468 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2469 depth+1,beta,exception);
2470 FxReturn(asin(alpha));
2471 }
2472 if (LocaleNCompare(expression,"alt",3) == 0)
2473 {
2474 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2475 depth+1,beta,exception);
2476 FxReturn(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
2477 }
2478 if (LocaleNCompare(expression,"atan2",5) == 0)
2479 {
2480 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2481 depth+1,beta,exception);
2482 FxReturn(atan2(alpha,*beta));
2483 }
2484 #if defined(MAGICKCORE_HAVE_ATANH)
2485 if (LocaleNCompare(expression,"atanh",5) == 0)
2486 {
2487 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2488 depth+1,beta,exception);
2489 FxReturn(atanh(alpha));
2490 }
2491 #endif
2492 if (LocaleNCompare(expression,"atan",4) == 0)
2493 {
2494 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2495 depth+1,beta,exception);
2496 FxReturn(atan(alpha));
2497 }
2498 if (LocaleCompare(expression,"a") == 0)
2499 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2500 break;
2501 }
2502 case 'B':
2503 case 'b':
2504 {
2505 if (LocaleCompare(expression,"b") == 0)
2506 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2507 break;
2508 }
2509 case 'C':
2510 case 'c':
2511 {
2512 if (LocaleNCompare(expression,"ceil",4) == 0)
2513 {
2514 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2515 depth+1,beta,exception);
2516 FxReturn(ceil(alpha));
2517 }
2518 if (LocaleNCompare(expression,"clamp",5) == 0)
2519 {
2520 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2521 depth+1,beta,exception);
2522 if (alpha < 0.0)
2523 FxReturn(0.0);
2524 if (alpha > 1.0)
2525 FxReturn(1.0);
2526 FxReturn(alpha);
2527 }
2528 if (LocaleNCompare(expression,"cosh",4) == 0)
2529 {
2530 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2531 depth+1,beta,exception);
2532 FxReturn(cosh(alpha));
2533 }
2534 if (LocaleNCompare(expression,"cos",3) == 0)
2535 {
2536 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2537 depth+1,beta,exception);
2538 FxReturn(cos(alpha));
2539 }
2540 if (LocaleCompare(expression,"c") == 0)
2541 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2542 break;
2543 }
2544 case 'D':
2545 case 'd':
2546 {
2547 if (LocaleNCompare(expression,"debug",5) == 0)
2548 {
2549 const char
2550 *type;
2551
2552 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2553 depth+1,beta,exception);
2554 if (fx_info->images->colorspace == CMYKColorspace)
2555 switch (channel)
2556 {
2557 case CyanPixelChannel: type="cyan"; break;
2558 case MagentaPixelChannel: type="magenta"; break;
2559 case YellowPixelChannel: type="yellow"; break;
2560 case AlphaPixelChannel: type="opacity"; break;
2561 case BlackPixelChannel: type="black"; break;
2562 default: type="unknown"; break;
2563 }
2564 else
2565 switch (channel)
2566 {
2567 case RedPixelChannel: type="red"; break;
2568 case GreenPixelChannel: type="green"; break;
2569 case BluePixelChannel: type="blue"; break;
2570 case AlphaPixelChannel: type="opacity"; break;
2571 default: type="unknown"; break;
2572 }
2573 *subexpression='\0';
2574 if (strlen(expression) > 6)
2575 (void) CopyMagickString(subexpression,expression+6,
2576 MagickPathExtent);
2577 if (strlen(subexpression) > 1)
2578 subexpression[strlen(subexpression)-1]='\0';
2579 if (fx_info->file != (FILE *) NULL)
2580 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2581 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2582 subexpression,GetMagickPrecision(),alpha);
2583 FxReturn(0.0);
2584 }
2585 if (LocaleNCompare(expression,"drc",3) == 0)
2586 {
2587 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2588 depth+1,beta,exception);
2589 FxReturn((alpha/(*beta*(alpha-1.0)+1.0)));
2590 }
2591 break;
2592 }
2593 case 'E':
2594 case 'e':
2595 {
2596 if (LocaleCompare(expression,"epsilon") == 0)
2597 FxReturn(MagickEpsilon);
2598 #if defined(MAGICKCORE_HAVE_ERF)
2599 if (LocaleNCompare(expression,"erf",3) == 0)
2600 {
2601 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2602 depth+1,beta,exception);
2603 FxReturn(erf(alpha));
2604 }
2605 #endif
2606 if (LocaleNCompare(expression,"exp",3) == 0)
2607 {
2608 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2609 depth+1,beta,exception);
2610 FxReturn(exp(alpha));
2611 }
2612 if (LocaleCompare(expression,"e") == 0)
2613 FxReturn(2.7182818284590452354);
2614 break;
2615 }
2616 case 'F':
2617 case 'f':
2618 {
2619 if (LocaleNCompare(expression,"floor",5) == 0)
2620 {
2621 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2622 depth+1,beta,exception);
2623 FxReturn(floor(alpha));
2624 }
2625 break;
2626 }
2627 case 'G':
2628 case 'g':
2629 {
2630 if (LocaleNCompare(expression,"gauss",5) == 0)
2631 {
2632 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2633 depth+1,beta,exception);
2634 gamma=exp((-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2635 FxReturn(gamma);
2636 }
2637 if (LocaleNCompare(expression,"gcd",3) == 0)
2638 {
2639 MagickOffsetType
2640 gcd;
2641
2642 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2643 depth+1,beta,exception);
2644 gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2645 0.5));
2646 FxReturn((double) gcd);
2647 }
2648 if (LocaleCompare(expression,"g") == 0)
2649 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2650 break;
2651 }
2652 case 'H':
2653 case 'h':
2654 {
2655 if (LocaleCompare(expression,"h") == 0)
2656 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2657 if (LocaleCompare(expression,"hue") == 0)
2658 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2659 if (LocaleNCompare(expression,"hypot",5) == 0)
2660 {
2661 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2662 depth+1,beta,exception);
2663 FxReturn(hypot(alpha,*beta));
2664 }
2665 break;
2666 }
2667 case 'K':
2668 case 'k':
2669 {
2670 if (LocaleCompare(expression,"k") == 0)
2671 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2672 break;
2673 }
2674 case 'I':
2675 case 'i':
2676 {
2677 if (LocaleCompare(expression,"intensity") == 0)
2678 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2679 if (LocaleNCompare(expression,"int",3) == 0)
2680 {
2681 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2682 depth+1,beta,exception);
2683 FxReturn(floor(alpha));
2684 }
2685 if (LocaleNCompare(expression,"isnan",5) == 0)
2686 {
2687 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2688 depth+1,beta,exception);
2689 FxReturn((double) !!IsNaN(alpha));
2690 }
2691 if (LocaleCompare(expression,"i") == 0)
2692 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2693 break;
2694 }
2695 case 'J':
2696 case 'j':
2697 {
2698 if (LocaleCompare(expression,"j") == 0)
2699 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2700 #if defined(MAGICKCORE_HAVE_J0)
2701 if (LocaleNCompare(expression,"j0",2) == 0)
2702 {
2703 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2704 depth+1,beta,exception);
2705 FxReturn(j0(alpha));
2706 }
2707 #endif
2708 #if defined(MAGICKCORE_HAVE_J1)
2709 if (LocaleNCompare(expression,"j1",2) == 0)
2710 {
2711 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2712 depth+1,beta,exception);
2713 FxReturn(j1(alpha));
2714 }
2715 #endif
2716 #if defined(MAGICKCORE_HAVE_J1)
2717 if (LocaleNCompare(expression,"jinc",4) == 0)
2718 {
2719 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2720 depth+1,beta,exception);
2721 if (alpha == 0.0)
2722 FxReturn(1.0);
2723 gamma=(2.0*j1((MagickPI*alpha))/(MagickPI*alpha));
2724 FxReturn(gamma);
2725 }
2726 #endif
2727 break;
2728 }
2729 case 'L':
2730 case 'l':
2731 {
2732 if (LocaleNCompare(expression,"ln",2) == 0)
2733 {
2734 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2735 depth+1,beta,exception);
2736 FxReturn(log(alpha));
2737 }
2738 if (LocaleNCompare(expression,"logtwo",6) == 0)
2739 {
2740 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,
2741 depth+1,beta,exception);
2742 FxReturn(log10(alpha)/log10(2.0));
2743 }
2744 if (LocaleNCompare(expression,"log",3) == 0)
2745 {
2746 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2747 depth+1,beta,exception);
2748 FxReturn(log10(alpha));
2749 }
2750 if (LocaleCompare(expression,"lightness") == 0)
2751 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2752 break;
2753 }
2754 case 'M':
2755 case 'm':
2756 {
2757 if (LocaleCompare(expression,"MaxRGB") == 0)
2758 FxReturn(QuantumRange);
2759 if (LocaleNCompare(expression,"maxima",6) == 0)
2760 break;
2761 if (LocaleNCompare(expression,"max",3) == 0)
2762 {
2763 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2764 depth+1,beta,exception);
2765 FxReturn(alpha > *beta ? alpha : *beta);
2766 }
2767 if (LocaleNCompare(expression,"minima",6) == 0)
2768 break;
2769 if (LocaleNCompare(expression,"min",3) == 0)
2770 {
2771 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2772 depth+1,beta,exception);
2773 FxReturn(alpha < *beta ? alpha : *beta);
2774 }
2775 if (LocaleNCompare(expression,"mod",3) == 0)
2776 {
2777 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2778 depth+1,beta,exception);
2779 gamma=alpha-floor((alpha*PerceptibleReciprocal(*beta)))*(*beta);
2780 FxReturn(gamma);
2781 }
2782 if (LocaleCompare(expression,"m") == 0)
2783 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2784 break;
2785 }
2786 case 'N':
2787 case 'n':
2788 {
2789 if (LocaleNCompare(expression,"not",3) == 0)
2790 {
2791 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2792 depth+1,beta,exception);
2793 FxReturn((double) (alpha < MagickEpsilon));
2794 }
2795 if (LocaleCompare(expression,"n") == 0)
2796 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2797 break;
2798 }
2799 case 'O':
2800 case 'o':
2801 {
2802 if (LocaleCompare(expression,"Opaque") == 0)
2803 FxReturn(1.0);
2804 if (LocaleCompare(expression,"o") == 0)
2805 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2806 break;
2807 }
2808 case 'P':
2809 case 'p':
2810 {
2811 if (LocaleCompare(expression,"phi") == 0)
2812 FxReturn(MagickPHI);
2813 if (LocaleCompare(expression,"pi") == 0)
2814 FxReturn(MagickPI);
2815 if (LocaleNCompare(expression,"pow",3) == 0)
2816 {
2817 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2818 depth+1,beta,exception);
2819 FxReturn(pow(alpha,*beta));
2820 }
2821 if (LocaleCompare(expression,"p") == 0)
2822 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2823 break;
2824 }
2825 case 'Q':
2826 case 'q':
2827 {
2828 if (LocaleCompare(expression,"QuantumRange") == 0)
2829 FxReturn(QuantumRange);
2830 if (LocaleCompare(expression,"QuantumScale") == 0)
2831 FxReturn(QuantumScale);
2832 break;
2833 }
2834 case 'R':
2835 case 'r':
2836 {
2837 if (LocaleNCompare(expression,"rand",4) == 0)
2838 {
2839 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2840 #pragma omp critical (MagickCore_FxEvaluateSubexpression)
2841 #endif
2842 alpha=GetPseudoRandomValue(fx_info->random_info);
2843 FxReturn(alpha);
2844 }
2845 if (LocaleNCompare(expression,"round",5) == 0)
2846 {
2847 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2848 depth+1,beta,exception);
2849 FxReturn(floor(alpha+0.5));
2850 }
2851 if (LocaleCompare(expression,"r") == 0)
2852 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2853 break;
2854 }
2855 case 'S':
2856 case 's':
2857 {
2858 if (LocaleCompare(expression,"saturation") == 0)
2859 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2860 if (LocaleNCompare(expression,"sign",4) == 0)
2861 {
2862 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2863 depth+1,beta,exception);
2864 FxReturn(alpha < 0.0 ? -1.0 : 1.0);
2865 }
2866 if (LocaleNCompare(expression,"sinc",4) == 0)
2867 {
2868 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2869 depth+1,beta,exception);
2870 if (alpha == 0)
2871 FxReturn(1.0);
2872 gamma=sin((MagickPI*alpha))/(MagickPI*alpha);
2873 FxReturn(gamma);
2874 }
2875 if (LocaleNCompare(expression,"sinh",4) == 0)
2876 {
2877 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2878 depth+1,beta,exception);
2879 FxReturn(sinh(alpha));
2880 }
2881 if (LocaleNCompare(expression,"sin",3) == 0)
2882 {
2883 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2884 depth+1,beta,exception);
2885 FxReturn(sin(alpha));
2886 }
2887 if (LocaleNCompare(expression,"sqrt",4) == 0)
2888 {
2889 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2890 depth+1,beta,exception);
2891 FxReturn(sqrt(alpha));
2892 }
2893 if (LocaleNCompare(expression,"squish",6) == 0)
2894 {
2895 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,
2896 depth+1,beta,exception);
2897 FxReturn((1.0/(1.0+exp(-alpha))));
2898 }
2899 if (LocaleCompare(expression,"s") == 0)
2900 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2901 break;
2902 }
2903 case 'T':
2904 case 't':
2905 {
2906 if (LocaleNCompare(expression,"tanh",4) == 0)
2907 {
2908 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2909 depth+1,beta,exception);
2910 FxReturn(tanh(alpha));
2911 }
2912 if (LocaleNCompare(expression,"tan",3) == 0)
2913 {
2914 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2915 depth+1,beta,exception);
2916 FxReturn(tan(alpha));
2917 }
2918 if (LocaleCompare(expression,"Transparent") == 0)
2919 FxReturn(0.0);
2920 if (LocaleNCompare(expression,"trunc",5) == 0)
2921 {
2922 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2923 depth+1,beta,exception);
2924 if (alpha >= 0.0)
2925 FxReturn(floor(alpha));
2926 FxReturn(ceil(alpha));
2927 }
2928 if (LocaleCompare(expression,"t") == 0)
2929 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2930 break;
2931 }
2932 case 'U':
2933 case 'u':
2934 {
2935 if (LocaleCompare(expression,"u") == 0)
2936 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2937 break;
2938 }
2939 case 'V':
2940 case 'v':
2941 {
2942 if (LocaleCompare(expression,"v") == 0)
2943 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2944 break;
2945 }
2946 case 'W':
2947 case 'w':
2948 {
2949 if (LocaleNCompare(expression,"while",5) == 0)
2950 {
2951 do
2952 {
2953 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2954 depth+1,beta,exception);
2955 } while (fabs(alpha) >= MagickEpsilon);
2956 FxReturn(*beta);
2957 }
2958 if (LocaleCompare(expression,"w") == 0)
2959 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2960 break;
2961 }
2962 case 'Y':
2963 case 'y':
2964 {
2965 if (LocaleCompare(expression,"y") == 0)
2966 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2967 break;
2968 }
2969 case 'Z':
2970 case 'z':
2971 {
2972 if (LocaleCompare(expression,"z") == 0)
2973 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2974 break;
2975 }
2976 default:
2977 break;
2978 }
2979 subexpression=DestroyString(subexpression);
2980 q=(char *) expression;
2981 alpha=InterpretSiPrefixValue(expression,&q);
2982 if (q == expression)
2983 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2984 FxReturn(alpha);
2985 }
2986
FxEvaluateExpression(FxInfo * fx_info,double * alpha,ExceptionInfo * exception)2987 MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2988 double *alpha,ExceptionInfo *exception)
2989 {
2990 MagickBooleanType
2991 status;
2992
2993 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2994 exception);
2995 return(status);
2996 }
2997
FxPreprocessExpression(FxInfo * fx_info,double * alpha,ExceptionInfo * exception)2998 MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2999 double *alpha,ExceptionInfo *exception)
3000 {
3001 FILE
3002 *file;
3003
3004 MagickBooleanType
3005 status;
3006
3007 file=fx_info->file;
3008 fx_info->file=(FILE *) NULL;
3009 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
3010 exception);
3011 fx_info->file=file;
3012 return(status);
3013 }
3014
FxEvaluateChannelExpression(FxInfo * fx_info,const PixelChannel channel,const ssize_t x,const ssize_t y,double * alpha,ExceptionInfo * exception)3015 MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
3016 const PixelChannel channel,const ssize_t x,const ssize_t y,
3017 double *alpha,ExceptionInfo *exception)
3018 {
3019 double
3020 beta;
3021
3022 beta=0.0;
3023 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,0,
3024 &beta,exception);
3025 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
3026 }
3027
3028 /*
3029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3030 % %
3031 % %
3032 % %
3033 % F x I m a g e %
3034 % %
3035 % %
3036 % %
3037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3038 %
3039 % FxImage() applies a mathematical expression to the specified image.
3040 %
3041 % The format of the FxImage method is:
3042 %
3043 % Image *FxImage(const Image *image,const char *expression,
3044 % ExceptionInfo *exception)
3045 %
3046 % A description of each parameter follows:
3047 %
3048 % o image: the image.
3049 %
3050 % o expression: A mathematical expression.
3051 %
3052 % o exception: return any errors or warnings in this structure.
3053 %
3054 */
3055
DestroyFxThreadSet(FxInfo ** fx_info)3056 static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
3057 {
3058 register ssize_t
3059 i;
3060
3061 assert(fx_info != (FxInfo **) NULL);
3062 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
3063 if (fx_info[i] != (FxInfo *) NULL)
3064 fx_info[i]=DestroyFxInfo(fx_info[i]);
3065 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
3066 return(fx_info);
3067 }
3068
AcquireFxThreadSet(const Image * image,const char * expression,ExceptionInfo * exception)3069 static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
3070 ExceptionInfo *exception)
3071 {
3072 char
3073 *fx_expression;
3074
3075 FxInfo
3076 **fx_info;
3077
3078 double
3079 alpha;
3080
3081 register ssize_t
3082 i;
3083
3084 size_t
3085 number_threads;
3086
3087 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
3088 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
3089 if (fx_info == (FxInfo **) NULL)
3090 {
3091 (void) ThrowMagickException(exception,GetMagickModule(),
3092 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
3093 return((FxInfo **) NULL);
3094 }
3095 (void) memset(fx_info,0,number_threads*sizeof(*fx_info));
3096 if (*expression != '@')
3097 fx_expression=ConstantString(expression);
3098 else
3099 fx_expression=FileToString(expression+1,~0UL,exception);
3100 for (i=0; i < (ssize_t) number_threads; i++)
3101 {
3102 MagickBooleanType
3103 status;
3104
3105 fx_info[i]=AcquireFxInfo(image,fx_expression,exception);
3106 if (fx_info[i] == (FxInfo *) NULL)
3107 break;
3108 status=FxPreprocessExpression(fx_info[i],&alpha,exception);
3109 if (status == MagickFalse)
3110 break;
3111 }
3112 fx_expression=DestroyString(fx_expression);
3113 if (i < (ssize_t) number_threads)
3114 fx_info=DestroyFxThreadSet(fx_info);
3115 return(fx_info);
3116 }
3117
FxImage(const Image * image,const char * expression,ExceptionInfo * exception)3118 MagickExport Image *FxImage(const Image *image,const char *expression,
3119 ExceptionInfo *exception)
3120 {
3121 #define FxImageTag "Fx/Image"
3122
3123 CacheView
3124 *fx_view,
3125 *image_view;
3126
3127 FxInfo
3128 **magick_restrict fx_info;
3129
3130 Image
3131 *fx_image;
3132
3133 MagickBooleanType
3134 status;
3135
3136 MagickOffsetType
3137 progress;
3138
3139 ssize_t
3140 y;
3141
3142 assert(image != (Image *) NULL);
3143 assert(image->signature == MagickCoreSignature);
3144 if (image->debug != MagickFalse)
3145 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3146 if (expression == (const char *) NULL)
3147 return(CloneImage(image,0,0,MagickTrue,exception));
3148 fx_info=AcquireFxThreadSet(image,expression,exception);
3149 if (fx_info == (FxInfo **) NULL)
3150 return((Image *) NULL);
3151 fx_image=CloneImage(image,0,0,MagickTrue,exception);
3152 if (fx_image == (Image *) NULL)
3153 {
3154 fx_info=DestroyFxThreadSet(fx_info);
3155 return((Image *) NULL);
3156 }
3157 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
3158 {
3159 fx_info=DestroyFxThreadSet(fx_info);
3160 fx_image=DestroyImage(fx_image);
3161 return((Image *) NULL);
3162 }
3163 /*
3164 Fx image.
3165 */
3166 status=MagickTrue;
3167 progress=0;
3168 image_view=AcquireVirtualCacheView(image,exception);
3169 fx_view=AcquireAuthenticCacheView(fx_image,exception);
3170 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3171 #pragma omp parallel for schedule(static) shared(progress,status) \
3172 magick_number_threads(image,fx_image,fx_image->rows,1)
3173 #endif
3174 for (y=0; y < (ssize_t) fx_image->rows; y++)
3175 {
3176 const int
3177 id = GetOpenMPThreadId();
3178
3179 register const Quantum
3180 *magick_restrict p;
3181
3182 register Quantum
3183 *magick_restrict q;
3184
3185 register ssize_t
3186 x;
3187
3188 if (status == MagickFalse)
3189 continue;
3190 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3191 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
3192 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3193 {
3194 status=MagickFalse;
3195 continue;
3196 }
3197 for (x=0; x < (ssize_t) fx_image->columns; x++)
3198 {
3199 register ssize_t
3200 i;
3201
3202 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3203 {
3204 double
3205 alpha;
3206
3207 PixelChannel channel = GetPixelChannelChannel(image,i);
3208 PixelTrait traits = GetPixelChannelTraits(image,channel);
3209 PixelTrait fx_traits=GetPixelChannelTraits(fx_image,channel);
3210 if ((traits == UndefinedPixelTrait) ||
3211 (fx_traits == UndefinedPixelTrait))
3212 continue;
3213 if ((fx_traits & CopyPixelTrait) != 0)
3214 {
3215 SetPixelChannel(fx_image,channel,p[i],q);
3216 continue;
3217 }
3218 alpha=0.0;
3219 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3220 exception);
3221 q[i]=ClampToQuantum(QuantumRange*alpha);
3222 }
3223 p+=GetPixelChannels(image);
3224 q+=GetPixelChannels(fx_image);
3225 }
3226 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3227 status=MagickFalse;
3228 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3229 {
3230 MagickBooleanType
3231 proceed;
3232
3233 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3234 #pragma omp atomic
3235 #endif
3236 progress++;
3237 proceed=SetImageProgress(image,FxImageTag,progress,image->rows);
3238 if (proceed == MagickFalse)
3239 status=MagickFalse;
3240 }
3241 }
3242 fx_view=DestroyCacheView(fx_view);
3243 image_view=DestroyCacheView(image_view);
3244 fx_info=DestroyFxThreadSet(fx_info);
3245 if (status == MagickFalse)
3246 fx_image=DestroyImage(fx_image);
3247 return(fx_image);
3248 }
3249
3250 /*
3251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3252 % %
3253 % %
3254 % %
3255 % I m p l o d e I m a g e %
3256 % %
3257 % %
3258 % %
3259 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3260 %
3261 % ImplodeImage() creates a new image that is a copy of an existing
3262 % one with the image pixels "implode" by the specified percentage. It
3263 % allocates the memory necessary for the new Image structure and returns a
3264 % pointer to the new image.
3265 %
3266 % The format of the ImplodeImage method is:
3267 %
3268 % Image *ImplodeImage(const Image *image,const double amount,
3269 % const PixelInterpolateMethod method,ExceptionInfo *exception)
3270 %
3271 % A description of each parameter follows:
3272 %
3273 % o implode_image: Method ImplodeImage returns a pointer to the image
3274 % after it is implode. A null image is returned if there is a memory
3275 % shortage.
3276 %
3277 % o image: the image.
3278 %
3279 % o amount: Define the extent of the implosion.
3280 %
3281 % o method: the pixel interpolation method.
3282 %
3283 % o exception: return any errors or warnings in this structure.
3284 %
3285 */
ImplodeImage(const Image * image,const double amount,const PixelInterpolateMethod method,ExceptionInfo * exception)3286 MagickExport Image *ImplodeImage(const Image *image,const double amount,
3287 const PixelInterpolateMethod method,ExceptionInfo *exception)
3288 {
3289 #define ImplodeImageTag "Implode/Image"
3290
3291 CacheView
3292 *canvas_view,
3293 *implode_view,
3294 *interpolate_view;
3295
3296 double
3297 radius;
3298
3299 Image
3300 *canvas_image,
3301 *implode_image;
3302
3303 MagickBooleanType
3304 status;
3305
3306 MagickOffsetType
3307 progress;
3308
3309 PointInfo
3310 center,
3311 scale;
3312
3313 ssize_t
3314 y;
3315
3316 /*
3317 Initialize implode image attributes.
3318 */
3319 assert(image != (Image *) NULL);
3320 assert(image->signature == MagickCoreSignature);
3321 if (image->debug != MagickFalse)
3322 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3323 assert(exception != (ExceptionInfo *) NULL);
3324 assert(exception->signature == MagickCoreSignature);
3325 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
3326 if (canvas_image == (Image *) NULL)
3327 return((Image *) NULL);
3328 if ((canvas_image->alpha_trait == UndefinedPixelTrait) &&
3329 (canvas_image->background_color.alpha != OpaqueAlpha))
3330 (void) SetImageAlphaChannel(canvas_image,OpaqueAlphaChannel,exception);
3331 implode_image=CloneImage(canvas_image,0,0,MagickTrue,exception);
3332 if (implode_image == (Image *) NULL)
3333 {
3334 canvas_image=DestroyImage(canvas_image);
3335 return((Image *) NULL);
3336 }
3337 if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
3338 {
3339 canvas_image=DestroyImage(canvas_image);
3340 implode_image=DestroyImage(implode_image);
3341 return((Image *) NULL);
3342 }
3343 /*
3344 Compute scaling factor.
3345 */
3346 scale.x=1.0;
3347 scale.y=1.0;
3348 center.x=0.5*canvas_image->columns;
3349 center.y=0.5*canvas_image->rows;
3350 radius=center.x;
3351 if (canvas_image->columns > canvas_image->rows)
3352 scale.y=(double) canvas_image->columns/(double) canvas_image->rows;
3353 else
3354 if (canvas_image->columns < canvas_image->rows)
3355 {
3356 scale.x=(double) canvas_image->rows/(double) canvas_image->columns;
3357 radius=center.y;
3358 }
3359 /*
3360 Implode image.
3361 */
3362 status=MagickTrue;
3363 progress=0;
3364 canvas_view=AcquireVirtualCacheView(canvas_image,exception);
3365 interpolate_view=AcquireVirtualCacheView(canvas_image,exception);
3366 implode_view=AcquireAuthenticCacheView(implode_image,exception);
3367 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3368 #pragma omp parallel for schedule(static) shared(progress,status) \
3369 magick_number_threads(canvas_image,implode_image,canvas_image->rows,1)
3370 #endif
3371 for (y=0; y < (ssize_t) canvas_image->rows; y++)
3372 {
3373 double
3374 distance;
3375
3376 PointInfo
3377 delta;
3378
3379 register const Quantum
3380 *magick_restrict p;
3381
3382 register ssize_t
3383 x;
3384
3385 register Quantum
3386 *magick_restrict q;
3387
3388 if (status == MagickFalse)
3389 continue;
3390 p=GetCacheViewVirtualPixels(canvas_view,0,y,canvas_image->columns,1,
3391 exception);
3392 q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3393 exception);
3394 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3395 {
3396 status=MagickFalse;
3397 continue;
3398 }
3399 delta.y=scale.y*(double) (y-center.y);
3400 for (x=0; x < (ssize_t) canvas_image->columns; x++)
3401 {
3402 register ssize_t
3403 i;
3404
3405 /*
3406 Determine if the pixel is within an ellipse.
3407 */
3408 delta.x=scale.x*(double) (x-center.x);
3409 distance=delta.x*delta.x+delta.y*delta.y;
3410 if (distance >= (radius*radius))
3411 for (i=0; i < (ssize_t) GetPixelChannels(canvas_image); i++)
3412 {
3413 PixelChannel channel = GetPixelChannelChannel(canvas_image,i);
3414 PixelTrait traits = GetPixelChannelTraits(canvas_image,channel);
3415 PixelTrait implode_traits = GetPixelChannelTraits(implode_image,
3416 channel);
3417 if ((traits == UndefinedPixelTrait) ||
3418 (implode_traits == UndefinedPixelTrait))
3419 continue;
3420 SetPixelChannel(implode_image,channel,p[i],q);
3421 }
3422 else
3423 {
3424 double
3425 factor;
3426
3427 /*
3428 Implode the pixel.
3429 */
3430 factor=1.0;
3431 if (distance > 0.0)
3432 factor=pow(sin(MagickPI*sqrt((double) distance)/radius/2),-amount);
3433 status=InterpolatePixelChannels(canvas_image,interpolate_view,
3434 implode_image,method,(double) (factor*delta.x/scale.x+center.x),
3435 (double) (factor*delta.y/scale.y+center.y),q,exception);
3436 if (status == MagickFalse)
3437 break;
3438 }
3439 p+=GetPixelChannels(canvas_image);
3440 q+=GetPixelChannels(implode_image);
3441 }
3442 if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3443 status=MagickFalse;
3444 if (canvas_image->progress_monitor != (MagickProgressMonitor) NULL)
3445 {
3446 MagickBooleanType
3447 proceed;
3448
3449 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3450 #pragma omp atomic
3451 #endif
3452 progress++;
3453 proceed=SetImageProgress(canvas_image,ImplodeImageTag,progress,
3454 canvas_image->rows);
3455 if (proceed == MagickFalse)
3456 status=MagickFalse;
3457 }
3458 }
3459 implode_view=DestroyCacheView(implode_view);
3460 interpolate_view=DestroyCacheView(interpolate_view);
3461 canvas_view=DestroyCacheView(canvas_view);
3462 canvas_image=DestroyImage(canvas_image);
3463 if (status == MagickFalse)
3464 implode_image=DestroyImage(implode_image);
3465 return(implode_image);
3466 }
3467
3468 /*
3469 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3470 % %
3471 % %
3472 % %
3473 % M o r p h I m a g e s %
3474 % %
3475 % %
3476 % %
3477 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3478 %
3479 % The MorphImages() method requires a minimum of two images. The first
3480 % image is transformed into the second by a number of intervening images
3481 % as specified by frames.
3482 %
3483 % The format of the MorphImage method is:
3484 %
3485 % Image *MorphImages(const Image *image,const size_t number_frames,
3486 % ExceptionInfo *exception)
3487 %
3488 % A description of each parameter follows:
3489 %
3490 % o image: the image.
3491 %
3492 % o number_frames: Define the number of in-between image to generate.
3493 % The more in-between frames, the smoother the morph.
3494 %
3495 % o exception: return any errors or warnings in this structure.
3496 %
3497 */
MorphImages(const Image * image,const size_t number_frames,ExceptionInfo * exception)3498 MagickExport Image *MorphImages(const Image *image,const size_t number_frames,
3499 ExceptionInfo *exception)
3500 {
3501 #define MorphImageTag "Morph/Image"
3502
3503 double
3504 alpha,
3505 beta;
3506
3507 Image
3508 *morph_image,
3509 *morph_images;
3510
3511 MagickBooleanType
3512 status;
3513
3514 MagickOffsetType
3515 scene;
3516
3517 register const Image
3518 *next;
3519
3520 register ssize_t
3521 n;
3522
3523 ssize_t
3524 y;
3525
3526 /*
3527 Clone first frame in sequence.
3528 */
3529 assert(image != (Image *) NULL);
3530 assert(image->signature == MagickCoreSignature);
3531 if (image->debug != MagickFalse)
3532 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3533 assert(exception != (ExceptionInfo *) NULL);
3534 assert(exception->signature == MagickCoreSignature);
3535 morph_images=CloneImage(image,0,0,MagickTrue,exception);
3536 if (morph_images == (Image *) NULL)
3537 return((Image *) NULL);
3538 if (GetNextImageInList(image) == (Image *) NULL)
3539 {
3540 /*
3541 Morph single image.
3542 */
3543 for (n=1; n < (ssize_t) number_frames; n++)
3544 {
3545 morph_image=CloneImage(image,0,0,MagickTrue,exception);
3546 if (morph_image == (Image *) NULL)
3547 {
3548 morph_images=DestroyImageList(morph_images);
3549 return((Image *) NULL);
3550 }
3551 AppendImageToList(&morph_images,morph_image);
3552 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3553 {
3554 MagickBooleanType
3555 proceed;
3556
3557 proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) n,
3558 number_frames);
3559 if (proceed == MagickFalse)
3560 status=MagickFalse;
3561 }
3562 }
3563 return(GetFirstImageInList(morph_images));
3564 }
3565 /*
3566 Morph image sequence.
3567 */
3568 status=MagickTrue;
3569 scene=0;
3570 next=image;
3571 for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3572 {
3573 for (n=0; n < (ssize_t) number_frames; n++)
3574 {
3575 CacheView
3576 *image_view,
3577 *morph_view;
3578
3579 beta=(double) (n+1.0)/(double) (number_frames+1.0);
3580 alpha=1.0-beta;
3581 morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
3582 GetNextImageInList(next)->columns+0.5),(size_t) (alpha*next->rows+beta*
3583 GetNextImageInList(next)->rows+0.5),next->filter,exception);
3584 if (morph_image == (Image *) NULL)
3585 {
3586 morph_images=DestroyImageList(morph_images);
3587 return((Image *) NULL);
3588 }
3589 status=SetImageStorageClass(morph_image,DirectClass,exception);
3590 if (status == MagickFalse)
3591 {
3592 morph_image=DestroyImage(morph_image);
3593 return((Image *) NULL);
3594 }
3595 AppendImageToList(&morph_images,morph_image);
3596 morph_images=GetLastImageInList(morph_images);
3597 morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3598 morph_images->rows,GetNextImageInList(next)->filter,exception);
3599 if (morph_image == (Image *) NULL)
3600 {
3601 morph_images=DestroyImageList(morph_images);
3602 return((Image *) NULL);
3603 }
3604 image_view=AcquireVirtualCacheView(morph_image,exception);
3605 morph_view=AcquireAuthenticCacheView(morph_images,exception);
3606 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3607 #pragma omp parallel for schedule(static) shared(status) \
3608 magick_number_threads(morph_image,morph_image,morph_image->rows,1)
3609 #endif
3610 for (y=0; y < (ssize_t) morph_images->rows; y++)
3611 {
3612 MagickBooleanType
3613 sync;
3614
3615 register const Quantum
3616 *magick_restrict p;
3617
3618 register ssize_t
3619 x;
3620
3621 register Quantum
3622 *magick_restrict q;
3623
3624 if (status == MagickFalse)
3625 continue;
3626 p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3627 exception);
3628 q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3629 exception);
3630 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3631 {
3632 status=MagickFalse;
3633 continue;
3634 }
3635 for (x=0; x < (ssize_t) morph_images->columns; x++)
3636 {
3637 register ssize_t
3638 i;
3639
3640 for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
3641 {
3642 PixelChannel channel = GetPixelChannelChannel(morph_image,i);
3643 PixelTrait traits = GetPixelChannelTraits(morph_image,channel);
3644 PixelTrait morph_traits=GetPixelChannelTraits(morph_images,channel);
3645 if ((traits == UndefinedPixelTrait) ||
3646 (morph_traits == UndefinedPixelTrait))
3647 continue;
3648 if ((morph_traits & CopyPixelTrait) != 0)
3649 {
3650 SetPixelChannel(morph_image,channel,p[i],q);
3651 continue;
3652 }
3653 SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3654 GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3655 }
3656 p+=GetPixelChannels(morph_image);
3657 q+=GetPixelChannels(morph_images);
3658 }
3659 sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3660 if (sync == MagickFalse)
3661 status=MagickFalse;
3662 }
3663 morph_view=DestroyCacheView(morph_view);
3664 image_view=DestroyCacheView(image_view);
3665 morph_image=DestroyImage(morph_image);
3666 }
3667 if (n < (ssize_t) number_frames)
3668 break;
3669 /*
3670 Clone last frame in sequence.
3671 */
3672 morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3673 if (morph_image == (Image *) NULL)
3674 {
3675 morph_images=DestroyImageList(morph_images);
3676 return((Image *) NULL);
3677 }
3678 AppendImageToList(&morph_images,morph_image);
3679 morph_images=GetLastImageInList(morph_images);
3680 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3681 {
3682 MagickBooleanType
3683 proceed;
3684
3685 proceed=SetImageProgress(image,MorphImageTag,scene,
3686 GetImageListLength(image));
3687 if (proceed == MagickFalse)
3688 status=MagickFalse;
3689 }
3690 scene++;
3691 }
3692 if (GetNextImageInList(next) != (Image *) NULL)
3693 {
3694 morph_images=DestroyImageList(morph_images);
3695 return((Image *) NULL);
3696 }
3697 return(GetFirstImageInList(morph_images));
3698 }
3699
3700 /*
3701 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3702 % %
3703 % %
3704 % %
3705 % P l a s m a I m a g e %
3706 % %
3707 % %
3708 % %
3709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3710 %
3711 % PlasmaImage() initializes an image with plasma fractal values. The image
3712 % must be initialized with a base color and the random number generator
3713 % seeded before this method is called.
3714 %
3715 % The format of the PlasmaImage method is:
3716 %
3717 % MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
3718 % size_t attenuate,size_t depth,ExceptionInfo *exception)
3719 %
3720 % A description of each parameter follows:
3721 %
3722 % o image: the image.
3723 %
3724 % o segment: Define the region to apply plasma fractals values.
3725 %
3726 % o attenuate: Define the plasma attenuation factor.
3727 %
3728 % o depth: Limit the plasma recursion depth.
3729 %
3730 % o exception: return any errors or warnings in this structure.
3731 %
3732 */
3733
PlasmaPixel(RandomInfo * random_info,const double pixel,const double noise)3734 static inline Quantum PlasmaPixel(RandomInfo *random_info,
3735 const double pixel,const double noise)
3736 {
3737 Quantum
3738 plasma;
3739
3740 plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
3741 noise/2.0);
3742 if (plasma <= 0)
3743 return((Quantum) 0);
3744 if (plasma >= QuantumRange)
3745 return(QuantumRange);
3746 return(plasma);
3747 }
3748
PlasmaImageProxy(Image * image,CacheView * image_view,CacheView * u_view,CacheView * v_view,RandomInfo * random_info,const SegmentInfo * segment,size_t attenuate,size_t depth,ExceptionInfo * exception)3749 static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
3750 CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3751 const SegmentInfo *segment,size_t attenuate,size_t depth,
3752 ExceptionInfo *exception)
3753 {
3754 double
3755 plasma;
3756
3757 register const Quantum
3758 *magick_restrict u,
3759 *magick_restrict v;
3760
3761 register Quantum
3762 *magick_restrict q;
3763
3764 register ssize_t
3765 i;
3766
3767 ssize_t
3768 x,
3769 x_mid,
3770 y,
3771 y_mid;
3772
3773 if ((fabs(segment->x2-segment->x1) <= MagickEpsilon) &&
3774 (fabs(segment->y2-segment->y1) <= MagickEpsilon))
3775 return(MagickTrue);
3776 if (depth != 0)
3777 {
3778 MagickBooleanType
3779 status;
3780
3781 SegmentInfo
3782 local_info;
3783
3784 /*
3785 Divide the area into quadrants and recurse.
3786 */
3787 depth--;
3788 attenuate++;
3789 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3790 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
3791 local_info=(*segment);
3792 local_info.x2=(double) x_mid;
3793 local_info.y2=(double) y_mid;
3794 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3795 &local_info,attenuate,depth,exception);
3796 local_info=(*segment);
3797 local_info.y1=(double) y_mid;
3798 local_info.x2=(double) x_mid;
3799 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3800 &local_info,attenuate,depth,exception);
3801 local_info=(*segment);
3802 local_info.x1=(double) x_mid;
3803 local_info.y2=(double) y_mid;
3804 (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3805 &local_info,attenuate,depth,exception);
3806 local_info=(*segment);
3807 local_info.x1=(double) x_mid;
3808 local_info.y1=(double) y_mid;
3809 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3810 &local_info,attenuate,depth,exception);
3811 return(status);
3812 }
3813 x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3814 y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
3815 if ((fabs(segment->x1-x_mid) < MagickEpsilon) &&
3816 (fabs(segment->x2-x_mid) < MagickEpsilon) &&
3817 (fabs(segment->y1-y_mid) < MagickEpsilon) &&
3818 (fabs(segment->y2-y_mid) < MagickEpsilon))
3819 return(MagickFalse);
3820 /*
3821 Average pixels and apply plasma.
3822 */
3823 plasma=(double) QuantumRange/(2.0*attenuate);
3824 if ((fabs(segment->x1-x_mid) > MagickEpsilon) ||
3825 (fabs(segment->x2-x_mid) > MagickEpsilon))
3826 {
3827 /*
3828 Left pixel.
3829 */
3830 x=(ssize_t) ceil(segment->x1-0.5);
3831 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3832 exception);
3833 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3834 exception);
3835 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
3836 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3837 (q == (Quantum *) NULL))
3838 return(MagickTrue);
3839 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3840 {
3841 PixelChannel channel = GetPixelChannelChannel(image,i);
3842 PixelTrait traits = GetPixelChannelTraits(image,channel);
3843 if (traits == UndefinedPixelTrait)
3844 continue;
3845 q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
3846 }
3847 (void) SyncCacheViewAuthenticPixels(image_view,exception);
3848 if (fabs(segment->x1-segment->x2) > MagickEpsilon)
3849 {
3850 /*
3851 Right pixel.
3852 */
3853 x=(ssize_t) ceil(segment->x2-0.5);
3854 u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3855 1,1,exception);
3856 v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3857 1,1,exception);
3858 q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
3859 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3860 (q == (Quantum *) NULL))
3861 return(MagickTrue);
3862 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3863 {
3864 PixelChannel channel = GetPixelChannelChannel(image,i);
3865 PixelTrait traits = GetPixelChannelTraits(image,channel);
3866 if (traits == UndefinedPixelTrait)
3867 continue;
3868 q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
3869 }
3870 (void) SyncCacheViewAuthenticPixels(image_view,exception);
3871 }
3872 }
3873 if ((fabs(segment->y1-y_mid) > MagickEpsilon) ||
3874 (fabs(segment->y2-y_mid) > MagickEpsilon))
3875 {
3876 if ((fabs(segment->x1-x_mid) > MagickEpsilon) ||
3877 (fabs(segment->y2-y_mid) > MagickEpsilon))
3878 {
3879 /*
3880 Bottom pixel.
3881 */
3882 y=(ssize_t) ceil(segment->y2-0.5);
3883 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3884 1,1,exception);
3885 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3886 1,1,exception);
3887 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
3888 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3889 (q == (Quantum *) NULL))
3890 return(MagickTrue);
3891 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3892 {
3893 PixelChannel channel = GetPixelChannelChannel(image,i);
3894 PixelTrait traits = GetPixelChannelTraits(image,channel);
3895 if (traits == UndefinedPixelTrait)
3896 continue;
3897 q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
3898 }
3899 (void) SyncCacheViewAuthenticPixels(image_view,exception);
3900 }
3901 if (fabs(segment->y1-segment->y2) > MagickEpsilon)
3902 {
3903 /*
3904 Top pixel.
3905 */
3906 y=(ssize_t) ceil(segment->y1-0.5);
3907 u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3908 1,1,exception);
3909 v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3910 1,1,exception);
3911 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
3912 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3913 (q == (Quantum *) NULL))
3914 return(MagickTrue);
3915 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3916 {
3917 PixelChannel channel = GetPixelChannelChannel(image,i);
3918 PixelTrait traits = GetPixelChannelTraits(image,channel);
3919 if (traits == UndefinedPixelTrait)
3920 continue;
3921 q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
3922 }
3923 (void) SyncCacheViewAuthenticPixels(image_view,exception);
3924 }
3925 }
3926 if ((fabs(segment->x1-segment->x2) > MagickEpsilon) ||
3927 (fabs(segment->y1-segment->y2) > MagickEpsilon))
3928 {
3929 /*
3930 Middle pixel.
3931 */
3932 x=(ssize_t) ceil(segment->x1-0.5);
3933 y=(ssize_t) ceil(segment->y1-0.5);
3934 u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
3935 x=(ssize_t) ceil(segment->x2-0.5);
3936 y=(ssize_t) ceil(segment->y2-0.5);
3937 v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
3938 q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
3939 if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3940 (q == (Quantum *) NULL))
3941 return(MagickTrue);
3942 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3943 {
3944 PixelChannel channel = GetPixelChannelChannel(image,i);
3945 PixelTrait traits = GetPixelChannelTraits(image,channel);
3946 if (traits == UndefinedPixelTrait)
3947 continue;
3948 q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
3949 }
3950 (void) SyncCacheViewAuthenticPixels(image_view,exception);
3951 }
3952 if ((fabs(segment->x2-segment->x1) < 3.0) &&
3953 (fabs(segment->y2-segment->y1) < 3.0))
3954 return(MagickTrue);
3955 return(MagickFalse);
3956 }
3957
PlasmaImage(Image * image,const SegmentInfo * segment,size_t attenuate,size_t depth,ExceptionInfo * exception)3958 MagickExport MagickBooleanType PlasmaImage(Image *image,
3959 const SegmentInfo *segment,size_t attenuate,size_t depth,
3960 ExceptionInfo *exception)
3961 {
3962 CacheView
3963 *image_view,
3964 *u_view,
3965 *v_view;
3966
3967 MagickBooleanType
3968 status;
3969
3970 RandomInfo
3971 *random_info;
3972
3973 assert(image != (Image *) NULL);
3974 if (image->debug != MagickFalse)
3975 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3976 assert(image->signature == MagickCoreSignature);
3977 if (image->debug != MagickFalse)
3978 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3979 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
3980 return(MagickFalse);
3981 image_view=AcquireAuthenticCacheView(image,exception);
3982 u_view=AcquireVirtualCacheView(image,exception);
3983 v_view=AcquireVirtualCacheView(image,exception);
3984 random_info=AcquireRandomInfo();
3985 status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
3986 attenuate,depth,exception);
3987 random_info=DestroyRandomInfo(random_info);
3988 v_view=DestroyCacheView(v_view);
3989 u_view=DestroyCacheView(u_view);
3990 image_view=DestroyCacheView(image_view);
3991 return(status);
3992 }
3993
3994 /*
3995 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3996 % %
3997 % %
3998 % %
3999 % P o l a r o i d I m a g e %
4000 % %
4001 % %
4002 % %
4003 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4004 %
4005 % PolaroidImage() simulates a Polaroid picture.
4006 %
4007 % The format of the PolaroidImage method is:
4008 %
4009 % Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
4010 % const char *caption,const double angle,
4011 % const PixelInterpolateMethod method,ExceptionInfo exception)
4012 %
4013 % A description of each parameter follows:
4014 %
4015 % o image: the image.
4016 %
4017 % o draw_info: the draw info.
4018 %
4019 % o caption: the Polaroid caption.
4020 %
4021 % o angle: Apply the effect along this angle.
4022 %
4023 % o method: the pixel interpolation method.
4024 %
4025 % o exception: return any errors or warnings in this structure.
4026 %
4027 */
PolaroidImage(const Image * image,const DrawInfo * draw_info,const char * caption,const double angle,const PixelInterpolateMethod method,ExceptionInfo * exception)4028 MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
4029 const char *caption,const double angle,const PixelInterpolateMethod method,
4030 ExceptionInfo *exception)
4031 {
4032 Image
4033 *bend_image,
4034 *caption_image,
4035 *flop_image,
4036 *picture_image,
4037 *polaroid_image,
4038 *rotate_image,
4039 *trim_image;
4040
4041 size_t
4042 height;
4043
4044 ssize_t
4045 quantum;
4046
4047 /*
4048 Simulate a Polaroid picture.
4049 */
4050 assert(image != (Image *) NULL);
4051 assert(image->signature == MagickCoreSignature);
4052 if (image->debug != MagickFalse)
4053 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4054 assert(exception != (ExceptionInfo *) NULL);
4055 assert(exception->signature == MagickCoreSignature);
4056 quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
4057 image->rows)/25.0,10.0);
4058 height=image->rows+2*quantum;
4059 caption_image=(Image *) NULL;
4060 if (caption != (const char *) NULL)
4061 {
4062 char
4063 *text;
4064
4065 /*
4066 Generate caption image.
4067 */
4068 caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
4069 if (caption_image == (Image *) NULL)
4070 return((Image *) NULL);
4071 text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
4072 exception);
4073 if (text != (char *) NULL)
4074 {
4075 char
4076 geometry[MagickPathExtent];
4077
4078 DrawInfo
4079 *annotate_info;
4080
4081 MagickBooleanType
4082 status;
4083
4084 ssize_t
4085 count;
4086
4087 TypeMetric
4088 metrics;
4089
4090 annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
4091 (void) CloneString(&annotate_info->text,text);
4092 count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,
4093 &metrics,&text,exception);
4094 status=SetImageExtent(caption_image,image->columns,(size_t)
4095 ((count+1)*(metrics.ascent-metrics.descent)+0.5),exception);
4096 if (status == MagickFalse)
4097 caption_image=DestroyImage(caption_image);
4098 else
4099 {
4100 caption_image->background_color=image->border_color;
4101 (void) SetImageBackgroundColor(caption_image,exception);
4102 (void) CloneString(&annotate_info->text,text);
4103 (void) FormatLocaleString(geometry,MagickPathExtent,"+0+%.20g",
4104 metrics.ascent);
4105 if (annotate_info->gravity == UndefinedGravity)
4106 (void) CloneString(&annotate_info->geometry,AcquireString(
4107 geometry));
4108 (void) AnnotateImage(caption_image,annotate_info,exception);
4109 height+=caption_image->rows;
4110 }
4111 annotate_info=DestroyDrawInfo(annotate_info);
4112 text=DestroyString(text);
4113 }
4114 }
4115 picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4116 exception);
4117 if (picture_image == (Image *) NULL)
4118 {
4119 if (caption_image != (Image *) NULL)
4120 caption_image=DestroyImage(caption_image);
4121 return((Image *) NULL);
4122 }
4123 picture_image->background_color=image->border_color;
4124 (void) SetImageBackgroundColor(picture_image,exception);
4125 (void) CompositeImage(picture_image,image,OverCompositeOp,MagickTrue,quantum,
4126 quantum,exception);
4127 if (caption_image != (Image *) NULL)
4128 {
4129 (void) CompositeImage(picture_image,caption_image,OverCompositeOp,
4130 MagickTrue,quantum,(ssize_t) (image->rows+3*quantum/2),exception);
4131 caption_image=DestroyImage(caption_image);
4132 }
4133 (void) QueryColorCompliance("none",AllCompliance,
4134 &picture_image->background_color,exception);
4135 (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
4136 rotate_image=RotateImage(picture_image,90.0,exception);
4137 picture_image=DestroyImage(picture_image);
4138 if (rotate_image == (Image *) NULL)
4139 return((Image *) NULL);
4140 picture_image=rotate_image;
4141 bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
4142 picture_image->columns,method,exception);
4143 picture_image=DestroyImage(picture_image);
4144 if (bend_image == (Image *) NULL)
4145 return((Image *) NULL);
4146 picture_image=bend_image;
4147 rotate_image=RotateImage(picture_image,-90.0,exception);
4148 picture_image=DestroyImage(picture_image);
4149 if (rotate_image == (Image *) NULL)
4150 return((Image *) NULL);
4151 picture_image=rotate_image;
4152 picture_image->background_color=image->background_color;
4153 polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
4154 exception);
4155 if (polaroid_image == (Image *) NULL)
4156 {
4157 picture_image=DestroyImage(picture_image);
4158 return(picture_image);
4159 }
4160 flop_image=FlopImage(polaroid_image,exception);
4161 polaroid_image=DestroyImage(polaroid_image);
4162 if (flop_image == (Image *) NULL)
4163 {
4164 picture_image=DestroyImage(picture_image);
4165 return(picture_image);
4166 }
4167 polaroid_image=flop_image;
4168 (void) CompositeImage(polaroid_image,picture_image,OverCompositeOp,
4169 MagickTrue,(ssize_t) (-0.01*picture_image->columns/2.0),0L,exception);
4170 picture_image=DestroyImage(picture_image);
4171 (void) QueryColorCompliance("none",AllCompliance,
4172 &polaroid_image->background_color,exception);
4173 rotate_image=RotateImage(polaroid_image,angle,exception);
4174 polaroid_image=DestroyImage(polaroid_image);
4175 if (rotate_image == (Image *) NULL)
4176 return((Image *) NULL);
4177 polaroid_image=rotate_image;
4178 trim_image=TrimImage(polaroid_image,exception);
4179 polaroid_image=DestroyImage(polaroid_image);
4180 if (trim_image == (Image *) NULL)
4181 return((Image *) NULL);
4182 polaroid_image=trim_image;
4183 return(polaroid_image);
4184 }
4185
4186 /*
4187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4188 % %
4189 % %
4190 % %
4191 % S e p i a T o n e I m a g e %
4192 % %
4193 % %
4194 % %
4195 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4196 %
4197 % MagickSepiaToneImage() applies a special effect to the image, similar to the
4198 % effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4199 % 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4200 % threshold of 80% is a good starting point for a reasonable tone.
4201 %
4202 % The format of the SepiaToneImage method is:
4203 %
4204 % Image *SepiaToneImage(const Image *image,const double threshold,
4205 % ExceptionInfo *exception)
4206 %
4207 % A description of each parameter follows:
4208 %
4209 % o image: the image.
4210 %
4211 % o threshold: the tone threshold.
4212 %
4213 % o exception: return any errors or warnings in this structure.
4214 %
4215 */
SepiaToneImage(const Image * image,const double threshold,ExceptionInfo * exception)4216 MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4217 ExceptionInfo *exception)
4218 {
4219 #define SepiaToneImageTag "SepiaTone/Image"
4220
4221 CacheView
4222 *image_view,
4223 *sepia_view;
4224
4225 Image
4226 *sepia_image;
4227
4228 MagickBooleanType
4229 status;
4230
4231 MagickOffsetType
4232 progress;
4233
4234 ssize_t
4235 y;
4236
4237 /*
4238 Initialize sepia-toned image attributes.
4239 */
4240 assert(image != (const Image *) NULL);
4241 assert(image->signature == MagickCoreSignature);
4242 if (image->debug != MagickFalse)
4243 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4244 assert(exception != (ExceptionInfo *) NULL);
4245 assert(exception->signature == MagickCoreSignature);
4246 sepia_image=CloneImage(image,0,0,MagickTrue,exception);
4247 if (sepia_image == (Image *) NULL)
4248 return((Image *) NULL);
4249 if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
4250 {
4251 sepia_image=DestroyImage(sepia_image);
4252 return((Image *) NULL);
4253 }
4254 /*
4255 Tone each row of the image.
4256 */
4257 status=MagickTrue;
4258 progress=0;
4259 image_view=AcquireVirtualCacheView(image,exception);
4260 sepia_view=AcquireAuthenticCacheView(sepia_image,exception);
4261 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4262 #pragma omp parallel for schedule(static) shared(progress,status) \
4263 magick_number_threads(image,sepia_image,image->rows,1)
4264 #endif
4265 for (y=0; y < (ssize_t) image->rows; y++)
4266 {
4267 register const Quantum
4268 *magick_restrict p;
4269
4270 register ssize_t
4271 x;
4272
4273 register Quantum
4274 *magick_restrict q;
4275
4276 if (status == MagickFalse)
4277 continue;
4278 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4279 q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4280 exception);
4281 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
4282 {
4283 status=MagickFalse;
4284 continue;
4285 }
4286 for (x=0; x < (ssize_t) image->columns; x++)
4287 {
4288 double
4289 intensity,
4290 tone;
4291
4292 intensity=GetPixelIntensity(image,p);
4293 tone=intensity > threshold ? (double) QuantumRange : intensity+
4294 (double) QuantumRange-threshold;
4295 SetPixelRed(sepia_image,ClampToQuantum(tone),q);
4296 tone=intensity > (7.0*threshold/6.0) ? (double) QuantumRange :
4297 intensity+(double) QuantumRange-7.0*threshold/6.0;
4298 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4299 tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
4300 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
4301 tone=threshold/7.0;
4302 if ((double) GetPixelGreen(image,q) < tone)
4303 SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4304 if ((double) GetPixelBlue(image,q) < tone)
4305 SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
4306 SetPixelAlpha(sepia_image,GetPixelAlpha(image,p),q);
4307 p+=GetPixelChannels(image);
4308 q+=GetPixelChannels(sepia_image);
4309 }
4310 if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4311 status=MagickFalse;
4312 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4313 {
4314 MagickBooleanType
4315 proceed;
4316
4317 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4318 #pragma omp atomic
4319 #endif
4320 progress++;
4321 proceed=SetImageProgress(image,SepiaToneImageTag,progress,image->rows);
4322 if (proceed == MagickFalse)
4323 status=MagickFalse;
4324 }
4325 }
4326 sepia_view=DestroyCacheView(sepia_view);
4327 image_view=DestroyCacheView(image_view);
4328 (void) NormalizeImage(sepia_image,exception);
4329 (void) ContrastImage(sepia_image,MagickTrue,exception);
4330 if (status == MagickFalse)
4331 sepia_image=DestroyImage(sepia_image);
4332 return(sepia_image);
4333 }
4334
4335 /*
4336 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4337 % %
4338 % %
4339 % %
4340 % S h a d o w I m a g e %
4341 % %
4342 % %
4343 % %
4344 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4345 %
4346 % ShadowImage() simulates a shadow from the specified image and returns it.
4347 %
4348 % The format of the ShadowImage method is:
4349 %
4350 % Image *ShadowImage(const Image *image,const double alpha,
4351 % const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4352 % ExceptionInfo *exception)
4353 %
4354 % A description of each parameter follows:
4355 %
4356 % o image: the image.
4357 %
4358 % o alpha: percentage transparency.
4359 %
4360 % o sigma: the standard deviation of the Gaussian, in pixels.
4361 %
4362 % o x_offset: the shadow x-offset.
4363 %
4364 % o y_offset: the shadow y-offset.
4365 %
4366 % o exception: return any errors or warnings in this structure.
4367 %
4368 */
ShadowImage(const Image * image,const double alpha,const double sigma,const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo * exception)4369 MagickExport Image *ShadowImage(const Image *image,const double alpha,
4370 const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4371 ExceptionInfo *exception)
4372 {
4373 #define ShadowImageTag "Shadow/Image"
4374
4375 CacheView
4376 *image_view;
4377
4378 ChannelType
4379 channel_mask;
4380
4381 Image
4382 *border_image,
4383 *clone_image,
4384 *shadow_image;
4385
4386 MagickBooleanType
4387 status;
4388
4389 PixelInfo
4390 background_color;
4391
4392 RectangleInfo
4393 border_info;
4394
4395 ssize_t
4396 y;
4397
4398 assert(image != (Image *) NULL);
4399 assert(image->signature == MagickCoreSignature);
4400 if (image->debug != MagickFalse)
4401 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4402 assert(exception != (ExceptionInfo *) NULL);
4403 assert(exception->signature == MagickCoreSignature);
4404 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4405 if (clone_image == (Image *) NULL)
4406 return((Image *) NULL);
4407 if (IsGrayColorspace(image->colorspace) != MagickFalse)
4408 (void) SetImageColorspace(clone_image,sRGBColorspace,exception);
4409 (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod,
4410 exception);
4411 border_info.width=(size_t) floor(2.0*sigma+0.5);
4412 border_info.height=(size_t) floor(2.0*sigma+0.5);
4413 border_info.x=0;
4414 border_info.y=0;
4415 (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4416 exception);
4417 clone_image->alpha_trait=BlendPixelTrait;
4418 border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
4419 clone_image=DestroyImage(clone_image);
4420 if (border_image == (Image *) NULL)
4421 return((Image *) NULL);
4422 if (border_image->alpha_trait == UndefinedPixelTrait)
4423 (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
4424 /*
4425 Shadow image.
4426 */
4427 status=MagickTrue;
4428 background_color=border_image->background_color;
4429 background_color.alpha_trait=BlendPixelTrait;
4430 image_view=AcquireAuthenticCacheView(border_image,exception);
4431 for (y=0; y < (ssize_t) border_image->rows; y++)
4432 {
4433 register Quantum
4434 *magick_restrict q;
4435
4436 register ssize_t
4437 x;
4438
4439 if (status == MagickFalse)
4440 continue;
4441 q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4442 exception);
4443 if (q == (Quantum *) NULL)
4444 {
4445 status=MagickFalse;
4446 continue;
4447 }
4448 for (x=0; x < (ssize_t) border_image->columns; x++)
4449 {
4450 if (border_image->alpha_trait != UndefinedPixelTrait)
4451 background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4452 SetPixelViaPixelInfo(border_image,&background_color,q);
4453 q+=GetPixelChannels(border_image);
4454 }
4455 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4456 status=MagickFalse;
4457 }
4458 image_view=DestroyCacheView(image_view);
4459 if (status == MagickFalse)
4460 {
4461 border_image=DestroyImage(border_image);
4462 return((Image *) NULL);
4463 }
4464 channel_mask=SetImageChannelMask(border_image,AlphaChannel);
4465 shadow_image=BlurImage(border_image,0.0,sigma,exception);
4466 border_image=DestroyImage(border_image);
4467 if (shadow_image == (Image *) NULL)
4468 return((Image *) NULL);
4469 (void) SetPixelChannelMask(shadow_image,channel_mask);
4470 if (shadow_image->page.width == 0)
4471 shadow_image->page.width=shadow_image->columns;
4472 if (shadow_image->page.height == 0)
4473 shadow_image->page.height=shadow_image->rows;
4474 shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4475 shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4476 shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4477 shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
4478 return(shadow_image);
4479 }
4480
4481 /*
4482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4483 % %
4484 % %
4485 % %
4486 % S k e t c h I m a g e %
4487 % %
4488 % %
4489 % %
4490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4491 %
4492 % SketchImage() simulates a pencil sketch. We convolve the image with a
4493 % Gaussian operator of the given radius and standard deviation (sigma). For
4494 % reasonable results, radius should be larger than sigma. Use a radius of 0
4495 % and SketchImage() selects a suitable radius for you. Angle gives the angle
4496 % of the sketch.
4497 %
4498 % The format of the SketchImage method is:
4499 %
4500 % Image *SketchImage(const Image *image,const double radius,
4501 % const double sigma,const double angle,ExceptionInfo *exception)
4502 %
4503 % A description of each parameter follows:
4504 %
4505 % o image: the image.
4506 %
4507 % o radius: the radius of the Gaussian, in pixels, not counting the
4508 % center pixel.
4509 %
4510 % o sigma: the standard deviation of the Gaussian, in pixels.
4511 %
4512 % o angle: apply the effect along this angle.
4513 %
4514 % o exception: return any errors or warnings in this structure.
4515 %
4516 */
SketchImage(const Image * image,const double radius,const double sigma,const double angle,ExceptionInfo * exception)4517 MagickExport Image *SketchImage(const Image *image,const double radius,
4518 const double sigma,const double angle,ExceptionInfo *exception)
4519 {
4520 CacheView
4521 *random_view;
4522
4523 Image
4524 *blend_image,
4525 *blur_image,
4526 *dodge_image,
4527 *random_image,
4528 *sketch_image;
4529
4530 MagickBooleanType
4531 status;
4532
4533 RandomInfo
4534 **magick_restrict random_info;
4535
4536 ssize_t
4537 y;
4538
4539 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4540 unsigned long
4541 key;
4542 #endif
4543
4544 /*
4545 Sketch image.
4546 */
4547 random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4548 MagickTrue,exception);
4549 if (random_image == (Image *) NULL)
4550 return((Image *) NULL);
4551 status=MagickTrue;
4552 random_info=AcquireRandomInfoThreadSet();
4553 random_view=AcquireAuthenticCacheView(random_image,exception);
4554 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4555 key=GetRandomSecretKey(random_info[0]);
4556 #pragma omp parallel for schedule(static) shared(status) \
4557 magick_number_threads(random_image,random_image,random_image->rows,key == ~0UL)
4558 #endif
4559 for (y=0; y < (ssize_t) random_image->rows; y++)
4560 {
4561 const int
4562 id = GetOpenMPThreadId();
4563
4564 register Quantum
4565 *magick_restrict q;
4566
4567 register ssize_t
4568 x;
4569
4570 if (status == MagickFalse)
4571 continue;
4572 q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4573 exception);
4574 if (q == (Quantum *) NULL)
4575 {
4576 status=MagickFalse;
4577 continue;
4578 }
4579 for (x=0; x < (ssize_t) random_image->columns; x++)
4580 {
4581 double
4582 value;
4583
4584 register ssize_t
4585 i;
4586
4587 value=GetPseudoRandomValue(random_info[id]);
4588 for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4589 {
4590 PixelChannel channel = GetPixelChannelChannel(image,i);
4591 PixelTrait traits = GetPixelChannelTraits(image,channel);
4592 if (traits == UndefinedPixelTrait)
4593 continue;
4594 q[i]=ClampToQuantum(QuantumRange*value);
4595 }
4596 q+=GetPixelChannels(random_image);
4597 }
4598 if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4599 status=MagickFalse;
4600 }
4601 random_view=DestroyCacheView(random_view);
4602 random_info=DestroyRandomInfoThreadSet(random_info);
4603 if (status == MagickFalse)
4604 {
4605 random_image=DestroyImage(random_image);
4606 return(random_image);
4607 }
4608 blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4609 random_image=DestroyImage(random_image);
4610 if (blur_image == (Image *) NULL)
4611 return((Image *) NULL);
4612 dodge_image=EdgeImage(blur_image,radius,exception);
4613 blur_image=DestroyImage(blur_image);
4614 if (dodge_image == (Image *) NULL)
4615 return((Image *) NULL);
4616 status=ClampImage(dodge_image,exception);
4617 if (status != MagickFalse)
4618 status=NormalizeImage(dodge_image,exception);
4619 if (status != MagickFalse)
4620 status=NegateImage(dodge_image,MagickFalse,exception);
4621 if (status != MagickFalse)
4622 status=TransformImage(&dodge_image,(char *) NULL,"50%",exception);
4623 sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4624 if (sketch_image == (Image *) NULL)
4625 {
4626 dodge_image=DestroyImage(dodge_image);
4627 return((Image *) NULL);
4628 }
4629 (void) CompositeImage(sketch_image,dodge_image,ColorDodgeCompositeOp,
4630 MagickTrue,0,0,exception);
4631 dodge_image=DestroyImage(dodge_image);
4632 blend_image=CloneImage(image,0,0,MagickTrue,exception);
4633 if (blend_image == (Image *) NULL)
4634 {
4635 sketch_image=DestroyImage(sketch_image);
4636 return((Image *) NULL);
4637 }
4638 if (blend_image->alpha_trait != BlendPixelTrait)
4639 (void) SetImageAlpha(blend_image,TransparentAlpha,exception);
4640 (void) SetImageArtifact(blend_image,"compose:args","20x80");
4641 (void) CompositeImage(sketch_image,blend_image,BlendCompositeOp,MagickTrue,
4642 0,0,exception);
4643 blend_image=DestroyImage(blend_image);
4644 return(sketch_image);
4645 }
4646
4647 /*
4648 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4649 % %
4650 % %
4651 % %
4652 % S o l a r i z e I m a g e %
4653 % %
4654 % %
4655 % %
4656 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4657 %
4658 % SolarizeImage() applies a special effect to the image, similar to the effect
4659 % achieved in a photo darkroom by selectively exposing areas of photo
4660 % sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4661 % measure of the extent of the solarization.
4662 %
4663 % The format of the SolarizeImage method is:
4664 %
4665 % MagickBooleanType SolarizeImage(Image *image,const double threshold,
4666 % ExceptionInfo *exception)
4667 %
4668 % A description of each parameter follows:
4669 %
4670 % o image: the image.
4671 %
4672 % o threshold: Define the extent of the solarization.
4673 %
4674 % o exception: return any errors or warnings in this structure.
4675 %
4676 */
SolarizeImage(Image * image,const double threshold,ExceptionInfo * exception)4677 MagickExport MagickBooleanType SolarizeImage(Image *image,
4678 const double threshold,ExceptionInfo *exception)
4679 {
4680 #define SolarizeImageTag "Solarize/Image"
4681
4682 CacheView
4683 *image_view;
4684
4685 MagickBooleanType
4686 status;
4687
4688 MagickOffsetType
4689 progress;
4690
4691 ssize_t
4692 y;
4693
4694 assert(image != (Image *) NULL);
4695 assert(image->signature == MagickCoreSignature);
4696 if (image->debug != MagickFalse)
4697 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4698 if (IsGrayColorspace(image->colorspace) != MagickFalse)
4699 (void) SetImageColorspace(image,sRGBColorspace,exception);
4700 if (image->storage_class == PseudoClass)
4701 {
4702 register ssize_t
4703 i;
4704
4705 /*
4706 Solarize colormap.
4707 */
4708 for (i=0; i < (ssize_t) image->colors; i++)
4709 {
4710 if ((double) image->colormap[i].red > threshold)
4711 image->colormap[i].red=QuantumRange-image->colormap[i].red;
4712 if ((double) image->colormap[i].green > threshold)
4713 image->colormap[i].green=QuantumRange-image->colormap[i].green;
4714 if ((double) image->colormap[i].blue > threshold)
4715 image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
4716 }
4717 }
4718 /*
4719 Solarize image.
4720 */
4721 status=MagickTrue;
4722 progress=0;
4723 image_view=AcquireAuthenticCacheView(image,exception);
4724 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4725 #pragma omp parallel for schedule(static) shared(progress,status) \
4726 magick_number_threads(image,image,image->rows,1)
4727 #endif
4728 for (y=0; y < (ssize_t) image->rows; y++)
4729 {
4730 register ssize_t
4731 x;
4732
4733 register Quantum
4734 *magick_restrict q;
4735
4736 if (status == MagickFalse)
4737 continue;
4738 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4739 if (q == (Quantum *) NULL)
4740 {
4741 status=MagickFalse;
4742 continue;
4743 }
4744 for (x=0; x < (ssize_t) image->columns; x++)
4745 {
4746 register ssize_t
4747 i;
4748
4749 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4750 {
4751 PixelChannel channel = GetPixelChannelChannel(image,i);
4752 PixelTrait traits = GetPixelChannelTraits(image,channel);
4753 if ((traits & UpdatePixelTrait) == 0)
4754 continue;
4755 if ((double) q[i] > threshold)
4756 q[i]=QuantumRange-q[i];
4757 }
4758 q+=GetPixelChannels(image);
4759 }
4760 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4761 status=MagickFalse;
4762 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4763 {
4764 MagickBooleanType
4765 proceed;
4766
4767 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4768 #pragma omp atomic
4769 #endif
4770 progress++;
4771 proceed=SetImageProgress(image,SolarizeImageTag,progress,image->rows);
4772 if (proceed == MagickFalse)
4773 status=MagickFalse;
4774 }
4775 }
4776 image_view=DestroyCacheView(image_view);
4777 return(status);
4778 }
4779
4780 /*
4781 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4782 % %
4783 % %
4784 % %
4785 % S t e g a n o I m a g e %
4786 % %
4787 % %
4788 % %
4789 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4790 %
4791 % SteganoImage() hides a digital watermark within the image. Recover
4792 % the hidden watermark later to prove that the authenticity of an image.
4793 % Offset defines the start position within the image to hide the watermark.
4794 %
4795 % The format of the SteganoImage method is:
4796 %
4797 % Image *SteganoImage(const Image *image,Image *watermark,
4798 % ExceptionInfo *exception)
4799 %
4800 % A description of each parameter follows:
4801 %
4802 % o image: the image.
4803 %
4804 % o watermark: the watermark image.
4805 %
4806 % o exception: return any errors or warnings in this structure.
4807 %
4808 */
SteganoImage(const Image * image,const Image * watermark,ExceptionInfo * exception)4809 MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4810 ExceptionInfo *exception)
4811 {
4812 #define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
4813 #define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
4814 | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
4815 #define SteganoImageTag "Stegano/Image"
4816
4817 CacheView
4818 *stegano_view,
4819 *watermark_view;
4820
4821 Image
4822 *stegano_image;
4823
4824 int
4825 c;
4826
4827 MagickBooleanType
4828 status;
4829
4830 PixelInfo
4831 pixel;
4832
4833 register Quantum
4834 *q;
4835
4836 register ssize_t
4837 x;
4838
4839 size_t
4840 depth,
4841 one;
4842
4843 ssize_t
4844 i,
4845 j,
4846 k,
4847 y;
4848
4849 /*
4850 Initialize steganographic image attributes.
4851 */
4852 assert(image != (const Image *) NULL);
4853 assert(image->signature == MagickCoreSignature);
4854 if (image->debug != MagickFalse)
4855 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4856 assert(watermark != (const Image *) NULL);
4857 assert(watermark->signature == MagickCoreSignature);
4858 assert(exception != (ExceptionInfo *) NULL);
4859 assert(exception->signature == MagickCoreSignature);
4860 one=1UL;
4861 stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4862 if (stegano_image == (Image *) NULL)
4863 return((Image *) NULL);
4864 stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4865 if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
4866 {
4867 stegano_image=DestroyImage(stegano_image);
4868 return((Image *) NULL);
4869 }
4870 /*
4871 Hide watermark in low-order bits of image.
4872 */
4873 c=0;
4874 i=0;
4875 j=0;
4876 depth=stegano_image->depth;
4877 k=stegano_image->offset;
4878 status=MagickTrue;
4879 watermark_view=AcquireVirtualCacheView(watermark,exception);
4880 stegano_view=AcquireAuthenticCacheView(stegano_image,exception);
4881 for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
4882 {
4883 for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
4884 {
4885 for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
4886 {
4887 ssize_t
4888 offset;
4889
4890 (void) GetOneCacheViewVirtualPixelInfo(watermark_view,x,y,&pixel,
4891 exception);
4892 offset=k/(ssize_t) stegano_image->columns;
4893 if (offset >= (ssize_t) stegano_image->rows)
4894 break;
4895 q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4896 stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4897 exception);
4898 if (q == (Quantum *) NULL)
4899 break;
4900 switch (c)
4901 {
4902 case 0:
4903 {
4904 SetPixelRed(stegano_image,SetBit(GetPixelRed(stegano_image,q),j,
4905 GetBit(GetPixelInfoIntensity(stegano_image,&pixel),i)),q);
4906 break;
4907 }
4908 case 1:
4909 {
4910 SetPixelGreen(stegano_image,SetBit(GetPixelGreen(stegano_image,q),j,
4911 GetBit(GetPixelInfoIntensity(stegano_image,&pixel),i)),q);
4912 break;
4913 }
4914 case 2:
4915 {
4916 SetPixelBlue(stegano_image,SetBit(GetPixelBlue(stegano_image,q),j,
4917 GetBit(GetPixelInfoIntensity(stegano_image,&pixel),i)),q);
4918 break;
4919 }
4920 }
4921 if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
4922 break;
4923 c++;
4924 if (c == 3)
4925 c=0;
4926 k++;
4927 if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
4928 k=0;
4929 if (k == stegano_image->offset)
4930 j++;
4931 }
4932 }
4933 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4934 {
4935 MagickBooleanType
4936 proceed;
4937
4938 proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4939 (depth-i),depth);
4940 if (proceed == MagickFalse)
4941 status=MagickFalse;
4942 }
4943 }
4944 stegano_view=DestroyCacheView(stegano_view);
4945 watermark_view=DestroyCacheView(watermark_view);
4946 if (status == MagickFalse)
4947 stegano_image=DestroyImage(stegano_image);
4948 return(stegano_image);
4949 }
4950
4951 /*
4952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4953 % %
4954 % %
4955 % %
4956 % S t e r e o A n a g l y p h I m a g e %
4957 % %
4958 % %
4959 % %
4960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4961 %
4962 % StereoAnaglyphImage() combines two images and produces a single image that
4963 % is the composite of a left and right image of a stereo pair. Special
4964 % red-green stereo glasses are required to view this effect.
4965 %
4966 % The format of the StereoAnaglyphImage method is:
4967 %
4968 % Image *StereoImage(const Image *left_image,const Image *right_image,
4969 % ExceptionInfo *exception)
4970 % Image *StereoAnaglyphImage(const Image *left_image,
4971 % const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
4972 % ExceptionInfo *exception)
4973 %
4974 % A description of each parameter follows:
4975 %
4976 % o left_image: the left image.
4977 %
4978 % o right_image: the right image.
4979 %
4980 % o exception: return any errors or warnings in this structure.
4981 %
4982 % o x_offset: amount, in pixels, by which the left image is offset to the
4983 % right of the right image.
4984 %
4985 % o y_offset: amount, in pixels, by which the left image is offset to the
4986 % bottom of the right image.
4987 %
4988 %
4989 */
StereoImage(const Image * left_image,const Image * right_image,ExceptionInfo * exception)4990 MagickExport Image *StereoImage(const Image *left_image,
4991 const Image *right_image,ExceptionInfo *exception)
4992 {
4993 return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4994 }
4995
StereoAnaglyphImage(const Image * left_image,const Image * right_image,const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo * exception)4996 MagickExport Image *StereoAnaglyphImage(const Image *left_image,
4997 const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
4998 ExceptionInfo *exception)
4999 {
5000 #define StereoImageTag "Stereo/Image"
5001
5002 const Image
5003 *image;
5004
5005 Image
5006 *stereo_image;
5007
5008 MagickBooleanType
5009 status;
5010
5011 ssize_t
5012 y;
5013
5014 assert(left_image != (const Image *) NULL);
5015 assert(left_image->signature == MagickCoreSignature);
5016 if (left_image->debug != MagickFalse)
5017 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5018 left_image->filename);
5019 assert(right_image != (const Image *) NULL);
5020 assert(right_image->signature == MagickCoreSignature);
5021 assert(exception != (ExceptionInfo *) NULL);
5022 assert(exception->signature == MagickCoreSignature);
5023 image=left_image;
5024 if ((left_image->columns != right_image->columns) ||
5025 (left_image->rows != right_image->rows))
5026 ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
5027 /*
5028 Initialize stereo image attributes.
5029 */
5030 stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
5031 MagickTrue,exception);
5032 if (stereo_image == (Image *) NULL)
5033 return((Image *) NULL);
5034 if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
5035 {
5036 stereo_image=DestroyImage(stereo_image);
5037 return((Image *) NULL);
5038 }
5039 (void) SetImageColorspace(stereo_image,sRGBColorspace,exception);
5040 /*
5041 Copy left image to red channel and right image to blue channel.
5042 */
5043 status=MagickTrue;
5044 for (y=0; y < (ssize_t) stereo_image->rows; y++)
5045 {
5046 register const Quantum
5047 *magick_restrict p,
5048 *magick_restrict q;
5049
5050 register ssize_t
5051 x;
5052
5053 register Quantum
5054 *magick_restrict r;
5055
5056 p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
5057 exception);
5058 q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
5059 r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
5060 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
5061 (r == (Quantum *) NULL))
5062 break;
5063 for (x=0; x < (ssize_t) stereo_image->columns; x++)
5064 {
5065 SetPixelRed(stereo_image,GetPixelRed(left_image,p),r);
5066 SetPixelGreen(stereo_image,GetPixelGreen(right_image,q),r);
5067 SetPixelBlue(stereo_image,GetPixelBlue(right_image,q),r);
5068 if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
5069 SetPixelAlpha(stereo_image,(GetPixelAlpha(left_image,p)+
5070 GetPixelAlpha(right_image,q))/2,r);
5071 p+=GetPixelChannels(left_image);
5072 q+=GetPixelChannels(right_image);
5073 r+=GetPixelChannels(stereo_image);
5074 }
5075 if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
5076 break;
5077 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5078 {
5079 MagickBooleanType
5080 proceed;
5081
5082 proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
5083 stereo_image->rows);
5084 if (proceed == MagickFalse)
5085 status=MagickFalse;
5086 }
5087 }
5088 if (status == MagickFalse)
5089 stereo_image=DestroyImage(stereo_image);
5090 return(stereo_image);
5091 }
5092
5093 /*
5094 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5095 % %
5096 % %
5097 % %
5098 % S w i r l I m a g e %
5099 % %
5100 % %
5101 % %
5102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5103 %
5104 % SwirlImage() swirls the pixels about the center of the image, where
5105 % degrees indicates the sweep of the arc through which each pixel is moved.
5106 % You get a more dramatic effect as the degrees move from 1 to 360.
5107 %
5108 % The format of the SwirlImage method is:
5109 %
5110 % Image *SwirlImage(const Image *image,double degrees,
5111 % const PixelInterpolateMethod method,ExceptionInfo *exception)
5112 %
5113 % A description of each parameter follows:
5114 %
5115 % o image: the image.
5116 %
5117 % o degrees: Define the tightness of the swirling effect.
5118 %
5119 % o method: the pixel interpolation method.
5120 %
5121 % o exception: return any errors or warnings in this structure.
5122 %
5123 */
SwirlImage(const Image * image,double degrees,const PixelInterpolateMethod method,ExceptionInfo * exception)5124 MagickExport Image *SwirlImage(const Image *image,double degrees,
5125 const PixelInterpolateMethod method,ExceptionInfo *exception)
5126 {
5127 #define SwirlImageTag "Swirl/Image"
5128
5129 CacheView
5130 *canvas_view,
5131 *interpolate_view,
5132 *swirl_view;
5133
5134 double
5135 radius;
5136
5137 Image
5138 *canvas_image,
5139 *swirl_image;
5140
5141 MagickBooleanType
5142 status;
5143
5144 MagickOffsetType
5145 progress;
5146
5147 PointInfo
5148 center,
5149 scale;
5150
5151 ssize_t
5152 y;
5153
5154 /*
5155 Initialize swirl image attributes.
5156 */
5157 assert(image != (const Image *) NULL);
5158 assert(image->signature == MagickCoreSignature);
5159 if (image->debug != MagickFalse)
5160 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5161 assert(exception != (ExceptionInfo *) NULL);
5162 assert(exception->signature == MagickCoreSignature);
5163 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5164 if (canvas_image == (Image *) NULL)
5165 return((Image *) NULL);
5166 if ((canvas_image->alpha_trait == UndefinedPixelTrait) &&
5167 (canvas_image->background_color.alpha != OpaqueAlpha))
5168 (void) SetImageAlphaChannel(canvas_image,OpaqueAlphaChannel,exception);
5169 swirl_image=CloneImage(canvas_image,0,0,MagickTrue,exception);
5170 if (swirl_image == (Image *) NULL)
5171 {
5172 canvas_image=DestroyImage(canvas_image);
5173 return((Image *) NULL);
5174 }
5175 if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
5176 {
5177 canvas_image=DestroyImage(canvas_image);
5178 swirl_image=DestroyImage(swirl_image);
5179 return((Image *) NULL);
5180 }
5181 /*
5182 Compute scaling factor.
5183 */
5184 center.x=(double) canvas_image->columns/2.0;
5185 center.y=(double) canvas_image->rows/2.0;
5186 radius=MagickMax(center.x,center.y);
5187 scale.x=1.0;
5188 scale.y=1.0;
5189 if (canvas_image->columns > canvas_image->rows)
5190 scale.y=(double) canvas_image->columns/(double) canvas_image->rows;
5191 else
5192 if (canvas_image->columns < canvas_image->rows)
5193 scale.x=(double) canvas_image->rows/(double) canvas_image->columns;
5194 degrees=(double) DegreesToRadians(degrees);
5195 /*
5196 Swirl image.
5197 */
5198 status=MagickTrue;
5199 progress=0;
5200 canvas_view=AcquireVirtualCacheView(canvas_image,exception);
5201 interpolate_view=AcquireVirtualCacheView(image,exception);
5202 swirl_view=AcquireAuthenticCacheView(swirl_image,exception);
5203 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5204 #pragma omp parallel for schedule(static) shared(progress,status) \
5205 magick_number_threads(canvas_image,swirl_image,canvas_image->rows,1)
5206 #endif
5207 for (y=0; y < (ssize_t) canvas_image->rows; y++)
5208 {
5209 double
5210 distance;
5211
5212 PointInfo
5213 delta;
5214
5215 register const Quantum
5216 *magick_restrict p;
5217
5218 register ssize_t
5219 x;
5220
5221 register Quantum
5222 *magick_restrict q;
5223
5224 if (status == MagickFalse)
5225 continue;
5226 p=GetCacheViewVirtualPixels(canvas_view,0,y,canvas_image->columns,1,
5227 exception);
5228 q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
5229 exception);
5230 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5231 {
5232 status=MagickFalse;
5233 continue;
5234 }
5235 delta.y=scale.y*(double) (y-center.y);
5236 for (x=0; x < (ssize_t) canvas_image->columns; x++)
5237 {
5238 /*
5239 Determine if the pixel is within an ellipse.
5240 */
5241 delta.x=scale.x*(double) (x-center.x);
5242 distance=delta.x*delta.x+delta.y*delta.y;
5243 if (distance >= (radius*radius))
5244 {
5245 register ssize_t
5246 i;
5247
5248 for (i=0; i < (ssize_t) GetPixelChannels(canvas_image); i++)
5249 {
5250 PixelChannel channel = GetPixelChannelChannel(canvas_image,i);
5251 PixelTrait traits = GetPixelChannelTraits(canvas_image,channel);
5252 PixelTrait swirl_traits = GetPixelChannelTraits(swirl_image,
5253 channel);
5254 if ((traits == UndefinedPixelTrait) ||
5255 (swirl_traits == UndefinedPixelTrait))
5256 continue;
5257 SetPixelChannel(swirl_image,channel,p[i],q);
5258 }
5259 }
5260 else
5261 {
5262 double
5263 cosine,
5264 factor,
5265 sine;
5266
5267 /*
5268 Swirl the pixel.
5269 */
5270 factor=1.0-sqrt((double) distance)/radius;
5271 sine=sin((double) (degrees*factor*factor));
5272 cosine=cos((double) (degrees*factor*factor));
5273 status=InterpolatePixelChannels(canvas_image,interpolate_view,
5274 swirl_image,method,((cosine*delta.x-sine*delta.y)/scale.x+center.x),
5275 (double) ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,
5276 exception);
5277 if (status == MagickFalse)
5278 break;
5279 }
5280 p+=GetPixelChannels(canvas_image);
5281 q+=GetPixelChannels(swirl_image);
5282 }
5283 if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5284 status=MagickFalse;
5285 if (canvas_image->progress_monitor != (MagickProgressMonitor) NULL)
5286 {
5287 MagickBooleanType
5288 proceed;
5289
5290 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5291 #pragma omp atomic
5292 #endif
5293 progress++;
5294 proceed=SetImageProgress(canvas_image,SwirlImageTag,progress,
5295 canvas_image->rows);
5296 if (proceed == MagickFalse)
5297 status=MagickFalse;
5298 }
5299 }
5300 swirl_view=DestroyCacheView(swirl_view);
5301 interpolate_view=DestroyCacheView(interpolate_view);
5302 canvas_view=DestroyCacheView(canvas_view);
5303 canvas_image=DestroyImage(canvas_image);
5304 if (status == MagickFalse)
5305 swirl_image=DestroyImage(swirl_image);
5306 return(swirl_image);
5307 }
5308
5309 /*
5310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5311 % %
5312 % %
5313 % %
5314 % T i n t I m a g e %
5315 % %
5316 % %
5317 % %
5318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5319 %
5320 % TintImage() applies a color vector to each pixel in the image. The length
5321 % of the vector is 0 for black and white and at its maximum for the midtones.
5322 % The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5323 %
5324 % The format of the TintImage method is:
5325 %
5326 % Image *TintImage(const Image *image,const char *blend,
5327 % const PixelInfo *tint,ExceptionInfo *exception)
5328 %
5329 % A description of each parameter follows:
5330 %
5331 % o image: the image.
5332 %
5333 % o blend: A color value used for tinting.
5334 %
5335 % o tint: A color value used for tinting.
5336 %
5337 % o exception: return any errors or warnings in this structure.
5338 %
5339 */
TintImage(const Image * image,const char * blend,const PixelInfo * tint,ExceptionInfo * exception)5340 MagickExport Image *TintImage(const Image *image,const char *blend,
5341 const PixelInfo *tint,ExceptionInfo *exception)
5342 {
5343 #define TintImageTag "Tint/Image"
5344
5345 CacheView
5346 *image_view,
5347 *tint_view;
5348
5349 double
5350 intensity;
5351
5352 GeometryInfo
5353 geometry_info;
5354
5355 Image
5356 *tint_image;
5357
5358 MagickBooleanType
5359 status;
5360
5361 MagickOffsetType
5362 progress;
5363
5364 PixelInfo
5365 color_vector;
5366
5367 MagickStatusType
5368 flags;
5369
5370 ssize_t
5371 y;
5372
5373 /*
5374 Allocate tint image.
5375 */
5376 assert(image != (const Image *) NULL);
5377 assert(image->signature == MagickCoreSignature);
5378 if (image->debug != MagickFalse)
5379 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5380 assert(exception != (ExceptionInfo *) NULL);
5381 assert(exception->signature == MagickCoreSignature);
5382 tint_image=CloneImage(image,0,0,MagickTrue,exception);
5383 if (tint_image == (Image *) NULL)
5384 return((Image *) NULL);
5385 if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
5386 {
5387 tint_image=DestroyImage(tint_image);
5388 return((Image *) NULL);
5389 }
5390 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
5391 (IsPixelInfoGray(tint) == MagickFalse))
5392 (void) SetImageColorspace(tint_image,sRGBColorspace,exception);
5393 if (blend == (const char *) NULL)
5394 return(tint_image);
5395 /*
5396 Determine RGB values of the color.
5397 */
5398 GetPixelInfo(image,&color_vector);
5399 flags=ParseGeometry(blend,&geometry_info);
5400 color_vector.red=geometry_info.rho;
5401 color_vector.green=geometry_info.rho;
5402 color_vector.blue=geometry_info.rho;
5403 color_vector.alpha=(MagickRealType) OpaqueAlpha;
5404 if ((flags & SigmaValue) != 0)
5405 color_vector.green=geometry_info.sigma;
5406 if ((flags & XiValue) != 0)
5407 color_vector.blue=geometry_info.xi;
5408 if ((flags & PsiValue) != 0)
5409 color_vector.alpha=geometry_info.psi;
5410 if (image->colorspace == CMYKColorspace)
5411 {
5412 color_vector.black=geometry_info.rho;
5413 if ((flags & PsiValue) != 0)
5414 color_vector.black=geometry_info.psi;
5415 if ((flags & ChiValue) != 0)
5416 color_vector.alpha=geometry_info.chi;
5417 }
5418 intensity=(double) GetPixelInfoIntensity((const Image *) NULL,tint);
5419 color_vector.red=(double) (color_vector.red*tint->red/100.0-intensity);
5420 color_vector.green=(double) (color_vector.green*tint->green/100.0-intensity);
5421 color_vector.blue=(double) (color_vector.blue*tint->blue/100.0-intensity);
5422 color_vector.black=(double) (color_vector.black*tint->black/100.0-intensity);
5423 color_vector.alpha=(double) (color_vector.alpha*tint->alpha/100.0-intensity);
5424 /*
5425 Tint image.
5426 */
5427 status=MagickTrue;
5428 progress=0;
5429 image_view=AcquireVirtualCacheView(image,exception);
5430 tint_view=AcquireAuthenticCacheView(tint_image,exception);
5431 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5432 #pragma omp parallel for schedule(static) shared(progress,status) \
5433 magick_number_threads(image,tint_image,image->rows,1)
5434 #endif
5435 for (y=0; y < (ssize_t) image->rows; y++)
5436 {
5437 register const Quantum
5438 *magick_restrict p;
5439
5440 register Quantum
5441 *magick_restrict q;
5442
5443 register ssize_t
5444 x;
5445
5446 if (status == MagickFalse)
5447 continue;
5448 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5449 q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5450 exception);
5451 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5452 {
5453 status=MagickFalse;
5454 continue;
5455 }
5456 for (x=0; x < (ssize_t) image->columns; x++)
5457 {
5458 PixelInfo
5459 pixel;
5460
5461 double
5462 weight;
5463
5464 GetPixelInfo(image,&pixel);
5465 weight=QuantumScale*GetPixelRed(image,p)-0.5;
5466 pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5467 (1.0-(4.0*(weight*weight)));
5468 weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5469 pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5470 (1.0-(4.0*(weight*weight)));
5471 weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5472 pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5473 (1.0-(4.0*(weight*weight)));
5474 weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5475 pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5476 (1.0-(4.0*(weight*weight)));
5477 pixel.alpha=(MagickRealType) GetPixelAlpha(image,p);
5478 SetPixelViaPixelInfo(tint_image,&pixel,q);
5479 p+=GetPixelChannels(image);
5480 q+=GetPixelChannels(tint_image);
5481 }
5482 if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5483 status=MagickFalse;
5484 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5485 {
5486 MagickBooleanType
5487 proceed;
5488
5489 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5490 #pragma omp atomic
5491 #endif
5492 progress++;
5493 proceed=SetImageProgress(image,TintImageTag,progress,image->rows);
5494 if (proceed == MagickFalse)
5495 status=MagickFalse;
5496 }
5497 }
5498 tint_view=DestroyCacheView(tint_view);
5499 image_view=DestroyCacheView(image_view);
5500 if (status == MagickFalse)
5501 tint_image=DestroyImage(tint_image);
5502 return(tint_image);
5503 }
5504
5505 /*
5506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5507 % %
5508 % %
5509 % %
5510 % V i g n e t t e I m a g e %
5511 % %
5512 % %
5513 % %
5514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5515 %
5516 % VignetteImage() softens the edges of the image in vignette style.
5517 %
5518 % The format of the VignetteImage method is:
5519 %
5520 % Image *VignetteImage(const Image *image,const double radius,
5521 % const double sigma,const ssize_t x,const ssize_t y,
5522 % ExceptionInfo *exception)
5523 %
5524 % A description of each parameter follows:
5525 %
5526 % o image: the image.
5527 %
5528 % o radius: the radius of the pixel neighborhood.
5529 %
5530 % o sigma: the standard deviation of the Gaussian, in pixels.
5531 %
5532 % o x, y: Define the x and y ellipse offset.
5533 %
5534 % o exception: return any errors or warnings in this structure.
5535 %
5536 */
VignetteImage(const Image * image,const double radius,const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo * exception)5537 MagickExport Image *VignetteImage(const Image *image,const double radius,
5538 const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
5539 {
5540 char
5541 ellipse[MagickPathExtent];
5542
5543 DrawInfo
5544 *draw_info;
5545
5546 Image
5547 *canvas,
5548 *blur_image,
5549 *oval_image,
5550 *vignette_image;
5551
5552 assert(image != (Image *) NULL);
5553 assert(image->signature == MagickCoreSignature);
5554 if (image->debug != MagickFalse)
5555 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5556 assert(exception != (ExceptionInfo *) NULL);
5557 assert(exception->signature == MagickCoreSignature);
5558 canvas=CloneImage(image,0,0,MagickTrue,exception);
5559 if (canvas == (Image *) NULL)
5560 return((Image *) NULL);
5561 if (SetImageStorageClass(canvas,DirectClass,exception) == MagickFalse)
5562 {
5563 canvas=DestroyImage(canvas);
5564 return((Image *) NULL);
5565 }
5566 canvas->alpha_trait=BlendPixelTrait;
5567 oval_image=CloneImage(canvas,canvas->columns,canvas->rows,MagickTrue,
5568 exception);
5569 if (oval_image == (Image *) NULL)
5570 {
5571 canvas=DestroyImage(canvas);
5572 return((Image *) NULL);
5573 }
5574 (void) QueryColorCompliance("#000000",AllCompliance,
5575 &oval_image->background_color,exception);
5576 (void) SetImageBackgroundColor(oval_image,exception);
5577 draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5578 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5579 exception);
5580 (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5581 exception);
5582 (void) FormatLocaleString(ellipse,MagickPathExtent,"ellipse %g,%g,%g,%g,"
5583 "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5584 image->rows/2.0-y);
5585 draw_info->primitive=AcquireString(ellipse);
5586 (void) DrawImage(oval_image,draw_info,exception);
5587 draw_info=DestroyDrawInfo(draw_info);
5588 blur_image=BlurImage(oval_image,radius,sigma,exception);
5589 oval_image=DestroyImage(oval_image);
5590 if (blur_image == (Image *) NULL)
5591 {
5592 canvas=DestroyImage(canvas);
5593 return((Image *) NULL);
5594 }
5595 blur_image->alpha_trait=UndefinedPixelTrait;
5596 (void) CompositeImage(canvas,blur_image,IntensityCompositeOp,MagickTrue,
5597 0,0,exception);
5598 blur_image=DestroyImage(blur_image);
5599 vignette_image=MergeImageLayers(canvas,FlattenLayer,exception);
5600 canvas=DestroyImage(canvas);
5601 if (vignette_image != (Image *) NULL)
5602 (void) TransformImageColorspace(vignette_image,image->colorspace,exception);
5603 return(vignette_image);
5604 }
5605
5606 /*
5607 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5608 % %
5609 % %
5610 % %
5611 % W a v e I m a g e %
5612 % %
5613 % %
5614 % %
5615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5616 %
5617 % WaveImage() creates a "ripple" effect in the image by shifting the pixels
5618 % vertically along a sine wave whose amplitude and wavelength is specified
5619 % by the given parameters.
5620 %
5621 % The format of the WaveImage method is:
5622 %
5623 % Image *WaveImage(const Image *image,const double amplitude,
5624 % const double wave_length,const PixelInterpolateMethod method,
5625 % ExceptionInfo *exception)
5626 %
5627 % A description of each parameter follows:
5628 %
5629 % o image: the image.
5630 %
5631 % o amplitude, wave_length: Define the amplitude and wave length of the
5632 % sine wave.
5633 %
5634 % o interpolate: the pixel interpolation method.
5635 %
5636 % o exception: return any errors or warnings in this structure.
5637 %
5638 */
WaveImage(const Image * image,const double amplitude,const double wave_length,const PixelInterpolateMethod method,ExceptionInfo * exception)5639 MagickExport Image *WaveImage(const Image *image,const double amplitude,
5640 const double wave_length,const PixelInterpolateMethod method,
5641 ExceptionInfo *exception)
5642 {
5643 #define WaveImageTag "Wave/Image"
5644
5645 CacheView
5646 *canvas_image_view,
5647 *wave_view;
5648
5649 Image
5650 *canvas_image,
5651 *wave_image;
5652
5653 MagickBooleanType
5654 status;
5655
5656 MagickOffsetType
5657 progress;
5658
5659 double
5660 *sine_map;
5661
5662 register ssize_t
5663 i;
5664
5665 ssize_t
5666 y;
5667
5668 /*
5669 Initialize wave image attributes.
5670 */
5671 assert(image != (Image *) NULL);
5672 assert(image->signature == MagickCoreSignature);
5673 if (image->debug != MagickFalse)
5674 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5675 assert(exception != (ExceptionInfo *) NULL);
5676 assert(exception->signature == MagickCoreSignature);
5677 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5678 if (canvas_image == (Image *) NULL)
5679 return((Image *) NULL);
5680 if ((canvas_image->alpha_trait == UndefinedPixelTrait) &&
5681 (canvas_image->background_color.alpha != OpaqueAlpha))
5682 (void) SetImageAlpha(canvas_image,OpaqueAlpha,exception);
5683 wave_image=CloneImage(canvas_image,canvas_image->columns,(size_t)
5684 (canvas_image->rows+2.0*fabs(amplitude)),MagickTrue,exception);
5685 if (wave_image == (Image *) NULL)
5686 {
5687 canvas_image=DestroyImage(canvas_image);
5688 return((Image *) NULL);
5689 }
5690 if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
5691 {
5692 canvas_image=DestroyImage(canvas_image);
5693 wave_image=DestroyImage(wave_image);
5694 return((Image *) NULL);
5695 }
5696 /*
5697 Allocate sine map.
5698 */
5699 sine_map=(double *) AcquireQuantumMemory((size_t) wave_image->columns,
5700 sizeof(*sine_map));
5701 if (sine_map == (double *) NULL)
5702 {
5703 canvas_image=DestroyImage(canvas_image);
5704 wave_image=DestroyImage(wave_image);
5705 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5706 }
5707 for (i=0; i < (ssize_t) wave_image->columns; i++)
5708 sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5709 wave_length));
5710 /*
5711 Wave image.
5712 */
5713 status=MagickTrue;
5714 progress=0;
5715 canvas_image_view=AcquireVirtualCacheView(canvas_image,exception);
5716 wave_view=AcquireAuthenticCacheView(wave_image,exception);
5717 (void) SetCacheViewVirtualPixelMethod(canvas_image_view,
5718 BackgroundVirtualPixelMethod);
5719 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5720 #pragma omp parallel for schedule(static) shared(progress,status) \
5721 magick_number_threads(canvas_image,wave_image,wave_image->rows,1)
5722 #endif
5723 for (y=0; y < (ssize_t) wave_image->rows; y++)
5724 {
5725 register const Quantum
5726 *magick_restrict p;
5727
5728 register Quantum
5729 *magick_restrict q;
5730
5731 register ssize_t
5732 x;
5733
5734 if (status == MagickFalse)
5735 continue;
5736 p=GetCacheViewVirtualPixels(canvas_image_view,0,y,canvas_image->columns,1,
5737 exception);
5738 q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5739 exception);
5740 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5741 {
5742 status=MagickFalse;
5743 continue;
5744 }
5745 for (x=0; x < (ssize_t) wave_image->columns; x++)
5746 {
5747 status=InterpolatePixelChannels(canvas_image,canvas_image_view,
5748 wave_image,method,(double) x,(double) (y-sine_map[x]),q,exception);
5749 if (status == MagickFalse)
5750 break;
5751 p+=GetPixelChannels(canvas_image);
5752 q+=GetPixelChannels(wave_image);
5753 }
5754 if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5755 status=MagickFalse;
5756 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5757 {
5758 MagickBooleanType
5759 proceed;
5760
5761 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5762 #pragma omp atomic
5763 #endif
5764 progress++;
5765 proceed=SetImageProgress(canvas_image,WaveImageTag,progress,
5766 canvas_image->rows);
5767 if (proceed == MagickFalse)
5768 status=MagickFalse;
5769 }
5770 }
5771 wave_view=DestroyCacheView(wave_view);
5772 canvas_image_view=DestroyCacheView(canvas_image_view);
5773 canvas_image=DestroyImage(canvas_image);
5774 sine_map=(double *) RelinquishMagickMemory(sine_map);
5775 if (status == MagickFalse)
5776 wave_image=DestroyImage(wave_image);
5777 return(wave_image);
5778 }
5779
5780 /*
5781 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5782 % %
5783 % %
5784 % %
5785 % W a v e l e t D e n o i s e I m a g e %
5786 % %
5787 % %
5788 % %
5789 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5790 %
5791 % WaveletDenoiseImage() removes noise from the image using a wavelet
5792 % transform. The wavelet transform is a fast hierarchical scheme for
5793 % processing an image using a set of consecutive lowpass and high_pass filters,
5794 % followed by a decimation. This results in a decomposition into different
5795 % scales which can be regarded as different “frequency bands”, determined by
5796 % the mother wavelet. Adapted from dcraw.c by David Coffin.
5797 %
5798 % The format of the WaveletDenoiseImage method is:
5799 %
5800 % Image *WaveletDenoiseImage(const Image *image,const double threshold,
5801 % const double softness,ExceptionInfo *exception)
5802 %
5803 % A description of each parameter follows:
5804 %
5805 % o image: the image.
5806 %
5807 % o threshold: set the threshold for smoothing.
5808 %
5809 % o softness: attenuate the smoothing threshold.
5810 %
5811 % o exception: return any errors or warnings in this structure.
5812 %
5813 */
5814
HatTransform(const float * magick_restrict pixels,const size_t stride,const size_t extent,const size_t scale,float * kernel)5815 static inline void HatTransform(const float *magick_restrict pixels,
5816 const size_t stride,const size_t extent,const size_t scale,float *kernel)
5817 {
5818 const float
5819 *magick_restrict p,
5820 *magick_restrict q,
5821 *magick_restrict r;
5822
5823 register ssize_t
5824 i;
5825
5826 p=pixels;
5827 q=pixels+scale*stride;
5828 r=pixels+scale*stride;
5829 for (i=0; i < (ssize_t) scale; i++)
5830 {
5831 kernel[i]=0.25f*(*p+(*p)+(*q)+(*r));
5832 p+=stride;
5833 q-=stride;
5834 r+=stride;
5835 }
5836 for ( ; i < (ssize_t) (extent-scale); i++)
5837 {
5838 kernel[i]=0.25f*(2.0f*(*p)+*(p-scale*stride)+*(p+scale*stride));
5839 p+=stride;
5840 }
5841 q=p-scale*stride;
5842 r=pixels+stride*(extent-2);
5843 for ( ; i < (ssize_t) extent; i++)
5844 {
5845 kernel[i]=0.25f*(*p+(*p)+(*q)+(*r));
5846 p+=stride;
5847 q+=stride;
5848 r-=stride;
5849 }
5850 }
5851
WaveletDenoiseImage(const Image * image,const double threshold,const double softness,ExceptionInfo * exception)5852 MagickExport Image *WaveletDenoiseImage(const Image *image,
5853 const double threshold,const double softness,ExceptionInfo *exception)
5854 {
5855 CacheView
5856 *image_view,
5857 *noise_view;
5858
5859 float
5860 *kernel,
5861 *pixels;
5862
5863 Image
5864 *noise_image;
5865
5866 MagickBooleanType
5867 status;
5868
5869 MagickSizeType
5870 number_pixels;
5871
5872 MemoryInfo
5873 *pixels_info;
5874
5875 ssize_t
5876 channel;
5877
5878 static const float
5879 noise_levels[] = { 0.8002f, 0.2735f, 0.1202f, 0.0585f, 0.0291f, 0.0152f,
5880 0.0080f, 0.0044f };
5881
5882 /*
5883 Initialize noise image attributes.
5884 */
5885 assert(image != (const Image *) NULL);
5886 assert(image->signature == MagickCoreSignature);
5887 if (image->debug != MagickFalse)
5888 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5889 assert(exception != (ExceptionInfo *) NULL);
5890 assert(exception->signature == MagickCoreSignature);
5891 #if defined(MAGICKCORE_OPENCL_SUPPORT)
5892 noise_image=AccelerateWaveletDenoiseImage(image,threshold,exception);
5893 if (noise_image != (Image *) NULL)
5894 return(noise_image);
5895 #endif
5896 noise_image=CloneImage(image,0,0,MagickTrue,exception);
5897 if (noise_image == (Image *) NULL)
5898 return((Image *) NULL);
5899 if (SetImageStorageClass(noise_image,DirectClass,exception) == MagickFalse)
5900 {
5901 noise_image=DestroyImage(noise_image);
5902 return((Image *) NULL);
5903 }
5904 if (AcquireMagickResource(WidthResource,4*image->columns) == MagickFalse)
5905 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5906 pixels_info=AcquireVirtualMemory(3*image->columns,image->rows*
5907 sizeof(*pixels));
5908 kernel=(float *) AcquireQuantumMemory(MagickMax(image->rows,image->columns)+1,
5909 GetOpenMPMaximumThreads()*sizeof(*kernel));
5910 if ((pixels_info == (MemoryInfo *) NULL) || (kernel == (float *) NULL))
5911 {
5912 if (kernel != (float *) NULL)
5913 kernel=(float *) RelinquishMagickMemory(kernel);
5914 if (pixels_info != (MemoryInfo *) NULL)
5915 pixels_info=RelinquishVirtualMemory(pixels_info);
5916 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5917 }
5918 pixels=(float *) GetVirtualMemoryBlob(pixels_info);
5919 status=MagickTrue;
5920 number_pixels=(MagickSizeType) image->columns*image->rows;
5921 image_view=AcquireAuthenticCacheView(image,exception);
5922 noise_view=AcquireAuthenticCacheView(noise_image,exception);
5923 for (channel=0; channel < (ssize_t) GetPixelChannels(image); channel++)
5924 {
5925 register ssize_t
5926 i;
5927
5928 size_t
5929 high_pass,
5930 low_pass;
5931
5932 ssize_t
5933 level,
5934 y;
5935
5936 PixelChannel
5937 pixel_channel;
5938
5939 PixelTrait
5940 traits;
5941
5942 if (status == MagickFalse)
5943 continue;
5944 traits=GetPixelChannelTraits(image,(PixelChannel) channel);
5945 if (traits == UndefinedPixelTrait)
5946 continue;
5947 pixel_channel=GetPixelChannelChannel(image,channel);
5948 if ((pixel_channel != RedPixelChannel) &&
5949 (pixel_channel != GreenPixelChannel) &&
5950 (pixel_channel != BluePixelChannel))
5951 continue;
5952 /*
5953 Copy channel from image to wavelet pixel array.
5954 */
5955 i=0;
5956 for (y=0; y < (ssize_t) image->rows; y++)
5957 {
5958 register const Quantum
5959 *magick_restrict p;
5960
5961 ssize_t
5962 x;
5963
5964 p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
5965 if (p == (const Quantum *) NULL)
5966 {
5967 status=MagickFalse;
5968 break;
5969 }
5970 for (x=0; x < (ssize_t) image->columns; x++)
5971 {
5972 pixels[i++]=(float) p[channel];
5973 p+=GetPixelChannels(image);
5974 }
5975 }
5976 /*
5977 Low pass filter outputs are called approximation kernel & high pass
5978 filters are referred to as detail kernel. The detail kernel
5979 have high values in the noisy parts of the signal.
5980 */
5981 high_pass=0;
5982 for (level=0; level < 5; level++)
5983 {
5984 double
5985 magnitude;
5986
5987 ssize_t
5988 x,
5989 y;
5990
5991 low_pass=(size_t) (number_pixels*((level & 0x01)+1));
5992 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5993 #pragma omp parallel for schedule(static,1) \
5994 magick_number_threads(image,image,image->rows,1)
5995 #endif
5996 for (y=0; y < (ssize_t) image->rows; y++)
5997 {
5998 const int
5999 id = GetOpenMPThreadId();
6000
6001 register float
6002 *magick_restrict p,
6003 *magick_restrict q;
6004
6005 register ssize_t
6006 x;
6007
6008 p=kernel+id*image->columns;
6009 q=pixels+y*image->columns;
6010 HatTransform(q+high_pass,1,image->columns,(size_t) (1UL << level),p);
6011 q+=low_pass;
6012 for (x=0; x < (ssize_t) image->columns; x++)
6013 *q++=(*p++);
6014 }
6015 #if defined(MAGICKCORE_OPENMP_SUPPORT)
6016 #pragma omp parallel for schedule(static,1) \
6017 magick_number_threads(image,image,image->columns,1)
6018 #endif
6019 for (x=0; x < (ssize_t) image->columns; x++)
6020 {
6021 const int
6022 id = GetOpenMPThreadId();
6023
6024 register float
6025 *magick_restrict p,
6026 *magick_restrict q;
6027
6028 register ssize_t
6029 y;
6030
6031 p=kernel+id*image->rows;
6032 q=pixels+x+low_pass;
6033 HatTransform(q,image->columns,image->rows,(size_t) (1UL << level),p);
6034 for (y=0; y < (ssize_t) image->rows; y++)
6035 {
6036 *q=(*p++);
6037 q+=image->columns;
6038 }
6039 }
6040 /*
6041 To threshold, each coefficient is compared to a threshold value and
6042 attenuated / shrunk by some factor.
6043 */
6044 magnitude=threshold*noise_levels[level];
6045 for (i=0; i < (ssize_t) number_pixels; ++i)
6046 {
6047 pixels[high_pass+i]-=pixels[low_pass+i];
6048 if (pixels[high_pass+i] < -magnitude)
6049 pixels[high_pass+i]+=magnitude-softness*magnitude;
6050 else
6051 if (pixels[high_pass+i] > magnitude)
6052 pixels[high_pass+i]-=magnitude-softness*magnitude;
6053 else
6054 pixels[high_pass+i]*=softness;
6055 if (high_pass != 0)
6056 pixels[i]+=pixels[high_pass+i];
6057 }
6058 high_pass=low_pass;
6059 }
6060 /*
6061 Reconstruct image from the thresholded wavelet kernel.
6062 */
6063 i=0;
6064 for (y=0; y < (ssize_t) image->rows; y++)
6065 {
6066 MagickBooleanType
6067 sync;
6068
6069 register Quantum
6070 *magick_restrict q;
6071
6072 register ssize_t
6073 x;
6074
6075 ssize_t
6076 offset;
6077
6078 q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
6079 exception);
6080 if (q == (Quantum *) NULL)
6081 {
6082 status=MagickFalse;
6083 break;
6084 }
6085 offset=GetPixelChannelOffset(noise_image,pixel_channel);
6086 for (x=0; x < (ssize_t) image->columns; x++)
6087 {
6088 MagickRealType
6089 pixel;
6090
6091 pixel=(MagickRealType) pixels[i]+pixels[low_pass+i];
6092 q[offset]=ClampToQuantum(pixel);
6093 i++;
6094 q+=GetPixelChannels(noise_image);
6095 }
6096 sync=SyncCacheViewAuthenticPixels(noise_view,exception);
6097 if (sync == MagickFalse)
6098 status=MagickFalse;
6099 }
6100 if (image->progress_monitor != (MagickProgressMonitor) NULL)
6101 {
6102 MagickBooleanType
6103 proceed;
6104
6105 proceed=SetImageProgress(image,AddNoiseImageTag,(MagickOffsetType)
6106 channel,GetPixelChannels(image));
6107 if (proceed == MagickFalse)
6108 status=MagickFalse;
6109 }
6110 }
6111 noise_view=DestroyCacheView(noise_view);
6112 image_view=DestroyCacheView(image_view);
6113 kernel=(float *) RelinquishMagickMemory(kernel);
6114 pixels_info=RelinquishVirtualMemory(pixels_info);
6115 if (status == MagickFalse)
6116 noise_image=DestroyImage(noise_image);
6117 return(noise_image);
6118 }
6119