1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE % 7 % C O O MM MM P P O O SS I T E % 8 % C O O M M M PPPP O O SSS I T EEE % 9 % C O O M M P O O SS I T E % 10 % CCCC OOO M M P OOO SSSSS IIIII T EEEEE % 11 % % 12 % % 13 % MagickCore Image Composite Methods % 14 % % 15 % Software Design % 16 % Cristy % 17 % July 1992 % 18 % % 19 % % 20 % Copyright 1999-2019 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 % https://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 /* 42 Include declarations. 43 */ 44 #include "MagickCore/studio.h" 45 #include "MagickCore/artifact.h" 46 #include "MagickCore/cache.h" 47 #include "MagickCore/cache-private.h" 48 #include "MagickCore/cache-view.h" 49 #include "MagickCore/channel.h" 50 #include "MagickCore/client.h" 51 #include "MagickCore/color.h" 52 #include "MagickCore/color-private.h" 53 #include "MagickCore/colorspace.h" 54 #include "MagickCore/colorspace-private.h" 55 #include "MagickCore/composite.h" 56 #include "MagickCore/composite-private.h" 57 #include "MagickCore/constitute.h" 58 #include "MagickCore/draw.h" 59 #include "MagickCore/fx.h" 60 #include "MagickCore/gem.h" 61 #include "MagickCore/geometry.h" 62 #include "MagickCore/image.h" 63 #include "MagickCore/image-private.h" 64 #include "MagickCore/list.h" 65 #include "MagickCore/log.h" 66 #include "MagickCore/monitor.h" 67 #include "MagickCore/monitor-private.h" 68 #include "MagickCore/memory_.h" 69 #include "MagickCore/option.h" 70 #include "MagickCore/pixel-accessor.h" 71 #include "MagickCore/property.h" 72 #include "MagickCore/quantum.h" 73 #include "MagickCore/resample.h" 74 #include "MagickCore/resource_.h" 75 #include "MagickCore/string_.h" 76 #include "MagickCore/thread-private.h" 77 #include "MagickCore/threshold.h" 78 #include "MagickCore/token.h" 79 #include "MagickCore/utility.h" 80 #include "MagickCore/utility-private.h" 81 #include "MagickCore/version.h" 82 83 /* 85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 86 % % 87 % % 88 % % 89 % C o m p o s i t e I m a g e % 90 % % 91 % % 92 % % 93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 94 % 95 % CompositeImage() returns the second image composited onto the first 96 % at the specified offset, using the specified composite method. 97 % 98 % The format of the CompositeImage method is: 99 % 100 % MagickBooleanType CompositeImage(Image *image, 101 % const Image *source_image,const CompositeOperator compose, 102 % const MagickBooleanType clip_to_self,const ssize_t x_offset, 103 % const ssize_t y_offset,ExceptionInfo *exception) 104 % 105 % A description of each parameter follows: 106 % 107 % o image: the canvas image, modified by he composition 108 % 109 % o source_image: the source image. 110 % 111 % o compose: This operator affects how the composite is applied to 112 % the image. The operators and how they are utilized are listed here 113 % http://www.w3.org/TR/SVG12/#compositing. 114 % 115 % o clip_to_self: set to MagickTrue to limit composition to area composed. 116 % 117 % o x_offset: the column offset of the composited image. 118 % 119 % o y_offset: the row offset of the composited image. 120 % 121 % Extra Controls from Image meta-data in 'image' (artifacts) 122 % 123 % o "compose:args" 124 % A string containing extra numerical arguments for specific compose 125 % methods, generally expressed as a 'geometry' or a comma separated list 126 % of numbers. 127 % 128 % Compose methods needing such arguments include "BlendCompositeOp" and 129 % "DisplaceCompositeOp". 130 % 131 % o exception: return any errors or warnings in this structure. 132 % 133 */ 134 135 /* 136 Composition based on the SVG specification: 137 138 A Composition is defined by... 139 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors 140 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc) 141 Y = 1 for source preserved 142 Z = 1 for canvas preserved 143 144 Conversion to transparency (then optimized) 145 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa) 146 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa) 147 148 Where... 149 Sca = Sc*Sa normalized Source color divided by Source alpha 150 Dca = Dc*Da normalized Dest color divided by Dest alpha 151 Dc' = Dca'/Da' the desired color value for this channel. 152 153 Da' in in the follow formula as 'gamma' The resulting alpla value. 154 155 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in 156 the following optimizations... 157 gamma = Sa+Da-Sa*Da; 158 gamma = 1 - QuantumScale*alpha * QuantumScale*beta; 159 opacity = QuantumScale*alpha*beta; // over blend, optimized 1-Gamma 160 161 The above SVG definitions also define that Mathematical Composition 162 methods should use a 'Over' blending mode for Alpha Channel. 163 It however was not applied for composition modes of 'Plus', 'Minus', 164 the modulus versions of 'Add' and 'Subtract'. 165 166 Mathematical operator changes to be applied from IM v6.7... 167 168 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed 169 'ModulusAdd' and 'ModulusSubtract' for clarity. 170 171 2) All mathematical compositions work as per the SVG specification 172 with regard to blending. This now includes 'ModulusAdd' and 173 'ModulusSubtract'. 174 175 3) When the special channel flag 'sync' (syncronize channel updates) 176 is turned off (enabled by default) then mathematical compositions are 177 only performed on the channels specified, and are applied 178 independantally of each other. In other words the mathematics is 179 performed as 'pure' mathematical operations, rather than as image 180 operations. 181 */ 182 183 static void HCLComposite(const MagickRealType hue,const MagickRealType chroma, 184 const MagickRealType luma,MagickRealType *red,MagickRealType *green, 185 MagickRealType *blue) 186 { 187 MagickRealType 188 b, 189 c, 190 g, 191 h, 192 m, 193 r, 194 x; 195 196 /* 197 Convert HCL to RGB colorspace. 198 */ 199 assert(red != (MagickRealType *) NULL); 200 assert(green != (MagickRealType *) NULL); 201 assert(blue != (MagickRealType *) NULL); 202 h=6.0*hue; 203 c=chroma; 204 x=c*(1.0-fabs(fmod(h,2.0)-1.0)); 205 r=0.0; 206 g=0.0; 207 b=0.0; 208 if ((0.0 <= h) && (h < 1.0)) 209 { 210 r=c; 211 g=x; 212 } 213 else 214 if ((1.0 <= h) && (h < 2.0)) 215 { 216 r=x; 217 g=c; 218 } 219 else 220 if ((2.0 <= h) && (h < 3.0)) 221 { 222 g=c; 223 b=x; 224 } 225 else 226 if ((3.0 <= h) && (h < 4.0)) 227 { 228 g=x; 229 b=c; 230 } 231 else 232 if ((4.0 <= h) && (h < 5.0)) 233 { 234 r=x; 235 b=c; 236 } 237 else 238 if ((5.0 <= h) && (h < 6.0)) 239 { 240 r=c; 241 b=x; 242 } 243 m=luma-(0.298839*r+0.586811*g+0.114350*b); 244 *red=QuantumRange*(r+m); 245 *green=QuantumRange*(g+m); 246 *blue=QuantumRange*(b+m); 247 } 248 249 static void CompositeHCL(const MagickRealType red,const MagickRealType green, 250 const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma, 251 MagickRealType *luma) 252 { 253 MagickRealType 254 b, 255 c, 256 g, 257 h, 258 max, 259 r; 260 261 /* 262 Convert RGB to HCL colorspace. 263 */ 264 assert(hue != (MagickRealType *) NULL); 265 assert(chroma != (MagickRealType *) NULL); 266 assert(luma != (MagickRealType *) NULL); 267 r=red; 268 g=green; 269 b=blue; 270 max=MagickMax(r,MagickMax(g,b)); 271 c=max-(MagickRealType) MagickMin(r,MagickMin(g,b)); 272 h=0.0; 273 if (c == 0) 274 h=0.0; 275 else 276 if (red == max) 277 h=fmod((g-b)/c+6.0,6.0); 278 else 279 if (green == max) 280 h=((b-r)/c)+2.0; 281 else 282 if (blue == max) 283 h=((r-g)/c)+4.0; 284 *hue=(h/6.0); 285 *chroma=QuantumScale*c; 286 *luma=QuantumScale*(0.298839*r+0.586811*g+0.114350*b); 287 } 288 289 static MagickBooleanType CompositeOverImage(Image *image, 290 const Image *source_image,const MagickBooleanType clip_to_self, 291 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception) 292 { 293 #define CompositeImageTag "Composite/Image" 294 295 CacheView 296 *image_view, 297 *source_view; 298 299 const char 300 *value; 301 302 MagickBooleanType 303 clamp, 304 status; 305 306 MagickOffsetType 307 progress; 308 309 ssize_t 310 y; 311 312 /* 313 Composite image. 314 */ 315 status=MagickTrue; 316 progress=0; 317 clamp=MagickTrue; 318 value=GetImageArtifact(image,"compose:clamp"); 319 if (value != (const char *) NULL) 320 clamp=IsStringTrue(value); 321 status=MagickTrue; 322 progress=0; 323 source_view=AcquireVirtualCacheView(source_image,exception); 324 image_view=AcquireAuthenticCacheView(image,exception); 325 #if defined(MAGICKCORE_OPENMP_SUPPORT) 326 #pragma omp parallel for schedule(static) shared(progress,status) \ 327 magick_number_threads(source_image,image,image->rows,1) 328 #endif 329 for (y=0; y < (ssize_t) image->rows; y++) 330 { 331 const Quantum 332 *pixels; 333 334 PixelInfo 335 canvas_pixel, 336 source_pixel; 337 338 register const Quantum 339 *magick_restrict p; 340 341 register Quantum 342 *magick_restrict q; 343 344 register ssize_t 345 x; 346 347 if (status == MagickFalse) 348 continue; 349 if (clip_to_self != MagickFalse) 350 { 351 if (y < y_offset) 352 continue; 353 if ((y-y_offset) >= (ssize_t) source_image->rows) 354 continue; 355 } 356 /* 357 If pixels is NULL, y is outside overlay region. 358 */ 359 pixels=(Quantum *) NULL; 360 p=(Quantum *) NULL; 361 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows)) 362 { 363 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset, 364 source_image->columns,1,exception); 365 if (p == (const Quantum *) NULL) 366 { 367 status=MagickFalse; 368 continue; 369 } 370 pixels=p; 371 if (x_offset < 0) 372 p-=x_offset*GetPixelChannels(source_image); 373 } 374 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 375 if (q == (Quantum *) NULL) 376 { 377 status=MagickFalse; 378 continue; 379 } 380 GetPixelInfo(image,&canvas_pixel); 381 GetPixelInfo(source_image,&source_pixel); 382 for (x=0; x < (ssize_t) image->columns; x++) 383 { 384 double 385 gamma; 386 387 MagickRealType 388 alpha, 389 Da, 390 Dc, 391 Dca, 392 Sa, 393 Sc, 394 Sca; 395 396 register ssize_t 397 i; 398 399 size_t 400 channels; 401 402 if (clip_to_self != MagickFalse) 403 { 404 if (x < x_offset) 405 { 406 q+=GetPixelChannels(image); 407 continue; 408 } 409 if ((x-x_offset) >= (ssize_t) source_image->columns) 410 break; 411 } 412 if ((pixels == (Quantum *) NULL) || (x < x_offset) || 413 ((x-x_offset) >= (ssize_t) source_image->columns)) 414 { 415 Quantum 416 source[MaxPixelChannels]; 417 418 /* 419 Virtual composite: 420 Sc: source color. 421 Dc: canvas color. 422 */ 423 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source, 424 exception); 425 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 426 { 427 MagickRealType 428 pixel; 429 430 PixelChannel channel = GetPixelChannelChannel(image,i); 431 PixelTrait traits = GetPixelChannelTraits(image,channel); 432 PixelTrait source_traits=GetPixelChannelTraits(source_image, 433 channel); 434 if ((traits == UndefinedPixelTrait) || 435 (source_traits == UndefinedPixelTrait)) 436 continue; 437 if (channel == AlphaPixelChannel) 438 pixel=(MagickRealType) TransparentAlpha; 439 else 440 pixel=(MagickRealType) q[i]; 441 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : 442 ClampToQuantum(pixel); 443 } 444 q+=GetPixelChannels(image); 445 continue; 446 } 447 /* 448 Authentic composite: 449 Sa: normalized source alpha. 450 Da: normalized canvas alpha. 451 */ 452 Sa=QuantumScale*GetPixelAlpha(source_image,p); 453 Da=QuantumScale*GetPixelAlpha(image,q); 454 alpha=Sa+Da-Sa*Da; 455 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 456 { 457 MagickRealType 458 pixel; 459 460 PixelChannel channel = GetPixelChannelChannel(image,i); 461 PixelTrait traits = GetPixelChannelTraits(image,channel); 462 PixelTrait source_traits=GetPixelChannelTraits(source_image,channel); 463 if (traits == UndefinedPixelTrait) 464 continue; 465 if ((source_traits == UndefinedPixelTrait) && 466 (channel != AlphaPixelChannel)) 467 continue; 468 if (channel == AlphaPixelChannel) 469 { 470 /* 471 Set alpha channel. 472 */ 473 pixel=QuantumRange*alpha; 474 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : 475 ClampToQuantum(pixel); 476 continue; 477 } 478 /* 479 Sc: source color. 480 Dc: canvas color. 481 */ 482 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p); 483 Dc=(MagickRealType) q[i]; 484 if ((traits & CopyPixelTrait) != 0) 485 { 486 /* 487 Copy channel. 488 */ 489 q[i]=ClampToQuantum(Sc); 490 continue; 491 } 492 /* 493 Porter-Duff compositions: 494 Sca: source normalized color multiplied by alpha. 495 Dca: normalized canvas color multiplied by alpha. 496 */ 497 Sca=QuantumScale*Sa*Sc; 498 Dca=QuantumScale*Da*Dc; 499 gamma=PerceptibleReciprocal(alpha); 500 pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa)); 501 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel); 502 } 503 p+=GetPixelChannels(source_image); 504 channels=GetPixelChannels(source_image); 505 if (p >= (pixels+channels*source_image->columns)) 506 p=pixels; 507 q+=GetPixelChannels(image); 508 } 509 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 510 status=MagickFalse; 511 if (image->progress_monitor != (MagickProgressMonitor) NULL) 512 { 513 MagickBooleanType 514 proceed; 515 516 #if defined(MAGICKCORE_OPENMP_SUPPORT) 517 #pragma omp atomic 518 #endif 519 progress++; 520 proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows); 521 if (proceed == MagickFalse) 522 status=MagickFalse; 523 } 524 } 525 source_view=DestroyCacheView(source_view); 526 image_view=DestroyCacheView(image_view); 527 return(status); 528 } 529 530 MagickExport MagickBooleanType CompositeImage(Image *image, 531 const Image *composite,const CompositeOperator compose, 532 const MagickBooleanType clip_to_self,const ssize_t x_offset, 533 const ssize_t y_offset,ExceptionInfo *exception) 534 { 535 #define CompositeImageTag "Composite/Image" 536 537 CacheView 538 *source_view, 539 *image_view; 540 541 const char 542 *value; 543 544 GeometryInfo 545 geometry_info; 546 547 Image 548 *canvas_image, 549 *source_image; 550 551 MagickBooleanType 552 clamp, 553 status; 554 555 MagickOffsetType 556 progress; 557 558 MagickRealType 559 amount, 560 canvas_dissolve, 561 midpoint, 562 percent_luma, 563 percent_chroma, 564 source_dissolve, 565 threshold; 566 567 MagickStatusType 568 flags; 569 570 ssize_t 571 y; 572 573 assert(image != (Image *) NULL); 574 assert(image->signature == MagickCoreSignature); 575 if (image->debug != MagickFalse) 576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 577 assert(composite != (Image *) NULL); 578 assert(composite->signature == MagickCoreSignature); 579 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 580 return(MagickFalse); 581 source_image=CloneImage(composite,0,0,MagickTrue,exception); 582 if (source_image == (const Image *) NULL) 583 return(MagickFalse); 584 if (IsGrayColorspace(image->colorspace) != MagickFalse) 585 (void) SetImageColorspace(image,sRGBColorspace,exception); 586 (void) SetImageColorspace(source_image,image->colorspace,exception); 587 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp)) 588 { 589 status=CompositeOverImage(image,source_image,clip_to_self,x_offset, 590 y_offset,exception); 591 source_image=DestroyImage(source_image); 592 return(status); 593 } 594 amount=0.5; 595 canvas_image=(Image *) NULL; 596 canvas_dissolve=1.0; 597 clamp=MagickTrue; 598 value=GetImageArtifact(image,"compose:clamp"); 599 if (value != (const char *) NULL) 600 clamp=IsStringTrue(value); 601 SetGeometryInfo(&geometry_info); 602 percent_luma=100.0; 603 percent_chroma=100.0; 604 source_dissolve=1.0; 605 threshold=0.05f; 606 switch (compose) 607 { 608 case CopyCompositeOp: 609 { 610 if ((x_offset < 0) || (y_offset < 0)) 611 break; 612 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns) 613 break; 614 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows) 615 break; 616 status=MagickTrue; 617 source_view=AcquireVirtualCacheView(source_image,exception); 618 image_view=AcquireAuthenticCacheView(image,exception); 619 #if defined(MAGICKCORE_OPENMP_SUPPORT) 620 #pragma omp parallel for schedule(static) shared(status) \ 621 magick_number_threads(source_image,image,source_image->rows,1) 622 #endif 623 for (y=0; y < (ssize_t) source_image->rows; y++) 624 { 625 MagickBooleanType 626 sync; 627 628 register const Quantum 629 *p; 630 631 register Quantum 632 *q; 633 634 register ssize_t 635 x; 636 637 if (status == MagickFalse) 638 continue; 639 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1, 640 exception); 641 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset, 642 source_image->columns,1,exception); 643 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 644 { 645 status=MagickFalse; 646 continue; 647 } 648 for (x=0; x < (ssize_t) source_image->columns; x++) 649 { 650 register ssize_t 651 i; 652 653 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2)) 654 { 655 p+=GetPixelChannels(source_image); 656 q+=GetPixelChannels(image); 657 continue; 658 } 659 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 660 { 661 PixelChannel channel = GetPixelChannelChannel(image,i); 662 PixelTrait traits = GetPixelChannelTraits(image,channel); 663 PixelTrait source_traits=GetPixelChannelTraits(source_image, 664 channel); 665 if (traits == UndefinedPixelTrait) 666 continue; 667 if (source_traits != UndefinedPixelTrait) 668 SetPixelChannel(image,channel,p[i],q); 669 else if (channel == AlphaPixelChannel) 670 SetPixelChannel(image,channel,OpaqueAlpha,q); 671 } 672 p+=GetPixelChannels(source_image); 673 q+=GetPixelChannels(image); 674 } 675 sync=SyncCacheViewAuthenticPixels(image_view,exception); 676 if (sync == MagickFalse) 677 status=MagickFalse; 678 if (image->progress_monitor != (MagickProgressMonitor) NULL) 679 { 680 MagickBooleanType 681 proceed; 682 683 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType) 684 y,image->rows); 685 if (proceed == MagickFalse) 686 status=MagickFalse; 687 } 688 } 689 source_view=DestroyCacheView(source_view); 690 image_view=DestroyCacheView(image_view); 691 source_image=DestroyImage(source_image); 692 return(status); 693 } 694 case IntensityCompositeOp: 695 { 696 if ((x_offset < 0) || (y_offset < 0)) 697 break; 698 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns) 699 break; 700 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows) 701 break; 702 status=MagickTrue; 703 source_view=AcquireVirtualCacheView(source_image,exception); 704 image_view=AcquireAuthenticCacheView(image,exception); 705 #if defined(MAGICKCORE_OPENMP_SUPPORT) 706 #pragma omp parallel for schedule(static) shared(status) \ 707 magick_number_threads(source_image,image,source_image->rows,1) 708 #endif 709 for (y=0; y < (ssize_t) source_image->rows; y++) 710 { 711 MagickBooleanType 712 sync; 713 714 register const Quantum 715 *p; 716 717 register Quantum 718 *q; 719 720 register ssize_t 721 x; 722 723 if (status == MagickFalse) 724 continue; 725 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1, 726 exception); 727 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset, 728 source_image->columns,1,exception); 729 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 730 { 731 status=MagickFalse; 732 continue; 733 } 734 for (x=0; x < (ssize_t) source_image->columns; x++) 735 { 736 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2)) 737 { 738 p+=GetPixelChannels(source_image); 739 q+=GetPixelChannels(image); 740 continue; 741 } 742 SetPixelAlpha(image,clamp != MagickFalse ? 743 ClampPixel(GetPixelIntensity(source_image,p)) : 744 ClampToQuantum(GetPixelIntensity(source_image,p)),q); 745 p+=GetPixelChannels(source_image); 746 q+=GetPixelChannels(image); 747 } 748 sync=SyncCacheViewAuthenticPixels(image_view,exception); 749 if (sync == MagickFalse) 750 status=MagickFalse; 751 if (image->progress_monitor != (MagickProgressMonitor) NULL) 752 { 753 MagickBooleanType 754 proceed; 755 756 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType) 757 y,image->rows); 758 if (proceed == MagickFalse) 759 status=MagickFalse; 760 } 761 } 762 source_view=DestroyCacheView(source_view); 763 image_view=DestroyCacheView(image_view); 764 source_image=DestroyImage(source_image); 765 return(status); 766 } 767 case CopyAlphaCompositeOp: 768 case ChangeMaskCompositeOp: 769 { 770 /* 771 Modify canvas outside the overlaid region and require an alpha 772 channel to exist, to add transparency. 773 */ 774 if (image->alpha_trait == UndefinedPixelTrait) 775 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); 776 break; 777 } 778 case BlurCompositeOp: 779 { 780 CacheView 781 *canvas_view; 782 783 MagickRealType 784 angle_range, 785 angle_start, 786 height, 787 width; 788 789 PixelInfo 790 pixel; 791 792 ResampleFilter 793 *resample_filter; 794 795 SegmentInfo 796 blur; 797 798 /* 799 Blur Image by resampling. 800 801 Blur Image dictated by an overlay gradient map: X = red_channel; 802 Y = green_channel; compose:args = x_scale[,y_scale[,angle]]. 803 */ 804 canvas_image=CloneImage(image,0,0,MagickTrue, 805 exception); 806 if (canvas_image == (Image *) NULL) 807 { 808 source_image=DestroyImage(source_image); 809 return(MagickFalse); 810 } 811 /* 812 Gather the maximum blur sigma values from user. 813 */ 814 flags=NoValue; 815 value=GetImageArtifact(image,"compose:args"); 816 if (value != (const char *) NULL) 817 flags=ParseGeometry(value,&geometry_info); 818 if ((flags & WidthValue) == 0) 819 { 820 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, 821 "InvalidSetting","'%s' '%s'","compose:args",value); 822 source_image=DestroyImage(source_image); 823 canvas_image=DestroyImage(canvas_image); 824 return(MagickFalse); 825 } 826 /* 827 Users input sigma now needs to be converted to the EWA ellipse size. 828 The filter defaults to a sigma of 0.5 so to make this match the 829 users input the ellipse size needs to be doubled. 830 */ 831 width=height=geometry_info.rho*2.0; 832 if ((flags & HeightValue) != 0 ) 833 height=geometry_info.sigma*2.0; 834 /* 835 Default the unrotated ellipse width and height axis vectors. 836 */ 837 blur.x1=width; 838 blur.x2=0.0; 839 blur.y1=0.0; 840 blur.y2=height; 841 /* rotate vectors if a rotation angle is given */ 842 if ((flags & XValue) != 0 ) 843 { 844 MagickRealType 845 angle; 846 847 angle=DegreesToRadians(geometry_info.xi); 848 blur.x1=width*cos(angle); 849 blur.x2=width*sin(angle); 850 blur.y1=(-height*sin(angle)); 851 blur.y2=height*cos(angle); 852 } 853 /* Otherwise lets set a angle range and calculate in the loop */ 854 angle_start=0.0; 855 angle_range=0.0; 856 if ((flags & YValue) != 0 ) 857 { 858 angle_start=DegreesToRadians(geometry_info.xi); 859 angle_range=DegreesToRadians(geometry_info.psi)-angle_start; 860 } 861 /* 862 Set up a gaussian cylindrical filter for EWA Bluring. 863 864 As the minimum ellipse radius of support*1.0 the EWA algorithm 865 can only produce a minimum blur of 0.5 for Gaussian (support=2.0) 866 This means that even 'No Blur' will be still a little blurry! 867 868 The solution (as well as the problem of preventing any user 869 expert filter settings, is to set our own user settings, then 870 restore them afterwards. 871 */ 872 resample_filter=AcquireResampleFilter(image,exception); 873 SetResampleFilter(resample_filter,GaussianFilter); 874 875 /* do the variable blurring of each pixel in image */ 876 GetPixelInfo(image,&pixel); 877 source_view=AcquireVirtualCacheView(source_image,exception); 878 canvas_view=AcquireAuthenticCacheView(canvas_image,exception); 879 for (y=0; y < (ssize_t) source_image->rows; y++) 880 { 881 MagickBooleanType 882 sync; 883 884 register const Quantum 885 *magick_restrict p; 886 887 register Quantum 888 *magick_restrict q; 889 890 register ssize_t 891 x; 892 893 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows)) 894 continue; 895 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1, 896 exception); 897 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1, 898 exception); 899 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 900 break; 901 for (x=0; x < (ssize_t) source_image->columns; x++) 902 { 903 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns)) 904 { 905 p+=GetPixelChannels(source_image); 906 continue; 907 } 908 if (fabs((double) angle_range) > MagickEpsilon) 909 { 910 MagickRealType 911 angle; 912 913 angle=angle_start+angle_range*QuantumScale* 914 GetPixelBlue(source_image,p); 915 blur.x1=width*cos(angle); 916 blur.x2=width*sin(angle); 917 blur.y1=(-height*sin(angle)); 918 blur.y2=height*cos(angle); 919 } 920 #if 0 921 if ( x == 10 && y == 60 ) { 922 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1, 923 blur.x2,blur.y1, blur.y2); 924 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale* 925 GetPixelRed(p),QuantumScale*GetPixelGreen(p)); 926 #endif 927 ScaleResampleFilter(resample_filter, 928 blur.x1*QuantumScale*GetPixelRed(source_image,p), 929 blur.y1*QuantumScale*GetPixelGreen(source_image,p), 930 blur.x2*QuantumScale*GetPixelRed(source_image,p), 931 blur.y2*QuantumScale*GetPixelGreen(source_image,p) ); 932 (void) ResamplePixelColor(resample_filter,(double) x_offset+x, 933 (double) y_offset+y,&pixel,exception); 934 SetPixelViaPixelInfo(canvas_image,&pixel,q); 935 p+=GetPixelChannels(source_image); 936 q+=GetPixelChannels(canvas_image); 937 } 938 sync=SyncCacheViewAuthenticPixels(canvas_view,exception); 939 if (sync == MagickFalse) 940 break; 941 } 942 resample_filter=DestroyResampleFilter(resample_filter); 943 source_view=DestroyCacheView(source_view); 944 canvas_view=DestroyCacheView(canvas_view); 945 source_image=DestroyImage(source_image); 946 source_image=canvas_image; 947 break; 948 } 949 case DisplaceCompositeOp: 950 case DistortCompositeOp: 951 { 952 CacheView 953 *canvas_view; 954 955 MagickRealType 956 horizontal_scale, 957 vertical_scale; 958 959 PixelInfo 960 pixel; 961 962 PointInfo 963 center, 964 offset; 965 966 /* 967 Displace/Distort based on overlay gradient map: 968 X = red_channel; Y = green_channel; 969 compose:args = x_scale[,y_scale[,center.x,center.y]] 970 */ 971 canvas_image=CloneImage(image,0,0,MagickTrue, 972 exception); 973 if (canvas_image == (Image *) NULL) 974 { 975 source_image=DestroyImage(source_image); 976 return(MagickFalse); 977 } 978 SetGeometryInfo(&geometry_info); 979 flags=NoValue; 980 value=GetImageArtifact(image,"compose:args"); 981 if (value != (char *) NULL) 982 flags=ParseGeometry(value,&geometry_info); 983 if ((flags & (WidthValue | HeightValue)) == 0 ) 984 { 985 if ((flags & AspectValue) == 0) 986 { 987 horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0; 988 vertical_scale=(MagickRealType) (source_image->rows-1)/2.0; 989 } 990 else 991 { 992 horizontal_scale=(MagickRealType) (image->columns-1)/2.0; 993 vertical_scale=(MagickRealType) (image->rows-1)/2.0; 994 } 995 } 996 else 997 { 998 horizontal_scale=geometry_info.rho; 999 vertical_scale=geometry_info.sigma; 1000 if ((flags & PercentValue) != 0) 1001 { 1002 if ((flags & AspectValue) == 0) 1003 { 1004 horizontal_scale*=(source_image->columns-1)/200.0; 1005 vertical_scale*=(source_image->rows-1)/200.0; 1006 } 1007 else 1008 { 1009 horizontal_scale*=(image->columns-1)/200.0; 1010 vertical_scale*=(image->rows-1)/200.0; 1011 } 1012 } 1013 if ((flags & HeightValue) == 0) 1014 vertical_scale=horizontal_scale; 1015 } 1016 /* 1017 Determine fixed center point for absolute distortion map 1018 Absolute distort == 1019 Displace offset relative to a fixed absolute point 1020 Select that point according to +X+Y user inputs. 1021 default = center of overlay image 1022 arg flag '!' = locations/percentage relative to background image 1023 */ 1024 center.x=(MagickRealType) x_offset; 1025 center.y=(MagickRealType) y_offset; 1026 if (compose == DistortCompositeOp) 1027 { 1028 if ((flags & XValue) == 0) 1029 if ((flags & AspectValue) != 0) 1030 center.x=(MagickRealType) ((image->columns-1)/2.0); 1031 else 1032 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/ 1033 2.0); 1034 else 1035 if ((flags & AspectValue) != 0) 1036 center.x=geometry_info.xi; 1037 else 1038 center.x=(MagickRealType) (x_offset+geometry_info.xi); 1039 if ((flags & YValue) == 0) 1040 if ((flags & AspectValue) != 0) 1041 center.y=(MagickRealType) ((image->rows-1)/2.0); 1042 else 1043 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0); 1044 else 1045 if ((flags & AspectValue) != 0) 1046 center.y=geometry_info.psi; 1047 else 1048 center.y=(MagickRealType) (y_offset+geometry_info.psi); 1049 } 1050 /* 1051 Shift the pixel offset point as defined by the provided, 1052 displacement/distortion map. -- Like a lens... 1053 */ 1054 GetPixelInfo(image,&pixel); 1055 image_view=AcquireVirtualCacheView(image,exception); 1056 source_view=AcquireVirtualCacheView(source_image,exception); 1057 canvas_view=AcquireAuthenticCacheView(canvas_image,exception); 1058 for (y=0; y < (ssize_t) source_image->rows; y++) 1059 { 1060 MagickBooleanType 1061 sync; 1062 1063 register const Quantum 1064 *magick_restrict p; 1065 1066 register Quantum 1067 *magick_restrict q; 1068 1069 register ssize_t 1070 x; 1071 1072 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows)) 1073 continue; 1074 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1, 1075 exception); 1076 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1, 1077 exception); 1078 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 1079 break; 1080 for (x=0; x < (ssize_t) source_image->columns; x++) 1081 { 1082 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns)) 1083 { 1084 p+=GetPixelChannels(source_image); 1085 continue; 1086 } 1087 /* 1088 Displace the offset. 1089 */ 1090 offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)- 1091 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType) 1092 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ? 1093 x : 0); 1094 offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)- 1095 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType) 1096 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ? 1097 y : 0); 1098 status=InterpolatePixelInfo(image,image_view, 1099 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y, 1100 &pixel,exception); 1101 if (status == MagickFalse) 1102 break; 1103 /* 1104 Mask with the 'invalid pixel mask' in alpha channel. 1105 */ 1106 pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)* 1107 (QuantumScale*GetPixelAlpha(source_image,p)); 1108 SetPixelViaPixelInfo(canvas_image,&pixel,q); 1109 p+=GetPixelChannels(source_image); 1110 q+=GetPixelChannels(canvas_image); 1111 } 1112 if (x < (ssize_t) source_image->columns) 1113 break; 1114 sync=SyncCacheViewAuthenticPixels(canvas_view,exception); 1115 if (sync == MagickFalse) 1116 break; 1117 } 1118 canvas_view=DestroyCacheView(canvas_view); 1119 source_view=DestroyCacheView(source_view); 1120 image_view=DestroyCacheView(image_view); 1121 source_image=DestroyImage(source_image); 1122 source_image=canvas_image; 1123 break; 1124 } 1125 case DissolveCompositeOp: 1126 { 1127 /* 1128 Geometry arguments to dissolve factors. 1129 */ 1130 value=GetImageArtifact(image,"compose:args"); 1131 if (value != (char *) NULL) 1132 { 1133 flags=ParseGeometry(value,&geometry_info); 1134 source_dissolve=geometry_info.rho/100.0; 1135 canvas_dissolve=1.0; 1136 if ((source_dissolve-MagickEpsilon) < 0.0) 1137 source_dissolve=0.0; 1138 if ((source_dissolve+MagickEpsilon) > 1.0) 1139 { 1140 canvas_dissolve=2.0-source_dissolve; 1141 source_dissolve=1.0; 1142 } 1143 if ((flags & SigmaValue) != 0) 1144 canvas_dissolve=geometry_info.sigma/100.0; 1145 if ((canvas_dissolve-MagickEpsilon) < 0.0) 1146 canvas_dissolve=0.0; 1147 } 1148 break; 1149 } 1150 case BlendCompositeOp: 1151 { 1152 value=GetImageArtifact(image,"compose:args"); 1153 if (value != (char *) NULL) 1154 { 1155 flags=ParseGeometry(value,&geometry_info); 1156 source_dissolve=geometry_info.rho/100.0; 1157 canvas_dissolve=1.0-source_dissolve; 1158 if ((flags & SigmaValue) != 0) 1159 canvas_dissolve=geometry_info.sigma/100.0; 1160 } 1161 break; 1162 } 1163 case MathematicsCompositeOp: 1164 { 1165 /* 1166 Just collect the values from "compose:args", setting. 1167 Unused values are set to zero automagically. 1168 1169 Arguments are normally a comma separated list, so this probably should 1170 be changed to some 'general comma list' parser, (with a minimum 1171 number of values) 1172 */ 1173 SetGeometryInfo(&geometry_info); 1174 value=GetImageArtifact(image,"compose:args"); 1175 if (value != (char *) NULL) 1176 (void) ParseGeometry(value,&geometry_info); 1177 break; 1178 } 1179 case ModulateCompositeOp: 1180 { 1181 /* 1182 Determine the luma and chroma scale. 1183 */ 1184 value=GetImageArtifact(image,"compose:args"); 1185 if (value != (char *) NULL) 1186 { 1187 flags=ParseGeometry(value,&geometry_info); 1188 percent_luma=geometry_info.rho; 1189 if ((flags & SigmaValue) != 0) 1190 percent_chroma=geometry_info.sigma; 1191 } 1192 break; 1193 } 1194 case ThresholdCompositeOp: 1195 { 1196 /* 1197 Determine the amount and threshold. 1198 */ 1199 value=GetImageArtifact(image,"compose:args"); 1200 if (value != (char *) NULL) 1201 { 1202 flags=ParseGeometry(value,&geometry_info); 1203 amount=geometry_info.rho; 1204 threshold=geometry_info.sigma; 1205 if ((flags & SigmaValue) == 0) 1206 threshold=0.05f; 1207 } 1208 threshold*=QuantumRange; 1209 break; 1210 } 1211 default: 1212 break; 1213 } 1214 /* 1215 Composite image. 1216 */ 1217 status=MagickTrue; 1218 progress=0; 1219 midpoint=((MagickRealType) QuantumRange+1.0)/2; 1220 source_view=AcquireVirtualCacheView(source_image,exception); 1221 image_view=AcquireAuthenticCacheView(image,exception); 1222 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1223 #pragma omp parallel for schedule(static) shared(progress,status) \ 1224 magick_number_threads(source_image,image,image->rows,1) 1225 #endif 1226 for (y=0; y < (ssize_t) image->rows; y++) 1227 { 1228 const Quantum 1229 *pixels; 1230 1231 MagickRealType 1232 blue, 1233 chroma, 1234 green, 1235 hue, 1236 luma, 1237 red; 1238 1239 PixelInfo 1240 canvas_pixel, 1241 source_pixel; 1242 1243 register const Quantum 1244 *magick_restrict p; 1245 1246 register Quantum 1247 *magick_restrict q; 1248 1249 register ssize_t 1250 x; 1251 1252 if (status == MagickFalse) 1253 continue; 1254 if (clip_to_self != MagickFalse) 1255 { 1256 if (y < y_offset) 1257 continue; 1258 if ((y-y_offset) >= (ssize_t) source_image->rows) 1259 continue; 1260 } 1261 /* 1262 If pixels is NULL, y is outside overlay region. 1263 */ 1264 pixels=(Quantum *) NULL; 1265 p=(Quantum *) NULL; 1266 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows)) 1267 { 1268 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset, 1269 source_image->columns,1,exception); 1270 if (p == (const Quantum *) NULL) 1271 { 1272 status=MagickFalse; 1273 continue; 1274 } 1275 pixels=p; 1276 if (x_offset < 0) 1277 p-=x_offset*GetPixelChannels(source_image); 1278 } 1279 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 1280 if (q == (Quantum *) NULL) 1281 { 1282 status=MagickFalse; 1283 continue; 1284 } 1285 hue=0.0; 1286 chroma=0.0; 1287 luma=0.0; 1288 GetPixelInfo(image,&canvas_pixel); 1289 GetPixelInfo(source_image,&source_pixel); 1290 for (x=0; x < (ssize_t) image->columns; x++) 1291 { 1292 double 1293 gamma; 1294 1295 MagickRealType 1296 alpha, 1297 Da, 1298 Dc, 1299 Dca, 1300 DcaDa, 1301 Sa, 1302 SaSca, 1303 Sc, 1304 Sca; 1305 1306 register ssize_t 1307 i; 1308 1309 size_t 1310 channels; 1311 1312 if (clip_to_self != MagickFalse) 1313 { 1314 if (x < x_offset) 1315 { 1316 q+=GetPixelChannels(image); 1317 continue; 1318 } 1319 if ((x-x_offset) >= (ssize_t) source_image->columns) 1320 break; 1321 } 1322 if ((pixels == (Quantum *) NULL) || (x < x_offset) || 1323 ((x-x_offset) >= (ssize_t) source_image->columns)) 1324 { 1325 Quantum 1326 source[MaxPixelChannels]; 1327 1328 /* 1329 Virtual composite: 1330 Sc: source color. 1331 Dc: canvas color. 1332 */ 1333 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source, 1334 exception); 1335 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1336 { 1337 MagickRealType 1338 pixel; 1339 1340 PixelChannel channel = GetPixelChannelChannel(image,i); 1341 PixelTrait traits = GetPixelChannelTraits(image,channel); 1342 PixelTrait source_traits=GetPixelChannelTraits(source_image, 1343 channel); 1344 if ((traits == UndefinedPixelTrait) || 1345 (source_traits == UndefinedPixelTrait)) 1346 continue; 1347 switch (compose) 1348 { 1349 case AlphaCompositeOp: 1350 case ChangeMaskCompositeOp: 1351 case CopyAlphaCompositeOp: 1352 case DstAtopCompositeOp: 1353 case DstInCompositeOp: 1354 case InCompositeOp: 1355 case OutCompositeOp: 1356 case SrcInCompositeOp: 1357 case SrcOutCompositeOp: 1358 { 1359 if (channel == AlphaPixelChannel) 1360 pixel=(MagickRealType) TransparentAlpha; 1361 else 1362 pixel=(MagickRealType) q[i]; 1363 break; 1364 } 1365 case ClearCompositeOp: 1366 case CopyCompositeOp: 1367 case ReplaceCompositeOp: 1368 case SrcCompositeOp: 1369 { 1370 if (channel == AlphaPixelChannel) 1371 pixel=(MagickRealType) TransparentAlpha; 1372 else 1373 pixel=0.0; 1374 break; 1375 } 1376 case BlendCompositeOp: 1377 case DissolveCompositeOp: 1378 { 1379 if (channel == AlphaPixelChannel) 1380 pixel=canvas_dissolve*GetPixelAlpha(source_image,source); 1381 else 1382 pixel=(MagickRealType) source[channel]; 1383 break; 1384 } 1385 default: 1386 { 1387 pixel=(MagickRealType) source[channel]; 1388 break; 1389 } 1390 } 1391 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : 1392 ClampToQuantum(pixel); 1393 } 1394 q+=GetPixelChannels(image); 1395 continue; 1396 } 1397 /* 1398 Authentic composite: 1399 Sa: normalized source alpha. 1400 Da: normalized canvas alpha. 1401 */ 1402 Sa=QuantumScale*GetPixelAlpha(source_image,p); 1403 Da=QuantumScale*GetPixelAlpha(image,q); 1404 switch (compose) 1405 { 1406 case BumpmapCompositeOp: 1407 { 1408 alpha=GetPixelIntensity(source_image,p)*Sa; 1409 break; 1410 } 1411 case ColorBurnCompositeOp: 1412 case ColorDodgeCompositeOp: 1413 case DarkenCompositeOp: 1414 case DifferenceCompositeOp: 1415 case DivideDstCompositeOp: 1416 case DivideSrcCompositeOp: 1417 case ExclusionCompositeOp: 1418 case HardLightCompositeOp: 1419 case HardMixCompositeOp: 1420 case LinearBurnCompositeOp: 1421 case LinearDodgeCompositeOp: 1422 case LinearLightCompositeOp: 1423 case LightenCompositeOp: 1424 case MathematicsCompositeOp: 1425 case MinusDstCompositeOp: 1426 case MinusSrcCompositeOp: 1427 case ModulusAddCompositeOp: 1428 case ModulusSubtractCompositeOp: 1429 case MultiplyCompositeOp: 1430 case OverlayCompositeOp: 1431 case PegtopLightCompositeOp: 1432 case PinLightCompositeOp: 1433 case ScreenCompositeOp: 1434 case SoftLightCompositeOp: 1435 case VividLightCompositeOp: 1436 { 1437 alpha=RoundToUnity(Sa+Da-Sa*Da); 1438 break; 1439 } 1440 case DstAtopCompositeOp: 1441 case DstInCompositeOp: 1442 case InCompositeOp: 1443 case SrcInCompositeOp: 1444 { 1445 alpha=Sa*Da; 1446 break; 1447 } 1448 case DissolveCompositeOp: 1449 { 1450 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+ 1451 canvas_dissolve*Da; 1452 break; 1453 } 1454 case DstOverCompositeOp: 1455 case OverCompositeOp: 1456 case SrcOverCompositeOp: 1457 { 1458 alpha=Sa+Da-Sa*Da; 1459 break; 1460 } 1461 case DstOutCompositeOp: 1462 { 1463 alpha=Da*(1.0-Sa); 1464 break; 1465 } 1466 case OutCompositeOp: 1467 case SrcOutCompositeOp: 1468 { 1469 alpha=Sa*(1.0-Da); 1470 break; 1471 } 1472 case BlendCompositeOp: 1473 case PlusCompositeOp: 1474 { 1475 alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da); 1476 break; 1477 } 1478 case XorCompositeOp: 1479 { 1480 alpha=Sa+Da-2.0*Sa*Da; 1481 break; 1482 } 1483 default: 1484 { 1485 alpha=1.0; 1486 break; 1487 } 1488 } 1489 switch (compose) 1490 { 1491 case ColorizeCompositeOp: 1492 case HueCompositeOp: 1493 case LuminizeCompositeOp: 1494 case ModulateCompositeOp: 1495 case SaturateCompositeOp: 1496 { 1497 GetPixelInfoPixel(source_image,p,&source_pixel); 1498 GetPixelInfoPixel(image,q,&canvas_pixel); 1499 break; 1500 } 1501 default: 1502 break; 1503 } 1504 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1505 { 1506 MagickRealType 1507 pixel, 1508 sans; 1509 1510 PixelChannel channel = GetPixelChannelChannel(image,i); 1511 PixelTrait traits = GetPixelChannelTraits(image,channel); 1512 PixelTrait source_traits = GetPixelChannelTraits(source_image,channel); 1513 if (traits == UndefinedPixelTrait) 1514 continue; 1515 if ((channel == AlphaPixelChannel) && 1516 ((traits & UpdatePixelTrait) != 0)) 1517 { 1518 /* 1519 Set alpha channel. 1520 */ 1521 switch (compose) 1522 { 1523 case AlphaCompositeOp: 1524 { 1525 pixel=QuantumRange*Sa; 1526 break; 1527 } 1528 case AtopCompositeOp: 1529 case CopyBlackCompositeOp: 1530 case CopyBlueCompositeOp: 1531 case CopyCyanCompositeOp: 1532 case CopyGreenCompositeOp: 1533 case CopyMagentaCompositeOp: 1534 case CopyRedCompositeOp: 1535 case CopyYellowCompositeOp: 1536 case SrcAtopCompositeOp: 1537 case DstCompositeOp: 1538 case NoCompositeOp: 1539 { 1540 pixel=QuantumRange*Da; 1541 break; 1542 } 1543 case ChangeMaskCompositeOp: 1544 { 1545 MagickBooleanType 1546 equivalent; 1547 1548 if (Da < 0.5) 1549 { 1550 pixel=(MagickRealType) TransparentAlpha; 1551 break; 1552 } 1553 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q); 1554 if (equivalent != MagickFalse) 1555 pixel=(MagickRealType) TransparentAlpha; 1556 else 1557 pixel=(MagickRealType) OpaqueAlpha; 1558 break; 1559 } 1560 case ClearCompositeOp: 1561 { 1562 pixel=(MagickRealType) TransparentAlpha; 1563 break; 1564 } 1565 case ColorizeCompositeOp: 1566 case HueCompositeOp: 1567 case LuminizeCompositeOp: 1568 case SaturateCompositeOp: 1569 { 1570 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon) 1571 { 1572 pixel=QuantumRange*Da; 1573 break; 1574 } 1575 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon) 1576 { 1577 pixel=QuantumRange*Sa; 1578 break; 1579 } 1580 if (Sa < Da) 1581 { 1582 pixel=QuantumRange*Da; 1583 break; 1584 } 1585 pixel=QuantumRange*Sa; 1586 break; 1587 } 1588 case CopyAlphaCompositeOp: 1589 { 1590 if (source_image->alpha_trait == UndefinedPixelTrait) 1591 pixel=GetPixelIntensity(source_image,p); 1592 else 1593 pixel=QuantumRange*Sa; 1594 break; 1595 } 1596 case CopyCompositeOp: 1597 case DisplaceCompositeOp: 1598 case DistortCompositeOp: 1599 case DstAtopCompositeOp: 1600 case ReplaceCompositeOp: 1601 case SrcCompositeOp: 1602 { 1603 pixel=QuantumRange*Sa; 1604 break; 1605 } 1606 case DarkenIntensityCompositeOp: 1607 { 1608 pixel=Sa*GetPixelIntensity(source_image,p) < 1609 Da*GetPixelIntensity(image,q) ? Sa : Da; 1610 break; 1611 } 1612 case DifferenceCompositeOp: 1613 { 1614 pixel=QuantumRange*fabs(Sa-Da); 1615 break; 1616 } 1617 case LightenIntensityCompositeOp: 1618 { 1619 pixel=Sa*GetPixelIntensity(source_image,p) > 1620 Da*GetPixelIntensity(image,q) ? Sa : Da; 1621 break; 1622 } 1623 case ModulateCompositeOp: 1624 { 1625 pixel=QuantumRange*Da; 1626 break; 1627 } 1628 case MultiplyCompositeOp: 1629 { 1630 pixel=QuantumRange*Sa*Da; 1631 break; 1632 } 1633 case StereoCompositeOp: 1634 { 1635 pixel=QuantumRange*(Sa+Da)/2; 1636 break; 1637 } 1638 default: 1639 { 1640 pixel=QuantumRange*alpha; 1641 break; 1642 } 1643 } 1644 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : 1645 ClampToQuantum(pixel); 1646 continue; 1647 } 1648 if (source_traits == UndefinedPixelTrait) 1649 continue; 1650 /* 1651 Sc: source color. 1652 Dc: canvas color. 1653 */ 1654 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p); 1655 Dc=(MagickRealType) q[i]; 1656 if ((traits & CopyPixelTrait) != 0) 1657 { 1658 /* 1659 Copy channel. 1660 */ 1661 q[i]=ClampToQuantum(Dc); 1662 continue; 1663 } 1664 /* 1665 Porter-Duff compositions: 1666 Sca: source normalized color multiplied by alpha. 1667 Dca: normalized canvas color multiplied by alpha. 1668 */ 1669 Sca=QuantumScale*Sa*Sc; 1670 Dca=QuantumScale*Da*Dc; 1671 SaSca=Sa*PerceptibleReciprocal(Sca); 1672 DcaDa=Dca*PerceptibleReciprocal(Da); 1673 switch (compose) 1674 { 1675 case DarkenCompositeOp: 1676 case LightenCompositeOp: 1677 case ModulusSubtractCompositeOp: 1678 { 1679 gamma=PerceptibleReciprocal(1.0-alpha); 1680 break; 1681 } 1682 default: 1683 { 1684 gamma=PerceptibleReciprocal(alpha); 1685 break; 1686 } 1687 } 1688 pixel=Dc; 1689 switch (compose) 1690 { 1691 case AlphaCompositeOp: 1692 { 1693 pixel=QuantumRange*Sa; 1694 break; 1695 } 1696 case AtopCompositeOp: 1697 case SrcAtopCompositeOp: 1698 { 1699 pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa)); 1700 break; 1701 } 1702 case BlendCompositeOp: 1703 { 1704 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc); 1705 break; 1706 } 1707 case BlurCompositeOp: 1708 case CopyCompositeOp: 1709 case ReplaceCompositeOp: 1710 case SrcCompositeOp: 1711 { 1712 pixel=QuantumRange*Sca; 1713 break; 1714 } 1715 case DisplaceCompositeOp: 1716 case DistortCompositeOp: 1717 { 1718 pixel=Sc; 1719 break; 1720 } 1721 case BumpmapCompositeOp: 1722 { 1723 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon) 1724 { 1725 pixel=Dc; 1726 break; 1727 } 1728 pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc; 1729 break; 1730 } 1731 case ChangeMaskCompositeOp: 1732 { 1733 pixel=Dc; 1734 break; 1735 } 1736 case ClearCompositeOp: 1737 { 1738 pixel=0.0; 1739 break; 1740 } 1741 case ColorBurnCompositeOp: 1742 { 1743 if ((Sca == 0.0) && (Dca == Da)) 1744 { 1745 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa)); 1746 break; 1747 } 1748 if (Sca == 0.0) 1749 { 1750 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)); 1751 break; 1752 } 1753 pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-DcaDa)* 1754 SaSca)+Sca*(1.0-Da)+Dca*(1.0-Sa)); 1755 break; 1756 } 1757 case ColorDodgeCompositeOp: 1758 { 1759 if ((Sca*Da+Dca*Sa) >= Sa*Da) 1760 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); 1761 else 1762 pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(Sa-Sca)+ 1763 Sca*(1.0-Da)+Dca*(1.0-Sa)); 1764 break; 1765 } 1766 case ColorizeCompositeOp: 1767 { 1768 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon) 1769 { 1770 pixel=Dc; 1771 break; 1772 } 1773 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon) 1774 { 1775 pixel=Sc; 1776 break; 1777 } 1778 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue, 1779 &sans,&sans,&luma); 1780 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue, 1781 &hue,&chroma,&sans); 1782 HCLComposite(hue,chroma,luma,&red,&green,&blue); 1783 switch (channel) 1784 { 1785 case RedPixelChannel: pixel=red; break; 1786 case GreenPixelChannel: pixel=green; break; 1787 case BluePixelChannel: pixel=blue; break; 1788 default: pixel=Dc; break; 1789 } 1790 break; 1791 } 1792 case CopyAlphaCompositeOp: 1793 { 1794 pixel=Dc; 1795 break; 1796 } 1797 case CopyBlackCompositeOp: 1798 { 1799 if (channel == BlackPixelChannel) 1800 pixel=(MagickRealType) (QuantumRange- 1801 GetPixelBlack(source_image,p)); 1802 break; 1803 } 1804 case CopyBlueCompositeOp: 1805 case CopyYellowCompositeOp: 1806 { 1807 if (channel == BluePixelChannel) 1808 pixel=(MagickRealType) GetPixelBlue(source_image,p); 1809 break; 1810 } 1811 case CopyGreenCompositeOp: 1812 case CopyMagentaCompositeOp: 1813 { 1814 if (channel == GreenPixelChannel) 1815 pixel=(MagickRealType) GetPixelGreen(source_image,p); 1816 break; 1817 } 1818 case CopyRedCompositeOp: 1819 case CopyCyanCompositeOp: 1820 { 1821 if (channel == RedPixelChannel) 1822 pixel=(MagickRealType) GetPixelRed(source_image,p); 1823 break; 1824 } 1825 case DarkenCompositeOp: 1826 { 1827 /* 1828 Darken is equivalent to a 'Minimum' method 1829 OR a greyscale version of a binary 'Or' 1830 OR the 'Intersection' of pixel sets. 1831 */ 1832 if ((Sca*Da) < (Dca*Sa)) 1833 { 1834 pixel=QuantumRange*(Sca+Dca*(1.0-Sa)); 1835 break; 1836 } 1837 pixel=QuantumRange*(Dca+Sca*(1.0-Da)); 1838 break; 1839 } 1840 case DarkenIntensityCompositeOp: 1841 { 1842 pixel=Sa*GetPixelIntensity(source_image,p) < 1843 Da*GetPixelIntensity(image,q) ? Sc : Dc; 1844 break; 1845 } 1846 case DifferenceCompositeOp: 1847 { 1848 pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa)); 1849 break; 1850 } 1851 case DissolveCompositeOp: 1852 { 1853 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa* 1854 canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc); 1855 break; 1856 } 1857 case DivideDstCompositeOp: 1858 { 1859 if ((fabs((double) Sca) < MagickEpsilon) && 1860 (fabs((double) Dca) < MagickEpsilon)) 1861 { 1862 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa)); 1863 break; 1864 } 1865 if (fabs((double) Dca) < MagickEpsilon) 1866 { 1867 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); 1868 break; 1869 } 1870 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); 1871 break; 1872 } 1873 case DivideSrcCompositeOp: 1874 { 1875 if ((fabs((double) Dca) < MagickEpsilon) && 1876 (fabs((double) Sca) < MagickEpsilon)) 1877 { 1878 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da)); 1879 break; 1880 } 1881 if (fabs((double) Sca) < MagickEpsilon) 1882 { 1883 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da)); 1884 break; 1885 } 1886 pixel=QuantumRange*gamma*(Dca*Sa*SaSca+Dca*(1.0-Sa)+Sca*(1.0-Da)); 1887 break; 1888 } 1889 case DstAtopCompositeOp: 1890 { 1891 pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da)); 1892 break; 1893 } 1894 case DstCompositeOp: 1895 case NoCompositeOp: 1896 { 1897 pixel=QuantumRange*Dca; 1898 break; 1899 } 1900 case DstInCompositeOp: 1901 { 1902 pixel=QuantumRange*(Dca*Sa); 1903 break; 1904 } 1905 case DstOutCompositeOp: 1906 { 1907 pixel=QuantumRange*(Dca*(1.0-Sa)); 1908 break; 1909 } 1910 case DstOverCompositeOp: 1911 { 1912 pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da)); 1913 break; 1914 } 1915 case ExclusionCompositeOp: 1916 { 1917 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+ 1918 Dca*(1.0-Sa)); 1919 break; 1920 } 1921 case HardLightCompositeOp: 1922 { 1923 if ((2.0*Sca) < Sa) 1924 { 1925 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0- 1926 Sa)); 1927 break; 1928 } 1929 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+ 1930 Dca*(1.0-Sa)); 1931 break; 1932 } 1933 case HardMixCompositeOp: 1934 { 1935 pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange); 1936 break; 1937 } 1938 case HueCompositeOp: 1939 { 1940 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon) 1941 { 1942 pixel=Dc; 1943 break; 1944 } 1945 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon) 1946 { 1947 pixel=Sc; 1948 break; 1949 } 1950 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue, 1951 &hue,&chroma,&luma); 1952 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue, 1953 &hue,&sans,&sans); 1954 HCLComposite(hue,chroma,luma,&red,&green,&blue); 1955 switch (channel) 1956 { 1957 case RedPixelChannel: pixel=red; break; 1958 case GreenPixelChannel: pixel=green; break; 1959 case BluePixelChannel: pixel=blue; break; 1960 default: pixel=Dc; break; 1961 } 1962 break; 1963 } 1964 case InCompositeOp: 1965 case SrcInCompositeOp: 1966 { 1967 pixel=QuantumRange*(Sca*Da); 1968 break; 1969 } 1970 case LinearBurnCompositeOp: 1971 { 1972 /* 1973 LinearBurn: as defined by Abode Photoshop, according to 1974 http://www.simplefilter.de/en/basics/mixmods.html is: 1975 1976 f(Sc,Dc) = Sc + Dc - 1 1977 */ 1978 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da); 1979 break; 1980 } 1981 case LinearDodgeCompositeOp: 1982 { 1983 pixel=gamma*(Sa*Sc+Da*Dc); 1984 break; 1985 } 1986 case LinearLightCompositeOp: 1987 { 1988 /* 1989 LinearLight: as defined by Abode Photoshop, according to 1990 http://www.simplefilter.de/en/basics/mixmods.html is: 1991 1992 f(Sc,Dc) = Dc + 2*Sc - 1 1993 */ 1994 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca); 1995 break; 1996 } 1997 case LightenCompositeOp: 1998 { 1999 if ((Sca*Da) > (Dca*Sa)) 2000 { 2001 pixel=QuantumRange*(Sca+Dca*(1.0-Sa)); 2002 break; 2003 } 2004 pixel=QuantumRange*(Dca+Sca*(1.0-Da)); 2005 break; 2006 } 2007 case LightenIntensityCompositeOp: 2008 { 2009 /* 2010 Lighten is equivalent to a 'Maximum' method 2011 OR a greyscale version of a binary 'And' 2012 OR the 'Union' of pixel sets. 2013 */ 2014 pixel=Sa*GetPixelIntensity(source_image,p) > 2015 Da*GetPixelIntensity(image,q) ? Sc : Dc; 2016 break; 2017 } 2018 case LuminizeCompositeOp: 2019 { 2020 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon) 2021 { 2022 pixel=Dc; 2023 break; 2024 } 2025 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon) 2026 { 2027 pixel=Sc; 2028 break; 2029 } 2030 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue, 2031 &hue,&chroma,&luma); 2032 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue, 2033 &sans,&sans,&luma); 2034 HCLComposite(hue,chroma,luma,&red,&green,&blue); 2035 switch (channel) 2036 { 2037 case RedPixelChannel: pixel=red; break; 2038 case GreenPixelChannel: pixel=green; break; 2039 case BluePixelChannel: pixel=blue; break; 2040 default: pixel=Dc; break; 2041 } 2042 break; 2043 } 2044 case MathematicsCompositeOp: 2045 { 2046 /* 2047 'Mathematics' a free form user control mathematical composition 2048 is defined as... 2049 2050 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D 2051 2052 Where the arguments A,B,C,D are (currently) passed to composite 2053 as a command separated 'geometry' string in "compose:args" image 2054 artifact. 2055 2056 A = a->rho, B = a->sigma, C = a->xi, D = a->psi 2057 2058 Applying the SVG transparency formula (see above), we get... 2059 2060 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa) 2061 2062 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) + 2063 Dca*(1.0-Sa) 2064 */ 2065 pixel=QuantumRange*gamma*(geometry_info.rho*Sca*Dca+ 2066 geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+ 2067 geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); 2068 break; 2069 } 2070 case MinusDstCompositeOp: 2071 { 2072 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa); 2073 break; 2074 } 2075 case MinusSrcCompositeOp: 2076 { 2077 /* 2078 Minus source from canvas. 2079 2080 f(Sc,Dc) = Sc - Dc 2081 */ 2082 pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da); 2083 break; 2084 } 2085 case ModulateCompositeOp: 2086 { 2087 ssize_t 2088 offset; 2089 2090 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon) 2091 { 2092 pixel=Dc; 2093 break; 2094 } 2095 offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint); 2096 if (offset == 0) 2097 { 2098 pixel=Dc; 2099 break; 2100 } 2101 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue, 2102 &hue,&chroma,&luma); 2103 luma+=(0.01*percent_luma*offset)/midpoint; 2104 chroma*=0.01*percent_chroma; 2105 HCLComposite(hue,chroma,luma,&red,&green,&blue); 2106 switch (channel) 2107 { 2108 case RedPixelChannel: pixel=red; break; 2109 case GreenPixelChannel: pixel=green; break; 2110 case BluePixelChannel: pixel=blue; break; 2111 default: pixel=Dc; break; 2112 } 2113 break; 2114 } 2115 case ModulusAddCompositeOp: 2116 { 2117 pixel=Sc+Dc; 2118 while (pixel > QuantumRange) 2119 pixel-=QuantumRange; 2120 while (pixel < 0.0) 2121 pixel+=QuantumRange; 2122 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa)); 2123 break; 2124 } 2125 case ModulusSubtractCompositeOp: 2126 { 2127 pixel=Sc-Dc; 2128 while (pixel > QuantumRange) 2129 pixel-=QuantumRange; 2130 while (pixel < 0.0) 2131 pixel+=QuantumRange; 2132 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa)); 2133 break; 2134 } 2135 case MultiplyCompositeOp: 2136 { 2137 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); 2138 break; 2139 } 2140 case OutCompositeOp: 2141 case SrcOutCompositeOp: 2142 { 2143 pixel=QuantumRange*(Sca*(1.0-Da)); 2144 break; 2145 } 2146 case OverCompositeOp: 2147 case SrcOverCompositeOp: 2148 { 2149 pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa)); 2150 break; 2151 } 2152 case OverlayCompositeOp: 2153 { 2154 if ((2.0*Dca) < Da) 2155 { 2156 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0- 2157 Da)); 2158 break; 2159 } 2160 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+ 2161 Sca*(1.0-Da)); 2162 break; 2163 } 2164 case PegtopLightCompositeOp: 2165 { 2166 /* 2167 PegTop: A Soft-Light alternative: A continuous version of the 2168 Softlight function, producing very similar results. 2169 2170 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc 2171 2172 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm. 2173 */ 2174 if (fabs((double) Da) < MagickEpsilon) 2175 { 2176 pixel=QuantumRange*gamma*(Sca); 2177 break; 2178 } 2179 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0- 2180 Da)+Dca*(1.0-Sa)); 2181 break; 2182 } 2183 case PinLightCompositeOp: 2184 { 2185 /* 2186 PinLight: A Photoshop 7 composition method 2187 http://www.simplefilter.de/en/basics/mixmods.html 2188 2189 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc 2190 */ 2191 if ((Dca*Sa) < (Da*(2.0*Sca-Sa))) 2192 { 2193 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa)); 2194 break; 2195 } 2196 if ((Dca*Sa) > (2.0*Sca*Da)) 2197 { 2198 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa)); 2199 break; 2200 } 2201 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca); 2202 break; 2203 } 2204 case PlusCompositeOp: 2205 { 2206 pixel=QuantumRange*(Sca+Dca); 2207 break; 2208 } 2209 case SaturateCompositeOp: 2210 { 2211 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon) 2212 { 2213 pixel=Dc; 2214 break; 2215 } 2216 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon) 2217 { 2218 pixel=Sc; 2219 break; 2220 } 2221 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue, 2222 &hue,&chroma,&luma); 2223 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue, 2224 &sans,&chroma,&sans); 2225 HCLComposite(hue,chroma,luma,&red,&green,&blue); 2226 switch (channel) 2227 { 2228 case RedPixelChannel: pixel=red; break; 2229 case GreenPixelChannel: pixel=green; break; 2230 case BluePixelChannel: pixel=blue; break; 2231 default: pixel=Dc; break; 2232 } 2233 break; 2234 } 2235 case ScreenCompositeOp: 2236 { 2237 /* 2238 Screen: a negated multiply: 2239 2240 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc) 2241 */ 2242 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca); 2243 break; 2244 } 2245 case SoftLightCompositeOp: 2246 { 2247 if ((2.0*Sca) < Sa) 2248 { 2249 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-DcaDa))+ 2250 Sca*(1.0-Da)+Dca*(1.0-Sa)); 2251 break; 2252 } 2253 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da)) 2254 { 2255 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*DcaDa* 2256 (4.0*DcaDa+1.0)*(DcaDa-1.0)+7.0*DcaDa)+Sca*(1.0-Da)+ 2257 Dca*(1.0-Sa)); 2258 break; 2259 } 2260 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow(DcaDa,0.5)- 2261 DcaDa)+Sca*(1.0-Da)+Dca*(1.0-Sa)); 2262 break; 2263 } 2264 case StereoCompositeOp: 2265 { 2266 if (channel == RedPixelChannel) 2267 pixel=(MagickRealType) GetPixelRed(source_image,p); 2268 break; 2269 } 2270 case ThresholdCompositeOp: 2271 { 2272 MagickRealType 2273 delta; 2274 2275 delta=Sc-Dc; 2276 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold) 2277 { 2278 pixel=gamma*Dc; 2279 break; 2280 } 2281 pixel=gamma*(Dc+delta*amount); 2282 break; 2283 } 2284 case VividLightCompositeOp: 2285 { 2286 /* 2287 VividLight: A Photoshop 7 composition method. See 2288 http://www.simplefilter.de/en/basics/mixmods.html. 2289 2290 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc)) 2291 */ 2292 if ((fabs((double) Sa) < MagickEpsilon) || 2293 (fabs((double) (Sca-Sa)) < MagickEpsilon)) 2294 { 2295 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); 2296 break; 2297 } 2298 if ((2.0*Sca) <= Sa) 2299 { 2300 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)* 2301 PerceptibleReciprocal(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa)); 2302 break; 2303 } 2304 pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(2.0* 2305 (Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa)); 2306 break; 2307 } 2308 case XorCompositeOp: 2309 { 2310 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa)); 2311 break; 2312 } 2313 default: 2314 { 2315 pixel=Sc; 2316 break; 2317 } 2318 } 2319 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel); 2320 } 2321 p+=GetPixelChannels(source_image); 2322 channels=GetPixelChannels(source_image); 2323 if (p >= (pixels+channels*source_image->columns)) 2324 p=pixels; 2325 q+=GetPixelChannels(image); 2326 } 2327 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 2328 status=MagickFalse; 2329 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2330 { 2331 MagickBooleanType 2332 proceed; 2333 2334 #if defined(MAGICKCORE_OPENMP_SUPPORT) 2335 #pragma omp atomic 2336 #endif 2337 progress++; 2338 proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows); 2339 if (proceed == MagickFalse) 2340 status=MagickFalse; 2341 } 2342 } 2343 source_view=DestroyCacheView(source_view); 2344 image_view=DestroyCacheView(image_view); 2345 if (canvas_image != (Image * ) NULL) 2346 canvas_image=DestroyImage(canvas_image); 2347 else 2348 source_image=DestroyImage(source_image); 2349 return(status); 2350 } 2351 2352 /* 2354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2355 % % 2356 % % 2357 % % 2358 % T e x t u r e I m a g e % 2359 % % 2360 % % 2361 % % 2362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2363 % 2364 % TextureImage() repeatedly tiles the texture image across and down the image 2365 % canvas. 2366 % 2367 % The format of the TextureImage method is: 2368 % 2369 % MagickBooleanType TextureImage(Image *image,const Image *texture, 2370 % ExceptionInfo *exception) 2371 % 2372 % A description of each parameter follows: 2373 % 2374 % o image: the image. 2375 % 2376 % o texture_image: This image is the texture to layer on the background. 2377 % 2378 */ 2379 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture, 2380 ExceptionInfo *exception) 2381 { 2382 #define TextureImageTag "Texture/Image" 2383 2384 CacheView 2385 *image_view, 2386 *texture_view; 2387 2388 Image 2389 *texture_image; 2390 2391 MagickBooleanType 2392 status; 2393 2394 ssize_t 2395 y; 2396 2397 assert(image != (Image *) NULL); 2398 if (image->debug != MagickFalse) 2399 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 2400 assert(image->signature == MagickCoreSignature); 2401 if (texture == (const Image *) NULL) 2402 return(MagickFalse); 2403 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 2404 return(MagickFalse); 2405 texture_image=CloneImage(texture,0,0,MagickTrue,exception); 2406 if (texture_image == (const Image *) NULL) 2407 return(MagickFalse); 2408 (void) TransformImageColorspace(texture_image,image->colorspace,exception); 2409 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod, 2410 exception); 2411 status=MagickTrue; 2412 if ((image->compose != CopyCompositeOp) && 2413 ((image->compose != OverCompositeOp) || 2414 (image->alpha_trait != UndefinedPixelTrait) || 2415 (texture_image->alpha_trait != UndefinedPixelTrait))) 2416 { 2417 /* 2418 Tile texture onto the image background. 2419 */ 2420 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows) 2421 { 2422 register ssize_t 2423 x; 2424 2425 if (status == MagickFalse) 2426 continue; 2427 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns) 2428 { 2429 MagickBooleanType 2430 thread_status; 2431 2432 thread_status=CompositeImage(image,texture_image,image->compose, 2433 MagickTrue,x+texture_image->tile_offset.x,y+ 2434 texture_image->tile_offset.y,exception); 2435 if (thread_status == MagickFalse) 2436 { 2437 status=thread_status; 2438 break; 2439 } 2440 } 2441 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2442 { 2443 MagickBooleanType 2444 proceed; 2445 2446 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y, 2447 image->rows); 2448 if (proceed == MagickFalse) 2449 status=MagickFalse; 2450 } 2451 } 2452 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType) 2453 image->rows,image->rows); 2454 texture_image=DestroyImage(texture_image); 2455 return(status); 2456 } 2457 /* 2458 Tile texture onto the image background (optimized). 2459 */ 2460 status=MagickTrue; 2461 texture_view=AcquireVirtualCacheView(texture_image,exception); 2462 image_view=AcquireAuthenticCacheView(image,exception); 2463 #if defined(MAGICKCORE_OPENMP_SUPPORT) 2464 #pragma omp parallel for schedule(static) shared(status) \ 2465 magick_number_threads(texture_image,image,image->rows,1) 2466 #endif 2467 for (y=0; y < (ssize_t) image->rows; y++) 2468 { 2469 MagickBooleanType 2470 sync; 2471 2472 register const Quantum 2473 *p, 2474 *pixels; 2475 2476 register ssize_t 2477 x; 2478 2479 register Quantum 2480 *q; 2481 2482 size_t 2483 width; 2484 2485 if (status == MagickFalse) 2486 continue; 2487 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x, 2488 (y+texture_image->tile_offset.y) % texture_image->rows, 2489 texture_image->columns,1,exception); 2490 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 2491 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 2492 { 2493 status=MagickFalse; 2494 continue; 2495 } 2496 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns) 2497 { 2498 register ssize_t 2499 j; 2500 2501 p=pixels; 2502 width=texture_image->columns; 2503 if ((x+(ssize_t) width) > (ssize_t) image->columns) 2504 width=image->columns-x; 2505 for (j=0; j < (ssize_t) width; j++) 2506 { 2507 register ssize_t 2508 i; 2509 2510 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++) 2511 { 2512 PixelChannel channel = GetPixelChannelChannel(texture_image,i); 2513 PixelTrait traits = GetPixelChannelTraits(image,channel); 2514 PixelTrait texture_traits=GetPixelChannelTraits(texture_image, 2515 channel); 2516 if ((traits == UndefinedPixelTrait) || 2517 (texture_traits == UndefinedPixelTrait)) 2518 continue; 2519 SetPixelChannel(image,channel,p[i],q); 2520 } 2521 p+=GetPixelChannels(texture_image); 2522 q+=GetPixelChannels(image); 2523 } 2524 } 2525 sync=SyncCacheViewAuthenticPixels(image_view,exception); 2526 if (sync == MagickFalse) 2527 status=MagickFalse; 2528 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2529 { 2530 MagickBooleanType 2531 proceed; 2532 2533 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y, 2534 image->rows); 2535 if (proceed == MagickFalse) 2536 status=MagickFalse; 2537 } 2538 } 2539 texture_view=DestroyCacheView(texture_view); 2540 image_view=DestroyCacheView(image_view); 2541 texture_image=DestroyImage(texture_image); 2542 return(status); 2543 } 2544