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 112struct _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*/ 165MagickPrivate 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*/ 270MagickExport 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*/ 441MagickExport 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*/ 600MagickExport 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*/ 665MagickExport 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 853MagickExport 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*/ 1060MagickPrivate 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 1114static 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 1226static double 1227 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t, 1228 const ssize_t,const char *,size_t *,double *,ExceptionInfo *); 1229 1230static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta) 1231{ 1232 if (beta != 0) 1233 return(FxGCD(beta,alpha % beta)); 1234 return(alpha); 1235} 1236 1237static 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 1264static 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 1845static 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 2102static 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 2943MagickPrivate 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 2954MagickExport 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 2971MagickPrivate 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 3017static 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 3030static 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 3079MagickExport 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*/ 3246MagickExport 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*/ 3452MagickExport 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 3693static 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 3708static 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 3917MagickExport 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*/ 3988MagickExport 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*/ 4177MagickExport 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*/ 4331MagickExport 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*/ 4480MagickExport 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*/ 4642MagickExport 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*/ 4779MagickExport 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*/ 4961MagickExport 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 4967MagickExport 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*/ 5097MagickExport 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*/ 5304MagickExport 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*/ 5518MagickExport 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*/ 5621MagickExport 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 5776static 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 5813MagickExport 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