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