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