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