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