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