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