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

Generated on 19 Nov 2009 for MagickCore by  doxygen 1.6.1