composite.c

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