|
MagickCore
6.7.5
|
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE % 00007 % C O O MM MM P P O O SS I T E % 00008 % C O O M M M PPPP O O SSS I T EEE % 00009 % C O O M M P O O SS I T E % 00010 % CCCC OOO M M P OOO SSSSS IIIII T EEEEE % 00011 % % 00012 % % 00013 % MagickCore Image Composite Methods % 00014 % % 00015 % Software Design % 00016 % John Cristy % 00017 % July 1992 % 00018 % % 00019 % % 00020 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % 00021 % dedicated to making software imaging solutions freely available. % 00022 % % 00023 % You may not use this file except in compliance with the License. You may % 00024 % obtain a copy of the License at % 00025 % % 00026 % http://www.imagemagick.org/script/license.php % 00027 % % 00028 % Unless required by applicable law or agreed to in writing, software % 00029 % distributed under the License is distributed on an "AS IS" BASIS, % 00030 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00031 % See the License for the specific language governing permissions and % 00032 % limitations under the License. % 00033 % % 00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00035 % 00036 % 00037 % 00038 */ 00039 00040 /* 00041 Include declarations. 00042 */ 00043 #include "MagickCore/studio.h" 00044 #include "MagickCore/artifact.h" 00045 #include "MagickCore/cache.h" 00046 #include "MagickCore/cache-private.h" 00047 #include "MagickCore/cache-view.h" 00048 #include "MagickCore/client.h" 00049 #include "MagickCore/color.h" 00050 #include "MagickCore/color-private.h" 00051 #include "MagickCore/colorspace.h" 00052 #include "MagickCore/colorspace-private.h" 00053 #include "MagickCore/composite.h" 00054 #include "MagickCore/composite-private.h" 00055 #include "MagickCore/constitute.h" 00056 #include "MagickCore/draw.h" 00057 #include "MagickCore/fx.h" 00058 #include "MagickCore/gem.h" 00059 #include "MagickCore/geometry.h" 00060 #include "MagickCore/image.h" 00061 #include "MagickCore/image-private.h" 00062 #include "MagickCore/list.h" 00063 #include "MagickCore/log.h" 00064 #include "MagickCore/monitor.h" 00065 #include "MagickCore/monitor-private.h" 00066 #include "MagickCore/memory_.h" 00067 #include "MagickCore/option.h" 00068 #include "MagickCore/pixel-accessor.h" 00069 #include "MagickCore/property.h" 00070 #include "MagickCore/quantum.h" 00071 #include "MagickCore/resample.h" 00072 #include "MagickCore/resource_.h" 00073 #include "MagickCore/string_.h" 00074 #include "MagickCore/thread-private.h" 00075 #include "MagickCore/utility.h" 00076 #include "MagickCore/utility-private.h" 00077 #include "MagickCore/version.h" 00078 00079 /* 00080 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00081 % % 00082 % % 00083 % % 00084 % C o m p o s i t e I m a g e % 00085 % % 00086 % % 00087 % % 00088 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00089 % 00090 % CompositeImage() returns the second image composited onto the first 00091 % at the specified offset, using the specified composite method. 00092 % 00093 % The format of the CompositeImage method is: 00094 % 00095 % MagickBooleanType CompositeImage(Image *image, 00096 % const CompositeOperator compose,Image *composite_image, 00097 % const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception) 00098 % 00099 % A description of each parameter follows: 00100 % 00101 % o image: the destination image, modified by he composition 00102 % 00103 % o compose: This operator affects how the composite is applied to 00104 % the image. The operators and how they are utilized are listed here 00105 % http://www.w3.org/TR/SVG12/#compositing. 00106 % 00107 % o composite_image: the composite (source) image. 00108 % 00109 % o x_offset: the column offset of the composited image. 00110 % 00111 % o y_offset: the row offset of the composited image. 00112 % 00113 % Extra Controls from Image meta-data in 'composite_image' (artifacts) 00114 % 00115 % o "compose:args" 00116 % A string containing extra numerical arguments for specific compose 00117 % methods, generally expressed as a 'geometry' or a comma separated list 00118 % of numbers. 00119 % 00120 % Compose methods needing such arguments include "BlendCompositeOp" and 00121 % "DisplaceCompositeOp". 00122 % 00123 % o "compose:outside-overlay" 00124 % Modify how the composition is to effect areas not directly covered 00125 % by the 'composite_image' at the offset given. Normally this is 00126 % dependant on the 'compose' method, especially Duff-Porter methods. 00127 % 00128 % If set to "false" then disable all normal handling of pixels not 00129 % covered by the composite_image. Typically used for repeated tiling 00130 % of the composite_image by the calling API. 00131 % 00132 % Previous to IM v6.5.3-3 this was called "modify-outside-overlay" 00133 % 00134 % o exception: return any errors or warnings in this structure. 00135 % 00136 */ 00137 00138 static void CompositeHSB(const Quantum red,const Quantum green, 00139 const Quantum blue,double *hue,double *saturation,double *brightness) 00140 { 00141 double 00142 delta; 00143 00144 Quantum 00145 max, 00146 min; 00147 00148 /* 00149 Convert RGB to HSB colorspace. 00150 */ 00151 assert(hue != (double *) NULL); 00152 assert(saturation != (double *) NULL); 00153 assert(brightness != (double *) NULL); 00154 max=(red > green ? red : green); 00155 if (blue > max) 00156 max=blue; 00157 min=(red < green ? red : green); 00158 if (blue < min) 00159 min=blue; 00160 *hue=0.0; 00161 *saturation=0.0; 00162 *brightness=(double) (QuantumScale*max); 00163 if (fabs((double) max) < MagickEpsilon) 00164 return; 00165 *saturation=(double) (1.0-min/max); 00166 delta=(MagickRealType) max-min; 00167 if (fabs(delta) < MagickEpsilon) 00168 return; 00169 if (fabs((double) red-max) < MagickEpsilon) 00170 *hue=(double) ((green-blue)/delta); 00171 else 00172 if (fabs((double) green-max) < MagickEpsilon) 00173 *hue=(double) (2.0+(blue-red)/delta); 00174 else 00175 if (fabs((double) blue-max) < MagickEpsilon) 00176 *hue=(double) (4.0+(red-green)/delta); 00177 *hue/=6.0; 00178 if (*hue < 0.0) 00179 *hue+=1.0; 00180 } 00181 00182 static void HSBComposite(const double hue,const double saturation, 00183 const double brightness,double *red,double *green,double *blue) 00184 { 00185 double 00186 f, 00187 h, 00188 p, 00189 q, 00190 t; 00191 00192 /* 00193 Convert HSB to RGB colorspace. 00194 */ 00195 assert(red != (double *) NULL); 00196 assert(green != (double *) NULL); 00197 assert(blue != (double *) NULL); 00198 if (saturation == 0.0) 00199 { 00200 *red=(double) QuantumRange*brightness; 00201 *green=(*red); 00202 *blue=(*red); 00203 return; 00204 } 00205 h=6.0*(hue-floor(hue)); 00206 f=h-floor((double) h); 00207 p=brightness*(1.0-saturation); 00208 q=brightness*(1.0-saturation*f); 00209 t=brightness*(1.0-saturation*(1.0-f)); 00210 switch ((int) h) 00211 { 00212 case 0: 00213 default: 00214 { 00215 *red=(double) QuantumRange*brightness; 00216 *green=(double) QuantumRange*t; 00217 *blue=(double) QuantumRange*p; 00218 break; 00219 } 00220 case 1: 00221 { 00222 *red=(double) QuantumRange*q; 00223 *green=(double) QuantumRange*brightness; 00224 *blue=(double) QuantumRange*p; 00225 break; 00226 } 00227 case 2: 00228 { 00229 *red=(double) QuantumRange*p; 00230 *green=(double) QuantumRange*brightness; 00231 *blue=(double) QuantumRange*t; 00232 break; 00233 } 00234 case 3: 00235 { 00236 *red=(double) QuantumRange*p; 00237 *green=(double) QuantumRange*q; 00238 *blue=(double) QuantumRange*brightness; 00239 break; 00240 } 00241 case 4: 00242 { 00243 *red=(double) QuantumRange*t; 00244 *green=(double) QuantumRange*p; 00245 *blue=(double) QuantumRange*brightness; 00246 break; 00247 } 00248 case 5: 00249 { 00250 *red=(double) QuantumRange*brightness; 00251 *green=(double) QuantumRange*p; 00252 *blue=(double) QuantumRange*q; 00253 break; 00254 } 00255 } 00256 } 00257 00258 static inline double MagickMin(const double x,const double y) 00259 { 00260 if (x < y) 00261 return(x); 00262 return(y); 00263 } 00264 static inline double MagickMax(const double x,const double y) 00265 { 00266 if (x > y) 00267 return(x); 00268 return(y); 00269 } 00270 00271 static MagickBooleanType CompositeOverImage(Image *image, 00272 const Image *composite_image,const ssize_t x_offset,const ssize_t y_offset, 00273 ExceptionInfo *exception) 00274 { 00275 #define CompositeImageTag "Composite/Image" 00276 00277 CacheView 00278 *composite_view, 00279 *image_view; 00280 00281 const char 00282 *value; 00283 00284 MagickBooleanType 00285 modify_outside_overlay, 00286 status; 00287 00288 MagickOffsetType 00289 progress; 00290 00291 ssize_t 00292 y; 00293 00294 /* 00295 Prepare composite image. 00296 */ 00297 modify_outside_overlay=MagickFalse; 00298 value=GetImageArtifact(composite_image,"compose:outside-overlay"); 00299 if (value != (const char *) NULL) 00300 modify_outside_overlay=IsMagickTrue(value); 00301 /* 00302 Composite image. 00303 */ 00304 status=MagickTrue; 00305 progress=0; 00306 image_view=AcquireCacheView(image); 00307 composite_view=AcquireCacheView(composite_image); 00308 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00309 #pragma omp parallel for schedule(static,4) shared(progress,status) 00310 #endif 00311 for (y=0; y < (ssize_t) image->rows; y++) 00312 { 00313 const Quantum 00314 *pixels; 00315 00316 register const Quantum 00317 *restrict p; 00318 00319 register Quantum 00320 *restrict q; 00321 00322 register ssize_t 00323 x; 00324 00325 size_t 00326 channels; 00327 00328 if (status == MagickFalse) 00329 continue; 00330 if (modify_outside_overlay == MagickFalse) 00331 { 00332 if (y < y_offset) 00333 continue; 00334 if ((y-y_offset) >= (ssize_t) composite_image->rows) 00335 continue; 00336 } 00337 /* 00338 If pixels is NULL, y is outside overlay region. 00339 */ 00340 pixels=(Quantum *) NULL; 00341 p=(Quantum *) NULL; 00342 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows)) 00343 { 00344 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset, 00345 composite_image->columns,1,exception); 00346 if (p == (const Quantum *) NULL) 00347 { 00348 status=MagickFalse; 00349 continue; 00350 } 00351 pixels=p; 00352 if (x_offset < 0) 00353 p-=x_offset*GetPixelChannels(composite_image); 00354 } 00355 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 00356 if (q == (Quantum *) NULL) 00357 { 00358 status=MagickFalse; 00359 continue; 00360 } 00361 for (x=0; x < (ssize_t) image->columns; x++) 00362 { 00363 MagickRealType 00364 alpha, 00365 Da, 00366 Dc, 00367 gamma, 00368 Sa, 00369 Sc; 00370 00371 register ssize_t 00372 i; 00373 00374 if (modify_outside_overlay == MagickFalse) 00375 { 00376 if (x < x_offset) 00377 { 00378 q+=GetPixelChannels(image); 00379 continue; 00380 } 00381 if ((x-x_offset) >= (ssize_t) composite_image->columns) 00382 break; 00383 } 00384 if ((pixels == (Quantum *) NULL) || (x < x_offset) || 00385 ((x-x_offset) >= (ssize_t) composite_image->columns)) 00386 { 00387 Quantum 00388 source[MaxPixelChannels]; 00389 00390 /* 00391 Virtual composite: 00392 Sc: source color. 00393 Dc: destination color. 00394 */ 00395 if (GetPixelMask(image,q) != 0) 00396 { 00397 q+=GetPixelChannels(image); 00398 continue; 00399 } 00400 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset, 00401 source,exception); 00402 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 00403 { 00404 PixelChannel 00405 channel; 00406 00407 PixelTrait 00408 composite_traits, 00409 traits; 00410 00411 channel=GetPixelChannelMapChannel(image,i); 00412 traits=GetPixelChannelMapTraits(image,channel); 00413 composite_traits=GetPixelChannelMapTraits(composite_image,channel); 00414 if ((traits == UndefinedPixelTrait) || 00415 (composite_traits == UndefinedPixelTrait)) 00416 continue; 00417 q[i]=source[channel]; 00418 } 00419 q+=GetPixelChannels(image); 00420 continue; 00421 } 00422 /* 00423 Authentic composite: 00424 Sa: normalized source alpha. 00425 Da: normalized destination alpha. 00426 */ 00427 if (GetPixelMask(composite_image,p) != 0) 00428 { 00429 p+=GetPixelChannels(composite_image); 00430 channels=GetPixelChannels(composite_image); 00431 if (p >= (pixels+channels*composite_image->columns)) 00432 p=pixels; 00433 q+=GetPixelChannels(image); 00434 continue; 00435 } 00436 Sa=QuantumScale*GetPixelAlpha(composite_image,p); 00437 Da=QuantumScale*GetPixelAlpha(image,q); 00438 alpha=Sa*(-Da)+Sa+Da; 00439 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 00440 { 00441 PixelChannel 00442 channel; 00443 00444 PixelTrait 00445 composite_traits, 00446 traits; 00447 00448 channel=GetPixelChannelMapChannel(image,i); 00449 traits=GetPixelChannelMapTraits(image,channel); 00450 composite_traits=GetPixelChannelMapTraits(composite_image,channel); 00451 if ((traits == UndefinedPixelTrait) || 00452 (composite_traits == UndefinedPixelTrait)) 00453 continue; 00454 if ((traits & CopyPixelTrait) != 0) 00455 { 00456 if (channel != AlphaPixelChannel) 00457 { 00458 /* 00459 Copy channel. 00460 */ 00461 q[i]=GetPixelChannel(composite_image,channel,p); 00462 continue; 00463 } 00464 /* 00465 Set alpha channel. 00466 */ 00467 q[i]=ClampToQuantum(QuantumRange*alpha); 00468 continue; 00469 } 00470 /* 00471 Sc: source color. 00472 Dc: destination color. 00473 */ 00474 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p); 00475 Dc=(MagickRealType) q[i]; 00476 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha); 00477 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc)); 00478 } 00479 p+=GetPixelChannels(composite_image); 00480 channels=GetPixelChannels(composite_image); 00481 if (p >= (pixels+channels*composite_image->columns)) 00482 p=pixels; 00483 q+=GetPixelChannels(image); 00484 } 00485 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 00486 status=MagickFalse; 00487 if (image->progress_monitor != (MagickProgressMonitor) NULL) 00488 { 00489 MagickBooleanType 00490 proceed; 00491 00492 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00493 #pragma omp critical (MagickCore_CompositeImage) 00494 #endif 00495 proceed=SetImageProgress(image,CompositeImageTag,progress++, 00496 image->rows); 00497 if (proceed == MagickFalse) 00498 status=MagickFalse; 00499 } 00500 } 00501 composite_view=DestroyCacheView(composite_view); 00502 image_view=DestroyCacheView(image_view); 00503 return(status); 00504 } 00505 00506 MagickExport MagickBooleanType CompositeImage(Image *image, 00507 const CompositeOperator compose,const Image *composite_image, 00508 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception) 00509 { 00510 #define CompositeImageTag "Composite/Image" 00511 00512 CacheView 00513 *composite_view, 00514 *image_view; 00515 00516 const char 00517 *value; 00518 00519 GeometryInfo 00520 geometry_info; 00521 00522 Image 00523 *destination_image; 00524 00525 MagickBooleanType 00526 modify_outside_overlay, 00527 status; 00528 00529 MagickOffsetType 00530 progress; 00531 00532 MagickRealType 00533 amount, 00534 destination_dissolve, 00535 midpoint, 00536 percent_brightness, 00537 percent_saturation, 00538 source_dissolve, 00539 threshold; 00540 00541 MagickStatusType 00542 flags; 00543 00544 ssize_t 00545 y; 00546 00547 /* 00548 Composition based on the SVG specification: 00549 00550 A Composition is defined by... 00551 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors 00552 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc) 00553 Y = 1 for source preserved 00554 Z = 1 for destination preserved 00555 00556 Conversion to transparency (then optimized) 00557 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa) 00558 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa) 00559 00560 Where... 00561 Sca = Sc*Sa normalized Source color divided by Source alpha 00562 Dca = Dc*Da normalized Dest color divided by Dest alpha 00563 Dc' = Dca'/Da' the desired color value for this channel. 00564 00565 Da' in in the follow formula as 'gamma' The resulting alpla value. 00566 00567 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in 00568 the following optimizations... 00569 gamma = Sa+Da-Sa*Da; 00570 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta; 00571 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma 00572 00573 The above SVG definitions also definate that Mathematical Composition 00574 methods should use a 'Over' blending mode for Alpha Channel. 00575 It however was not applied for composition modes of 'Plus', 'Minus', 00576 the modulus versions of 'Add' and 'Subtract'. 00577 00578 Mathematical operator changes to be applied from IM v6.7... 00579 00580 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed 00581 'ModulusAdd' and 'ModulusSubtract' for clarity. 00582 00583 2) All mathematical compositions work as per the SVG specification 00584 with regard to blending. This now includes 'ModulusAdd' and 00585 'ModulusSubtract'. 00586 00587 3) When the special channel flag 'sync' (syncronize channel updates) 00588 is turned off (enabled by default) then mathematical compositions are 00589 only performed on the channels specified, and are applied 00590 independantally of each other. In other words the mathematics is 00591 performed as 'pure' mathematical operations, rather than as image 00592 operations. 00593 */ 00594 assert(image != (Image *) NULL); 00595 assert(image->signature == MagickSignature); 00596 if (image->debug != MagickFalse) 00597 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00598 assert(composite_image != (Image *) NULL); 00599 assert(composite_image->signature == MagickSignature); 00600 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 00601 return(MagickFalse); 00602 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp)) 00603 { 00604 status=CompositeOverImage(image,composite_image,x_offset,y_offset, 00605 exception); 00606 return(status); 00607 } 00608 destination_image=(Image *) NULL; 00609 amount=0.5; 00610 destination_dissolve=1.0; 00611 modify_outside_overlay=MagickFalse; 00612 percent_brightness=100.0; 00613 percent_saturation=100.0; 00614 source_dissolve=1.0; 00615 threshold=0.05f; 00616 switch (compose) 00617 { 00618 case ClearCompositeOp: 00619 case DstAtopCompositeOp: 00620 case DstInCompositeOp: 00621 case InCompositeOp: 00622 case OutCompositeOp: 00623 case SrcCompositeOp: 00624 case SrcInCompositeOp: 00625 case SrcOutCompositeOp: 00626 { 00627 /* 00628 Modify destination outside the overlaid region. 00629 */ 00630 modify_outside_overlay=MagickTrue; 00631 break; 00632 } 00633 case CopyCompositeOp: 00634 { 00635 if ((x_offset < 0) || (y_offset < 0)) 00636 break; 00637 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns) 00638 break; 00639 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows) 00640 break; 00641 status=MagickTrue; 00642 image_view=AcquireCacheView(image); 00643 composite_view=AcquireCacheView(composite_image); 00644 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00645 #pragma omp parallel for schedule(static,4) shared(status) 00646 #endif 00647 for (y=0; y < (ssize_t) composite_image->rows; y++) 00648 { 00649 MagickBooleanType 00650 sync; 00651 00652 register const Quantum 00653 *p; 00654 00655 register Quantum 00656 *q; 00657 00658 register ssize_t 00659 x; 00660 00661 if (status == MagickFalse) 00662 continue; 00663 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns, 00664 1,exception); 00665 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset, 00666 composite_image->columns,1,exception); 00667 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 00668 { 00669 status=MagickFalse; 00670 continue; 00671 } 00672 for (x=0; x < (ssize_t) composite_image->columns; x++) 00673 { 00674 register ssize_t 00675 i; 00676 00677 if (GetPixelMask(image,p) != 0) 00678 { 00679 p+=GetPixelChannels(composite_image); 00680 q+=GetPixelChannels(image); 00681 continue; 00682 } 00683 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++) 00684 { 00685 PixelChannel 00686 channel; 00687 00688 PixelTrait 00689 composite_traits, 00690 traits; 00691 00692 channel=GetPixelChannelMapChannel(composite_image,i); 00693 composite_traits=GetPixelChannelMapTraits(composite_image,channel); 00694 traits=GetPixelChannelMapTraits(image,channel); 00695 if ((traits == UndefinedPixelTrait) || 00696 (composite_traits == UndefinedPixelTrait)) 00697 continue; 00698 SetPixelChannel(image,channel,p[i],q); 00699 } 00700 p+=GetPixelChannels(composite_image); 00701 q+=GetPixelChannels(image); 00702 } 00703 sync=SyncCacheViewAuthenticPixels(image_view,exception); 00704 if (sync == MagickFalse) 00705 status=MagickFalse; 00706 if (image->progress_monitor != (MagickProgressMonitor) NULL) 00707 { 00708 MagickBooleanType 00709 proceed; 00710 00711 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00712 #pragma omp critical (MagickCore_CompositeImage) 00713 #endif 00714 proceed=SetImageProgress(image,CompositeImageTag, 00715 (MagickOffsetType) y,image->rows); 00716 if (proceed == MagickFalse) 00717 status=MagickFalse; 00718 } 00719 } 00720 composite_view=DestroyCacheView(composite_view); 00721 image_view=DestroyCacheView(image_view); 00722 return(status); 00723 } 00724 case CopyAlphaCompositeOp: 00725 case ChangeMaskCompositeOp: 00726 case IntensityCompositeOp: 00727 { 00728 /* 00729 Modify destination outside the overlaid region and require an alpha 00730 channel to exist, to add transparency. 00731 */ 00732 if (image->matte == MagickFalse) 00733 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); 00734 modify_outside_overlay=MagickTrue; 00735 break; 00736 } 00737 case BlurCompositeOp: 00738 { 00739 CacheView 00740 *composite_view, 00741 *destination_view; 00742 00743 PixelInfo 00744 pixel; 00745 00746 MagickRealType 00747 angle_range, 00748 angle_start, 00749 height, 00750 width; 00751 00752 ResampleFilter 00753 *resample_filter; 00754 00755 SegmentInfo 00756 blur; 00757 00758 /* 00759 Blur Image dictated by an overlay gradient map: X = red_channel; 00760 Y = green_channel; compose:args = x_scale[,y_scale[,angle]]. 00761 */ 00762 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue, 00763 exception); 00764 if (destination_image == (Image *) NULL) 00765 return(MagickFalse); 00766 /* 00767 Determine the horizontal and vertical maximim blur. 00768 */ 00769 SetGeometryInfo(&geometry_info); 00770 flags=NoValue; 00771 value=GetImageArtifact(composite_image,"compose:args"); 00772 if (value != (char *) NULL) 00773 flags=ParseGeometry(value,&geometry_info); 00774 if ((flags & WidthValue) == 0 ) 00775 { 00776 destination_image=DestroyImage(destination_image); 00777 return(MagickFalse); 00778 } 00779 width=geometry_info.rho; 00780 height=geometry_info.sigma; 00781 blur.x1=geometry_info.rho; 00782 blur.x2=0.0; 00783 blur.y1=0.0; 00784 blur.y2=geometry_info.sigma; 00785 angle_start=0.0; 00786 angle_range=0.0; 00787 if ((flags & HeightValue) == 0) 00788 blur.y2=blur.x1; 00789 if ((flags & XValue) != 0 ) 00790 { 00791 MagickRealType 00792 angle; 00793 00794 angle=DegreesToRadians(geometry_info.xi); 00795 blur.x1=width*cos(angle); 00796 blur.x2=width*sin(angle); 00797 blur.y1=(-height*sin(angle)); 00798 blur.y2=height*cos(angle); 00799 } 00800 if ((flags & YValue) != 0 ) 00801 { 00802 angle_start=DegreesToRadians(geometry_info.xi); 00803 angle_range=DegreesToRadians(geometry_info.psi)-angle_start; 00804 } 00805 /* 00806 Blur Image by resampling. 00807 */ 00808 resample_filter=AcquireResampleFilter(image,exception); 00809 SetResampleFilter(resample_filter,CubicFilter,2.0); 00810 destination_view=AcquireCacheView(destination_image); 00811 composite_view=AcquireCacheView(composite_image); 00812 for (y=0; y < (ssize_t) composite_image->rows; y++) 00813 { 00814 MagickBooleanType 00815 sync; 00816 00817 register const Quantum 00818 *restrict p; 00819 00820 register Quantum 00821 *restrict q; 00822 00823 register ssize_t 00824 x; 00825 00826 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows)) 00827 continue; 00828 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns, 00829 1,exception); 00830 q=QueueCacheViewAuthenticPixels(destination_view,0,y, 00831 destination_image->columns,1,exception); 00832 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 00833 break; 00834 for (x=0; x < (ssize_t) composite_image->columns; x++) 00835 { 00836 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns)) 00837 { 00838 p+=GetPixelChannels(composite_image); 00839 continue; 00840 } 00841 if (fabs(angle_range) > MagickEpsilon) 00842 { 00843 MagickRealType 00844 angle; 00845 00846 angle=angle_start+angle_range*QuantumScale* 00847 GetPixelBlue(composite_image,p); 00848 blur.x1=width*cos(angle); 00849 blur.x2=width*sin(angle); 00850 blur.y1=(-height*sin(angle)); 00851 blur.y2=height*cos(angle); 00852 } 00853 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale* 00854 GetPixelRed(composite_image,p),blur.y1*QuantumScale* 00855 GetPixelGreen(composite_image,p),blur.x2*QuantumScale* 00856 GetPixelRed(composite_image,p),blur.y2*QuantumScale* 00857 GetPixelGreen(composite_image,p)); 00858 (void) ResamplePixelColor(resample_filter,(double) x_offset+x, 00859 (double) y_offset+y,&pixel); 00860 SetPixelInfoPixel(destination_image,&pixel,q); 00861 p+=GetPixelChannels(composite_image); 00862 q+=GetPixelChannels(destination_image); 00863 } 00864 sync=SyncCacheViewAuthenticPixels(destination_view,exception); 00865 if (sync == MagickFalse) 00866 break; 00867 } 00868 resample_filter=DestroyResampleFilter(resample_filter); 00869 composite_view=DestroyCacheView(composite_view); 00870 destination_view=DestroyCacheView(destination_view); 00871 composite_image=destination_image; 00872 break; 00873 } 00874 case DisplaceCompositeOp: 00875 case DistortCompositeOp: 00876 { 00877 CacheView 00878 *composite_view, 00879 *destination_view, 00880 *image_view; 00881 00882 PixelInfo 00883 pixel; 00884 00885 MagickRealType 00886 horizontal_scale, 00887 vertical_scale; 00888 00889 PointInfo 00890 center, 00891 offset; 00892 00893 /* 00894 Displace/Distort based on overlay gradient map: 00895 X = red_channel; Y = green_channel; 00896 compose:args = x_scale[,y_scale[,center.x,center.y]] 00897 */ 00898 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue, 00899 exception); 00900 if (destination_image == (Image *) NULL) 00901 return(MagickFalse); 00902 SetGeometryInfo(&geometry_info); 00903 flags=NoValue; 00904 value=GetImageArtifact(composite_image,"compose:args"); 00905 if (value != (char *) NULL) 00906 flags=ParseGeometry(value,&geometry_info); 00907 if ((flags & (WidthValue|HeightValue)) == 0 ) 00908 { 00909 if ((flags & AspectValue) == 0) 00910 { 00911 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/ 00912 2.0; 00913 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0; 00914 } 00915 else 00916 { 00917 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0; 00918 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0; 00919 } 00920 } 00921 else 00922 { 00923 horizontal_scale=geometry_info.rho; 00924 vertical_scale=geometry_info.sigma; 00925 if ((flags & PercentValue) != 0) 00926 { 00927 if ((flags & AspectValue) == 0) 00928 { 00929 horizontal_scale*=(composite_image->columns-1.0)/200.0; 00930 vertical_scale*=(composite_image->rows-1.0)/200.0; 00931 } 00932 else 00933 { 00934 horizontal_scale*=(image->columns-1.0)/200.0; 00935 vertical_scale*=(image->rows-1.0)/200.0; 00936 } 00937 } 00938 if ((flags & HeightValue) == 0) 00939 vertical_scale=horizontal_scale; 00940 } 00941 /* 00942 Determine fixed center point for absolute distortion map 00943 Absolute distort == 00944 Displace offset relative to a fixed absolute point 00945 Select that point according to +X+Y user inputs. 00946 default = center of overlay image 00947 arg flag '!' = locations/percentage relative to background image 00948 */ 00949 center.x=(MagickRealType) x_offset; 00950 center.y=(MagickRealType) y_offset; 00951 if (compose == DistortCompositeOp) 00952 { 00953 if ((flags & XValue) == 0) 00954 if ((flags & AspectValue) == 0) 00955 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/ 00956 2.0; 00957 else 00958 center.x=((MagickRealType) image->columns-1)/2.0; 00959 else 00960 if ((flags & AspectValue) == 0) 00961 center.x=(MagickRealType) x_offset+geometry_info.xi; 00962 else 00963 center.x=geometry_info.xi; 00964 if ((flags & YValue) == 0) 00965 if ((flags & AspectValue) == 0) 00966 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0; 00967 else 00968 center.y=((MagickRealType) image->rows-1)/2.0; 00969 else 00970 if ((flags & AspectValue) == 0) 00971 center.y=(MagickRealType) y_offset+geometry_info.psi; 00972 else 00973 center.y=geometry_info.psi; 00974 } 00975 /* 00976 Shift the pixel offset point as defined by the provided, 00977 displacement/distortion map. -- Like a lens... 00978 */ 00979 GetPixelInfo(image,&pixel); 00980 image_view=AcquireCacheView(image); 00981 destination_view=AcquireCacheView(destination_image); 00982 composite_view=AcquireCacheView(composite_image); 00983 for (y=0; y < (ssize_t) composite_image->rows; y++) 00984 { 00985 MagickBooleanType 00986 sync; 00987 00988 register const Quantum 00989 *restrict p; 00990 00991 register Quantum 00992 *restrict q; 00993 00994 register ssize_t 00995 x; 00996 00997 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows)) 00998 continue; 00999 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns, 01000 1,exception); 01001 q=QueueCacheViewAuthenticPixels(destination_view,0,y, 01002 destination_image->columns,1,exception); 01003 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 01004 break; 01005 for (x=0; x < (ssize_t) composite_image->columns; x++) 01006 { 01007 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns)) 01008 { 01009 p+=GetPixelChannels(composite_image); 01010 continue; 01011 } 01012 /* 01013 Displace the offset. 01014 */ 01015 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)- 01016 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType) 01017 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ? 01018 x : 0); 01019 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)- 01020 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType) 01021 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ? 01022 y : 0); 01023 (void) InterpolatePixelInfo(image,image_view, 01024 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y, 01025 &pixel,exception); 01026 /* 01027 Mask with the 'invalid pixel mask' in alpha channel. 01028 */ 01029 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale* 01030 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p))); 01031 SetPixelInfoPixel(destination_image,&pixel,q); 01032 p+=GetPixelChannels(composite_image); 01033 q+=GetPixelChannels(destination_image); 01034 } 01035 sync=SyncCacheViewAuthenticPixels(destination_view,exception); 01036 if (sync == MagickFalse) 01037 break; 01038 } 01039 destination_view=DestroyCacheView(destination_view); 01040 composite_view=DestroyCacheView(composite_view); 01041 image_view=DestroyCacheView(image_view); 01042 composite_image=destination_image; 01043 break; 01044 } 01045 case DissolveCompositeOp: 01046 { 01047 /* 01048 Geometry arguments to dissolve factors. 01049 */ 01050 value=GetImageArtifact(composite_image,"compose:args"); 01051 if (value != (char *) NULL) 01052 { 01053 flags=ParseGeometry(value,&geometry_info); 01054 source_dissolve=geometry_info.rho/100.0; 01055 destination_dissolve=1.0; 01056 if ((source_dissolve-MagickEpsilon) < 0.0) 01057 source_dissolve=0.0; 01058 if ((source_dissolve+MagickEpsilon) > 1.0) 01059 { 01060 destination_dissolve=2.0-source_dissolve; 01061 source_dissolve=1.0; 01062 } 01063 if ((flags & SigmaValue) != 0) 01064 destination_dissolve=geometry_info.sigma/100.0; 01065 if ((destination_dissolve-MagickEpsilon) < 0.0) 01066 destination_dissolve=0.0; 01067 modify_outside_overlay=MagickTrue; 01068 if ((destination_dissolve+MagickEpsilon) > 1.0 ) 01069 { 01070 destination_dissolve=1.0; 01071 modify_outside_overlay=MagickFalse; 01072 } 01073 } 01074 break; 01075 } 01076 case BlendCompositeOp: 01077 { 01078 value=GetImageArtifact(composite_image,"compose:args"); 01079 if (value != (char *) NULL) 01080 { 01081 flags=ParseGeometry(value,&geometry_info); 01082 source_dissolve=geometry_info.rho/100.0; 01083 destination_dissolve=1.0-source_dissolve; 01084 if ((flags & SigmaValue) != 0) 01085 destination_dissolve=geometry_info.sigma/100.0; 01086 modify_outside_overlay=MagickTrue; 01087 if ((destination_dissolve+MagickEpsilon) > 1.0) 01088 modify_outside_overlay=MagickFalse; 01089 } 01090 break; 01091 } 01092 case MathematicsCompositeOp: 01093 { 01094 /* 01095 Just collect the values from "compose:args", setting. 01096 Unused values are set to zero automagically. 01097 01098 Arguments are normally a comma separated list, so this probably should 01099 be changed to some 'general comma list' parser, (with a minimum 01100 number of values) 01101 */ 01102 SetGeometryInfo(&geometry_info); 01103 value=GetImageArtifact(composite_image,"compose:args"); 01104 if (value != (char *) NULL) 01105 (void) ParseGeometry(value,&geometry_info); 01106 break; 01107 } 01108 case ModulateCompositeOp: 01109 { 01110 /* 01111 Determine the brightness and saturation scale. 01112 */ 01113 value=GetImageArtifact(composite_image,"compose:args"); 01114 if (value != (char *) NULL) 01115 { 01116 flags=ParseGeometry(value,&geometry_info); 01117 percent_brightness=geometry_info.rho; 01118 if ((flags & SigmaValue) != 0) 01119 percent_saturation=geometry_info.sigma; 01120 } 01121 break; 01122 } 01123 case ThresholdCompositeOp: 01124 { 01125 /* 01126 Determine the amount and threshold. 01127 */ 01128 value=GetImageArtifact(composite_image,"compose:args"); 01129 if (value != (char *) NULL) 01130 { 01131 flags=ParseGeometry(value,&geometry_info); 01132 amount=geometry_info.rho; 01133 threshold=geometry_info.sigma; 01134 if ((flags & SigmaValue) == 0) 01135 threshold=0.05f; 01136 } 01137 threshold*=QuantumRange; 01138 break; 01139 } 01140 default: 01141 break; 01142 } 01143 value=GetImageArtifact(composite_image,"compose:outside-overlay"); 01144 if (value != (const char *) NULL) 01145 modify_outside_overlay=IsMagickTrue(value); 01146 /* 01147 Composite image. 01148 */ 01149 status=MagickTrue; 01150 progress=0; 01151 midpoint=((MagickRealType) QuantumRange+1.0)/2; 01152 image_view=AcquireCacheView(image); 01153 composite_view=AcquireCacheView(composite_image); 01154 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01155 #pragma omp parallel for schedule(static,4) shared(progress,status) 01156 #endif 01157 for (y=0; y < (ssize_t) image->rows; y++) 01158 { 01159 const Quantum 01160 *pixels; 01161 01162 double 01163 blue, 01164 brightness, 01165 green, 01166 hue, 01167 red, 01168 saturation; 01169 01170 register const Quantum 01171 *restrict p; 01172 01173 register Quantum 01174 *restrict q; 01175 01176 register ssize_t 01177 x; 01178 01179 if (status == MagickFalse) 01180 continue; 01181 if (modify_outside_overlay == MagickFalse) 01182 { 01183 if (y < y_offset) 01184 continue; 01185 if ((y-y_offset) >= (ssize_t) composite_image->rows) 01186 continue; 01187 } 01188 /* 01189 If pixels is NULL, y is outside overlay region. 01190 */ 01191 pixels=(Quantum *) NULL; 01192 p=(Quantum *) NULL; 01193 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows)) 01194 { 01195 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset, 01196 composite_image->columns,1,exception); 01197 if (p == (const Quantum *) NULL) 01198 { 01199 status=MagickFalse; 01200 continue; 01201 } 01202 pixels=p; 01203 if (x_offset < 0) 01204 p-=x_offset*GetPixelChannels(composite_image); 01205 } 01206 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 01207 if (q == (Quantum *) NULL) 01208 { 01209 status=MagickFalse; 01210 continue; 01211 } 01212 hue=0.0; 01213 saturation=0.0; 01214 brightness=0.0; 01215 for (x=0; x < (ssize_t) image->columns; x++) 01216 { 01217 MagickRealType 01218 alpha, 01219 Da, 01220 Dc, 01221 Dca, 01222 gamma, 01223 Sa, 01224 Sc, 01225 Sca; 01226 01227 register ssize_t 01228 i; 01229 01230 size_t 01231 channels; 01232 01233 if (modify_outside_overlay == MagickFalse) 01234 { 01235 if (x < x_offset) 01236 { 01237 q+=GetPixelChannels(image); 01238 continue; 01239 } 01240 if ((x-x_offset) >= (ssize_t) composite_image->columns) 01241 break; 01242 } 01243 if ((pixels == (Quantum *) NULL) || (x < x_offset) || 01244 ((x-x_offset) >= (ssize_t) composite_image->columns)) 01245 { 01246 Quantum 01247 source[MaxPixelChannels]; 01248 01249 /* 01250 Virtual composite: 01251 Sc: source color. 01252 Dc: destination color. 01253 */ 01254 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset, 01255 source,exception); 01256 if (GetPixelMask(image,q) != 0) 01257 { 01258 q+=GetPixelChannels(image); 01259 continue; 01260 } 01261 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 01262 { 01263 MagickRealType 01264 pixel; 01265 01266 PixelChannel 01267 channel; 01268 01269 PixelTrait 01270 composite_traits, 01271 traits; 01272 01273 channel=GetPixelChannelMapChannel(image,i); 01274 traits=GetPixelChannelMapTraits(image,channel); 01275 composite_traits=GetPixelChannelMapTraits(composite_image,channel); 01276 if ((traits == UndefinedPixelTrait) || 01277 (composite_traits == UndefinedPixelTrait)) 01278 continue; 01279 switch (compose) 01280 { 01281 case AlphaCompositeOp: 01282 case ChangeMaskCompositeOp: 01283 case CopyAlphaCompositeOp: 01284 case DstAtopCompositeOp: 01285 case DstInCompositeOp: 01286 case InCompositeOp: 01287 case IntensityCompositeOp: 01288 case OutCompositeOp: 01289 case SrcInCompositeOp: 01290 case SrcOutCompositeOp: 01291 { 01292 pixel=(MagickRealType) q[i]; 01293 if (channel == AlphaPixelChannel) 01294 pixel=(MagickRealType) TransparentAlpha; 01295 break; 01296 } 01297 case ClearCompositeOp: 01298 case CopyCompositeOp: 01299 case ReplaceCompositeOp: 01300 case SrcCompositeOp: 01301 { 01302 if (channel == AlphaPixelChannel) 01303 { 01304 pixel=(MagickRealType) TransparentAlpha; 01305 break; 01306 } 01307 pixel=0.0; 01308 break; 01309 } 01310 case BlendCompositeOp: 01311 case DissolveCompositeOp: 01312 { 01313 if (channel == AlphaPixelChannel) 01314 { 01315 pixel=destination_dissolve*GetPixelAlpha(composite_image, 01316 source); 01317 break; 01318 } 01319 pixel=(MagickRealType) source[channel]; 01320 break; 01321 } 01322 default: 01323 { 01324 pixel=(MagickRealType) source[channel]; 01325 break; 01326 } 01327 } 01328 q[i]=ClampToQuantum(pixel); 01329 } 01330 q+=GetPixelChannels(image); 01331 continue; 01332 } 01333 /* 01334 Authentic composite: 01335 Sa: normalized source alpha. 01336 Da: normalized destination alpha. 01337 */ 01338 Sa=QuantumScale*GetPixelAlpha(composite_image,p); 01339 Da=QuantumScale*GetPixelAlpha(image,q); 01340 switch (compose) 01341 { 01342 case BumpmapCompositeOp: 01343 { 01344 alpha=GetPixelIntensity(composite_image,p)*Sa; 01345 break; 01346 } 01347 case ColorBurnCompositeOp: 01348 case ColorDodgeCompositeOp: 01349 case DifferenceCompositeOp: 01350 case DivideDstCompositeOp: 01351 case DivideSrcCompositeOp: 01352 case ExclusionCompositeOp: 01353 case HardLightCompositeOp: 01354 case LinearBurnCompositeOp: 01355 case LinearDodgeCompositeOp: 01356 case LinearLightCompositeOp: 01357 case MathematicsCompositeOp: 01358 case MinusDstCompositeOp: 01359 case MinusSrcCompositeOp: 01360 case ModulusAddCompositeOp: 01361 case ModulusSubtractCompositeOp: 01362 case MultiplyCompositeOp: 01363 case OverlayCompositeOp: 01364 case PegtopLightCompositeOp: 01365 case PinLightCompositeOp: 01366 case ScreenCompositeOp: 01367 case SoftLightCompositeOp: 01368 case VividLightCompositeOp: 01369 { 01370 alpha=RoundToUnity(Sa+Da-Sa*Da); 01371 break; 01372 } 01373 case DarkenCompositeOp: 01374 case DstAtopCompositeOp: 01375 case DstInCompositeOp: 01376 case InCompositeOp: 01377 case LightenCompositeOp: 01378 case SrcInCompositeOp: 01379 { 01380 alpha=Sa*Da; 01381 break; 01382 } 01383 case DissolveCompositeOp: 01384 { 01385 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve* 01386 Sa+destination_dissolve*Da; 01387 break; 01388 } 01389 case DstOverCompositeOp: 01390 { 01391 alpha=Da*(-Sa)+Da+Sa; 01392 break; 01393 } 01394 case DstOutCompositeOp: 01395 { 01396 alpha=Da*(1.0-Sa); 01397 break; 01398 } 01399 case OutCompositeOp: 01400 case SrcOutCompositeOp: 01401 { 01402 alpha=Sa*(1.0-Da); 01403 break; 01404 } 01405 case OverCompositeOp: 01406 case SrcOverCompositeOp: 01407 { 01408 alpha=Sa*(-Da)+Sa+Da; 01409 break; 01410 } 01411 case BlendCompositeOp: 01412 case PlusCompositeOp: 01413 { 01414 alpha=RoundToUnity(Sa+Da); 01415 break; 01416 } 01417 case XorCompositeOp: 01418 { 01419 alpha=Sa+Da-2.0*Sa*Da; 01420 break; 01421 } 01422 default: 01423 { 01424 alpha=1.0; 01425 break; 01426 } 01427 } 01428 if (GetPixelMask(image,p) != 0) 01429 { 01430 p+=GetPixelChannels(composite_image); 01431 q+=GetPixelChannels(image); 01432 continue; 01433 } 01434 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 01435 { 01436 double 01437 sans; 01438 01439 MagickRealType 01440 pixel; 01441 01442 PixelChannel 01443 channel; 01444 01445 PixelTrait 01446 composite_traits, 01447 traits; 01448 01449 channel=GetPixelChannelMapChannel(image,i); 01450 traits=GetPixelChannelMapTraits(image,channel); 01451 composite_traits=GetPixelChannelMapTraits(composite_image,channel); 01452 if (traits == UndefinedPixelTrait) 01453 continue; 01454 if ((compose != IntensityCompositeOp) && 01455 (composite_traits == UndefinedPixelTrait)) 01456 continue; 01457 /* 01458 Sc: source color. 01459 Dc: destination color. 01460 */ 01461 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p); 01462 Dc=(MagickRealType) q[i]; 01463 if ((traits & CopyPixelTrait) != 0) 01464 { 01465 if (channel != AlphaPixelChannel) 01466 { 01467 /* 01468 Copy channel. 01469 */ 01470 q[i]=ClampToQuantum(Sc); 01471 continue; 01472 } 01473 /* 01474 Set alpha channel. 01475 */ 01476 switch (compose) 01477 { 01478 case AlphaCompositeOp: 01479 { 01480 pixel=QuantumRange*Sa; 01481 break; 01482 } 01483 case AtopCompositeOp: 01484 case CopyBlackCompositeOp: 01485 case CopyBlueCompositeOp: 01486 case CopyCyanCompositeOp: 01487 case CopyGreenCompositeOp: 01488 case CopyMagentaCompositeOp: 01489 case CopyRedCompositeOp: 01490 case CopyYellowCompositeOp: 01491 case SrcAtopCompositeOp: 01492 case DstCompositeOp: 01493 case NoCompositeOp: 01494 { 01495 pixel=QuantumRange*Da; 01496 break; 01497 } 01498 case ChangeMaskCompositeOp: 01499 { 01500 MagickBooleanType 01501 equivalent; 01502 01503 if (Da > ((MagickRealType) QuantumRange/2.0)) 01504 { 01505 pixel=(MagickRealType) TransparentAlpha; 01506 break; 01507 } 01508 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q); 01509 if (equivalent != MagickFalse) 01510 { 01511 pixel=(MagickRealType) TransparentAlpha; 01512 break; 01513 } 01514 pixel=(MagickRealType) OpaqueAlpha; 01515 break; 01516 } 01517 case ClearCompositeOp: 01518 { 01519 pixel=(MagickRealType) TransparentAlpha; 01520 break; 01521 } 01522 case ColorizeCompositeOp: 01523 case HueCompositeOp: 01524 case LuminizeCompositeOp: 01525 case SaturateCompositeOp: 01526 { 01527 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon) 01528 { 01529 pixel=QuantumRange*Da; 01530 break; 01531 } 01532 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon) 01533 { 01534 pixel=QuantumRange*Sa; 01535 break; 01536 } 01537 if (Sa < Da) 01538 { 01539 pixel=QuantumRange*Da; 01540 break; 01541 } 01542 pixel=QuantumRange*Sa; 01543 break; 01544 } 01545 case CopyCompositeOp: 01546 case CopyAlphaCompositeOp: 01547 case DisplaceCompositeOp: 01548 case DistortCompositeOp: 01549 case DstAtopCompositeOp: 01550 case ReplaceCompositeOp: 01551 case SrcCompositeOp: 01552 { 01553 pixel=QuantumRange*Sa; 01554 break; 01555 } 01556 case DarkenIntensityCompositeOp: 01557 { 01558 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) < 01559 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da; 01560 break; 01561 } 01562 case IntensityCompositeOp: 01563 { 01564 pixel=(MagickRealType) GetPixelIntensity(composite_image,p); 01565 break; 01566 } 01567 case LightenIntensityCompositeOp: 01568 { 01569 pixel=Sa*GetPixelIntensity(composite_image,p) > 01570 Da*GetPixelIntensity(image,q) ? Sa : Da; 01571 break; 01572 } 01573 case ModulateCompositeOp: 01574 { 01575 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon) 01576 { 01577 pixel=QuantumRange*Da; 01578 break; 01579 } 01580 pixel=QuantumRange*Da; 01581 break; 01582 } 01583 default: 01584 { 01585 pixel=QuantumRange*alpha; 01586 break; 01587 } 01588 } 01589 q[i]=ClampToQuantum(pixel); 01590 continue; 01591 } 01592 /* 01593 Porter-Duff compositions: 01594 Sca: source normalized color multiplied by alpha. 01595 Dca: normalized destination color multiplied by alpha. 01596 */ 01597 Sca=QuantumScale*Sa*Sc; 01598 Dca=QuantumScale*Da*Dc; 01599 switch (compose) 01600 { 01601 case DarkenCompositeOp: 01602 case LightenCompositeOp: 01603 case ModulusSubtractCompositeOp: 01604 { 01605 gamma=1.0-alpha; 01606 break; 01607 } 01608 default: 01609 break; 01610 } 01611 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha); 01612 pixel=Dc; 01613 switch (compose) 01614 { 01615 case AlphaCompositeOp: 01616 { 01617 pixel=QuantumRange*Sa; 01618 break; 01619 } 01620 case AtopCompositeOp: 01621 case SrcAtopCompositeOp: 01622 { 01623 pixel=Sc*Sa+Dc*(1.0-Sa); 01624 break; 01625 } 01626 case BlendCompositeOp: 01627 { 01628 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc); 01629 break; 01630 } 01631 case BlurCompositeOp: 01632 case DisplaceCompositeOp: 01633 case DistortCompositeOp: 01634 case CopyCompositeOp: 01635 case ReplaceCompositeOp: 01636 case SrcCompositeOp: 01637 { 01638 pixel=Sc; 01639 break; 01640 } 01641 case BumpmapCompositeOp: 01642 { 01643 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon) 01644 { 01645 pixel=Dc; 01646 break; 01647 } 01648 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc; 01649 break; 01650 } 01651 case ChangeMaskCompositeOp: 01652 { 01653 pixel=Dc; 01654 break; 01655 } 01656 case ClearCompositeOp: 01657 { 01658 pixel=0.0; 01659 break; 01660 } 01661 case ColorBurnCompositeOp: 01662 { 01663 /* 01664 Refer to the March 2009 SVG specification. 01665 */ 01666 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon)) 01667 { 01668 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa)); 01669 break; 01670 } 01671 if (Sca < MagickEpsilon) 01672 { 01673 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)); 01674 break; 01675 } 01676 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+ 01677 Sca*(1.0-Da)+Dca*(1.0-Sa)); 01678 break; 01679 } 01680 case ColorDodgeCompositeOp: 01681 { 01682 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon)) 01683 { 01684 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa)); 01685 break; 01686 } 01687 if (fabs(Sca-Sa) < MagickEpsilon) 01688 { 01689 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); 01690 break; 01691 } 01692 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca* 01693 (1.0-Sa)); 01694 break; 01695 } 01696 case ColorizeCompositeOp: 01697 { 01698 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon) 01699 { 01700 pixel=Dc; 01701 break; 01702 } 01703 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon) 01704 { 01705 pixel=Sc; 01706 break; 01707 } 01708 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q), 01709 GetPixelBlue(image,q),&sans,&sans,&brightness); 01710 CompositeHSB(GetPixelRed(composite_image,p), 01711 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p), 01712 &hue,&saturation,&sans); 01713 HSBComposite(hue,saturation,brightness,&red,&green,&blue); 01714 switch (channel) 01715 { 01716 case RedPixelChannel: pixel=red; break; 01717 case GreenPixelChannel: pixel=green; break; 01718 case BluePixelChannel: pixel=blue; break; 01719 default: pixel=Dc; break; 01720 } 01721 break; 01722 } 01723 case CopyAlphaCompositeOp: 01724 case IntensityCompositeOp: 01725 { 01726 if (channel == AlphaPixelChannel) 01727 pixel=(MagickRealType) GetPixelAlpha(composite_image,p); 01728 break; 01729 } 01730 case CopyBlackCompositeOp: 01731 { 01732 if (channel == BlackPixelChannel) 01733 pixel=(MagickRealType) GetPixelBlack(composite_image,p); 01734 break; 01735 } 01736 case CopyBlueCompositeOp: 01737 case CopyYellowCompositeOp: 01738 { 01739 if (channel == BluePixelChannel) 01740 pixel=(MagickRealType) GetPixelBlue(composite_image,p); 01741 break; 01742 } 01743 case CopyGreenCompositeOp: 01744 case CopyMagentaCompositeOp: 01745 { 01746 if (channel == GreenPixelChannel) 01747 pixel=(MagickRealType) GetPixelGreen(composite_image,p); 01748 break; 01749 } 01750 case CopyRedCompositeOp: 01751 case CopyCyanCompositeOp: 01752 { 01753 if (channel == RedPixelChannel) 01754 pixel=(MagickRealType) GetPixelRed(composite_image,p); 01755 break; 01756 } 01757 case DarkenCompositeOp: 01758 { 01759 /* 01760 Darken is equivalent to a 'Minimum' method 01761 OR a greyscale version of a binary 'Or' 01762 OR the 'Intersection' of pixel sets. 01763 */ 01764 if (Sc < Dc) 01765 { 01766 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc); 01767 break; 01768 } 01769 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc); 01770 break; 01771 } 01772 case DarkenIntensityCompositeOp: 01773 { 01774 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) < 01775 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc; 01776 break; 01777 } 01778 case DifferenceCompositeOp: 01779 { 01780 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc)); 01781 break; 01782 } 01783 case DissolveCompositeOp: 01784 { 01785 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa* 01786 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc); 01787 break; 01788 } 01789 case DivideDstCompositeOp: 01790 { 01791 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon)) 01792 { 01793 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa)); 01794 break; 01795 } 01796 if (fabs(Dca) < MagickEpsilon) 01797 { 01798 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); 01799 break; 01800 } 01801 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); 01802 break; 01803 } 01804 case DivideSrcCompositeOp: 01805 { 01806 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon)) 01807 { 01808 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da)); 01809 break; 01810 } 01811 if (fabs(Sca) < MagickEpsilon) 01812 { 01813 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da)); 01814 break; 01815 } 01816 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da)); 01817 break; 01818 } 01819 case DstAtopCompositeOp: 01820 { 01821 pixel=Dc*Da+Sc*(1.0-Da); 01822 break; 01823 } 01824 case DstCompositeOp: 01825 case NoCompositeOp: 01826 { 01827 pixel=Dc; 01828 break; 01829 } 01830 case DstInCompositeOp: 01831 { 01832 pixel=gamma*(Sa*Dc*Sa); 01833 break; 01834 } 01835 case DstOutCompositeOp: 01836 { 01837 pixel=gamma*(Da*Dc*(1.0-Sa)); 01838 break; 01839 } 01840 case DstOverCompositeOp: 01841 { 01842 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc); 01843 break; 01844 } 01845 case ExclusionCompositeOp: 01846 { 01847 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+ 01848 Dca*(1.0-Sa)); 01849 break; 01850 } 01851 case HardLightCompositeOp: 01852 { 01853 if ((2.0*Sca) < Sa) 01854 { 01855 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca* 01856 (1.0-Sa)); 01857 break; 01858 } 01859 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+ 01860 Dca*(1.0-Sa)); 01861 break; 01862 } 01863 case HueCompositeOp: 01864 { 01865 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon) 01866 { 01867 pixel=Dc; 01868 break; 01869 } 01870 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon) 01871 { 01872 pixel=Sc; 01873 break; 01874 } 01875 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q), 01876 GetPixelBlue(image,q),&hue,&saturation,&brightness); 01877 CompositeHSB(GetPixelRed(composite_image,p), 01878 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p), 01879 &hue,&sans,&sans); 01880 HSBComposite(hue,saturation,brightness,&red,&green,&blue); 01881 switch (channel) 01882 { 01883 case RedPixelChannel: pixel=red; break; 01884 case GreenPixelChannel: pixel=green; break; 01885 case BluePixelChannel: pixel=blue; break; 01886 default: pixel=Dc; break; 01887 } 01888 break; 01889 } 01890 case InCompositeOp: 01891 case SrcInCompositeOp: 01892 { 01893 pixel=gamma*(Da*Sc*Da); 01894 break; 01895 } 01896 case LinearBurnCompositeOp: 01897 { 01898 /* 01899 LinearBurn: as defined by Abode Photoshop, according to 01900 http://www.simplefilter.de/en/basics/mixmods.html is: 01901 01902 f(Sc,Dc) = Sc + Dc - 1 01903 */ 01904 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da); 01905 break; 01906 } 01907 case LinearDodgeCompositeOp: 01908 { 01909 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc); 01910 break; 01911 } 01912 case LinearLightCompositeOp: 01913 { 01914 /* 01915 LinearLight: as defined by Abode Photoshop, according to 01916 http://www.simplefilter.de/en/basics/mixmods.html is: 01917 01918 f(Sc,Dc) = Dc + 2*Sc - 1 01919 */ 01920 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca); 01921 break; 01922 } 01923 case LightenCompositeOp: 01924 { 01925 if (Sc > Dc) 01926 { 01927 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc); 01928 break; 01929 } 01930 pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc); 01931 break; 01932 } 01933 case LightenIntensityCompositeOp: 01934 { 01935 /* 01936 Lighten is equivalent to a 'Maximum' method 01937 OR a greyscale version of a binary 'And' 01938 OR the 'Union' of pixel sets. 01939 */ 01940 pixel=Sa*GetPixelIntensity(composite_image,p) > 01941 Da*GetPixelIntensity(image,q) ? Sc : Dc; 01942 break; 01943 } 01944 case LuminizeCompositeOp: 01945 { 01946 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon) 01947 { 01948 pixel=Dc; 01949 break; 01950 } 01951 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon) 01952 { 01953 pixel=Sc; 01954 break; 01955 } 01956 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q), 01957 GetPixelBlue(image,q),&hue,&saturation,&brightness); 01958 CompositeHSB(GetPixelRed(composite_image,p), 01959 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p), 01960 &sans,&sans,&brightness); 01961 HSBComposite(hue,saturation,brightness,&red,&green,&blue); 01962 switch (channel) 01963 { 01964 case RedPixelChannel: pixel=red; break; 01965 case GreenPixelChannel: pixel=green; break; 01966 case BluePixelChannel: pixel=blue; break; 01967 default: pixel=Dc; break; 01968 } 01969 break; 01970 } 01971 case MathematicsCompositeOp: 01972 { 01973 /* 01974 'Mathematics' a free form user control mathematical composition 01975 is defined as... 01976 01977 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D 01978 01979 Where the arguments A,B,C,D are (currently) passed to composite 01980 as a command separated 'geometry' string in "compose:args" image 01981 artifact. 01982 01983 A = a->rho, B = a->sigma, C = a->xi, D = a->psi 01984 01985 Applying the SVG transparency formula (see above), we get... 01986 01987 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa) 01988 01989 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) + 01990 Dca*(1.0-Sa) 01991 */ 01992 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma* 01993 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+ 01994 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa); 01995 break; 01996 } 01997 case MinusDstCompositeOp: 01998 { 01999 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa); 02000 break; 02001 } 02002 case MinusSrcCompositeOp: 02003 { 02004 /* 02005 Minus source from destination. 02006 02007 f(Sc,Dc) = Sc - Dc 02008 */ 02009 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da); 02010 break; 02011 } 02012 case ModulateCompositeOp: 02013 { 02014 ssize_t 02015 offset; 02016 02017 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon) 02018 { 02019 pixel=Dc; 02020 break; 02021 } 02022 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint); 02023 if (offset == 0) 02024 { 02025 pixel=Dc; 02026 break; 02027 } 02028 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q), 02029 GetPixelBlue(image,q),&hue,&saturation,&brightness); 02030 brightness+=(0.01*percent_brightness*offset)/midpoint; 02031 saturation*=0.01*percent_saturation; 02032 HSBComposite(hue,saturation,brightness,&red,&green,&blue); 02033 switch (channel) 02034 { 02035 case RedPixelChannel: pixel=red; break; 02036 case GreenPixelChannel: pixel=green; break; 02037 case BluePixelChannel: pixel=blue; break; 02038 default: pixel=Dc; break; 02039 } 02040 break; 02041 } 02042 case ModulusAddCompositeOp: 02043 { 02044 pixel=Sc+Dc; 02045 if (pixel > QuantumRange) 02046 pixel-=(QuantumRange+1.0); 02047 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa)); 02048 break; 02049 } 02050 case ModulusSubtractCompositeOp: 02051 { 02052 pixel=Sc-Dc; 02053 if (pixel < 0.0) 02054 pixel+=(QuantumRange+1.0); 02055 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa)); 02056 break; 02057 } 02058 case MultiplyCompositeOp: 02059 { 02060 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); 02061 break; 02062 } 02063 case OutCompositeOp: 02064 case SrcOutCompositeOp: 02065 { 02066 pixel=gamma*(Sa*Sc*(1.0-Da)); 02067 break; 02068 } 02069 case OverCompositeOp: 02070 case SrcOverCompositeOp: 02071 { 02072 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc); 02073 break; 02074 } 02075 case OverlayCompositeOp: 02076 { 02077 if ((2.0*Dca) < Da) 02078 { 02079 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca* 02080 (1.0-Da)); 02081 break; 02082 } 02083 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+ 02084 Sca*(1.0-Da)); 02085 break; 02086 } 02087 case PegtopLightCompositeOp: 02088 { 02089 /* 02090 PegTop: A Soft-Light alternative: A continuous version of the 02091 Softlight function, producing very similar results. 02092 02093 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc 02094 02095 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm. 02096 */ 02097 if (fabs(Da) < MagickEpsilon) 02098 { 02099 pixel=QuantumRange*gamma*(Sca); 02100 break; 02101 } 02102 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0- 02103 Da)+Dca*(1.0-Sa)); 02104 break; 02105 } 02106 case PinLightCompositeOp: 02107 { 02108 /* 02109 PinLight: A Photoshop 7 composition method 02110 http://www.simplefilter.de/en/basics/mixmods.html 02111 02112 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc 02113 */ 02114 if ((Dca*Sa) < (Da*(2.0*Sca-Sa))) 02115 { 02116 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa)); 02117 break; 02118 } 02119 if ((Dca*Sa) > (2.0*Sca*Da)) 02120 { 02121 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa)); 02122 break; 02123 } 02124 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca); 02125 break; 02126 } 02127 case PlusCompositeOp: 02128 { 02129 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc); 02130 break; 02131 } 02132 case SaturateCompositeOp: 02133 { 02134 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon) 02135 { 02136 pixel=Dc; 02137 break; 02138 } 02139 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon) 02140 { 02141 pixel=Sc; 02142 break; 02143 } 02144 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q), 02145 GetPixelBlue(image,q),&hue,&saturation,&brightness); 02146 CompositeHSB(GetPixelRed(composite_image,p), 02147 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p), 02148 &sans,&saturation,&sans); 02149 HSBComposite(hue,saturation,brightness,&red,&green,&blue); 02150 switch (channel) 02151 { 02152 case RedPixelChannel: pixel=red; break; 02153 case GreenPixelChannel: pixel=green; break; 02154 case BluePixelChannel: pixel=blue; break; 02155 default: pixel=Dc; break; 02156 } 02157 break; 02158 } 02159 case ScreenCompositeOp: 02160 { 02161 /* 02162 Screen: a negated multiply: 02163 02164 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc) 02165 */ 02166 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca); 02167 break; 02168 } 02169 case SoftLightCompositeOp: 02170 { 02171 /* 02172 Refer to the March 2009 SVG specification. 02173 */ 02174 if ((2.0*Sca) < Sa) 02175 { 02176 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+ 02177 Sca*(1.0-Da)+Dca*(1.0-Sa)); 02178 break; 02179 } 02180 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da)) 02181 { 02182 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)* 02183 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+ 02184 Dca*(1.0-Sa)); 02185 break; 02186 } 02187 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)- 02188 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa)); 02189 break; 02190 } 02191 case ThresholdCompositeOp: 02192 { 02193 MagickRealType 02194 delta; 02195 02196 delta=Sc-Dc; 02197 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold) 02198 { 02199 pixel=gamma*Dc; 02200 break; 02201 } 02202 pixel=gamma*(Dc+delta*amount); 02203 break; 02204 } 02205 case VividLightCompositeOp: 02206 { 02207 /* 02208 VividLight: A Photoshop 7 composition method. See 02209 http://www.simplefilter.de/en/basics/mixmods.html. 02210 02211 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc)) 02212 */ 02213 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon)) 02214 { 02215 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); 02216 break; 02217 } 02218 if ((2.0*Sca) <= Sa) 02219 { 02220 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca* 02221 (1.0-Da)+Dca*(1.0-Sa)); 02222 break; 02223 } 02224 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+ 02225 Dca*(1.0-Sa)); 02226 break; 02227 } 02228 case XorCompositeOp: 02229 { 02230 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa)); 02231 break; 02232 } 02233 default: 02234 { 02235 pixel=Sc; 02236 break; 02237 } 02238 } 02239 q[i]=ClampToQuantum(pixel); 02240 } 02241 p+=GetPixelChannels(composite_image); 02242 channels=GetPixelChannels(composite_image); 02243 if (p >= (pixels+channels*composite_image->columns)) 02244 p=pixels; 02245 q+=GetPixelChannels(image); 02246 } 02247 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 02248 status=MagickFalse; 02249 if (image->progress_monitor != (MagickProgressMonitor) NULL) 02250 { 02251 MagickBooleanType 02252 proceed; 02253 02254 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02255 #pragma omp critical (MagickCore_CompositeImage) 02256 #endif 02257 proceed=SetImageProgress(image,CompositeImageTag,progress++, 02258 image->rows); 02259 if (proceed == MagickFalse) 02260 status=MagickFalse; 02261 } 02262 } 02263 composite_view=DestroyCacheView(composite_view); 02264 image_view=DestroyCacheView(image_view); 02265 if (destination_image != (Image * ) NULL) 02266 destination_image=DestroyImage(destination_image); 02267 return(status); 02268 } 02269 02270 /* 02271 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02272 % % 02273 % % 02274 % % 02275 % T e x t u r e I m a g e % 02276 % % 02277 % % 02278 % % 02279 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02280 % 02281 % TextureImage() repeatedly tiles the texture image across and down the image 02282 % canvas. 02283 % 02284 % The format of the TextureImage method is: 02285 % 02286 % MagickBooleanType TextureImage(Image *image,const Image *texture_image, 02287 % ExceptionInfo *exception) 02288 % 02289 % A description of each parameter follows: 02290 % 02291 % o image: the image. 02292 % 02293 % o texture_image: This image is the texture to layer on the background. 02294 % 02295 */ 02296 MagickExport MagickBooleanType TextureImage(Image *image, 02297 const Image *texture_image,ExceptionInfo *exception) 02298 { 02299 #define TextureImageTag "Texture/Image" 02300 02301 CacheView 02302 *image_view, 02303 *texture_view; 02304 02305 MagickBooleanType 02306 status; 02307 02308 ssize_t 02309 y; 02310 02311 assert(image != (Image *) NULL); 02312 if (image->debug != MagickFalse) 02313 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 02314 assert(image->signature == MagickSignature); 02315 if (texture_image == (const Image *) NULL) 02316 return(MagickFalse); 02317 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod); 02318 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 02319 return(MagickFalse); 02320 status=MagickTrue; 02321 if ((image->compose != CopyCompositeOp) && 02322 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) || 02323 (texture_image->matte != MagickFalse))) 02324 { 02325 /* 02326 Tile texture onto the image background. 02327 */ 02328 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02329 #pragma omp parallel for schedule(static) shared(status) 02330 #endif 02331 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows) 02332 { 02333 register ssize_t 02334 x; 02335 02336 if (status == MagickFalse) 02337 continue; 02338 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns) 02339 { 02340 MagickBooleanType 02341 thread_status; 02342 02343 thread_status=CompositeImage(image,image->compose,texture_image,x+ 02344 texture_image->tile_offset.x,y+texture_image->tile_offset.y, 02345 exception); 02346 if (thread_status == MagickFalse) 02347 { 02348 status=thread_status; 02349 break; 02350 } 02351 } 02352 if (image->progress_monitor != (MagickProgressMonitor) NULL) 02353 { 02354 MagickBooleanType 02355 proceed; 02356 02357 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02358 #pragma omp critical (MagickCore_TextureImage) 02359 #endif 02360 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) 02361 y,image->rows); 02362 if (proceed == MagickFalse) 02363 status=MagickFalse; 02364 } 02365 } 02366 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType) 02367 image->rows,image->rows); 02368 return(status); 02369 } 02370 /* 02371 Tile texture onto the image background (optimized). 02372 */ 02373 status=MagickTrue; 02374 image_view=AcquireCacheView(image); 02375 texture_view=AcquireCacheView(texture_image); 02376 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02377 #pragma omp parallel for schedule(static) shared(status) 02378 #endif 02379 for (y=0; y < (ssize_t) image->rows; y++) 02380 { 02381 MagickBooleanType 02382 sync; 02383 02384 register const Quantum 02385 *p, 02386 *pixels; 02387 02388 register ssize_t 02389 x; 02390 02391 register Quantum 02392 *q; 02393 02394 size_t 02395 width; 02396 02397 if (status == MagickFalse) 02398 continue; 02399 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x, 02400 (y+texture_image->tile_offset.y) % texture_image->rows, 02401 texture_image->columns,1,exception); 02402 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 02403 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 02404 { 02405 status=MagickFalse; 02406 continue; 02407 } 02408 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns) 02409 { 02410 register ssize_t 02411 j; 02412 02413 p=pixels; 02414 width=texture_image->columns; 02415 if ((x+(ssize_t) width) > (ssize_t) image->columns) 02416 width=image->columns-x; 02417 for (j=0; j < (ssize_t) width; j++) 02418 { 02419 register ssize_t 02420 i; 02421 02422 if (GetPixelMask(image,p) != 0) 02423 { 02424 p+=GetPixelChannels(texture_image); 02425 q+=GetPixelChannels(image); 02426 continue; 02427 } 02428 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++) 02429 { 02430 PixelChannel 02431 channel; 02432 02433 PixelTrait 02434 texture_traits, 02435 traits; 02436 02437 channel=GetPixelChannelMapChannel(texture_image,i); 02438 texture_traits=GetPixelChannelMapTraits(texture_image,channel); 02439 traits=GetPixelChannelMapTraits(image,channel); 02440 if ((traits == UndefinedPixelTrait) || 02441 (texture_traits == UndefinedPixelTrait)) 02442 continue; 02443 SetPixelChannel(image,channel,p[i],q); 02444 } 02445 p+=GetPixelChannels(texture_image); 02446 q+=GetPixelChannels(image); 02447 } 02448 } 02449 sync=SyncCacheViewAuthenticPixels(image_view,exception); 02450 if (sync == MagickFalse) 02451 status=MagickFalse; 02452 if (image->progress_monitor != (MagickProgressMonitor) NULL) 02453 { 02454 MagickBooleanType 02455 proceed; 02456 02457 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02458 #pragma omp critical (MagickCore_TextureImage) 02459 #endif 02460 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y, 02461 image->rows); 02462 if (proceed == MagickFalse) 02463 status=MagickFalse; 02464 } 02465 } 02466 texture_view=DestroyCacheView(texture_view); 02467 image_view=DestroyCacheView(image_view); 02468 return(status); 02469 }