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