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