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