]> 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       MagickRealType
934         horizontal_scale,
935         vertical_scale;
936
937       PixelInfo
938         pixel;
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)/2.0;
966               vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
967             }
968           else
969             {
970               horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
971               vertical_scale=(MagickRealType) (image->rows-1)/2.0;
972             }
973         }
974       else
975         {
976           horizontal_scale=geometry_info.rho;
977           vertical_scale=geometry_info.sigma;
978           if ((flags & PercentValue) != 0)
979             {
980               if ((flags & AspectValue) == 0)
981                 {
982                   horizontal_scale*=(source_image->columns-1)/200.0;
983                   vertical_scale*=(source_image->rows-1)/200.0;
984                 }
985               else
986                 {
987                   horizontal_scale*=(image->columns-1)/200.0;
988                   vertical_scale*=(image->rows-1)/200.0;
989                 }
990             }
991           if ((flags & HeightValue) == 0)
992             vertical_scale=horizontal_scale;
993         }
994       /*
995         Determine fixed center point for absolute distortion map
996          Absolute distort ==
997            Displace offset relative to a fixed absolute point
998            Select that point according to +X+Y user inputs.
999            default = center of overlay image
1000            arg flag '!' = locations/percentage relative to background image
1001       */
1002       center.x=(MagickRealType) x_offset;
1003       center.y=(MagickRealType) y_offset;
1004       if (compose == DistortCompositeOp)
1005         {
1006           if ((flags & XValue) == 0)
1007             if ((flags & AspectValue) == 0)
1008               center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1009                 2.0);
1010             else
1011               center.x=(MagickRealType) ((image->columns-1)/2.0);
1012           else
1013             if ((flags & AspectValue) == 0)
1014               center.x=(MagickRealType) x_offset+geometry_info.xi;
1015             else
1016               center.x=geometry_info.xi;
1017           if ((flags & YValue) == 0)
1018             if ((flags & AspectValue) == 0)
1019               center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1020             else
1021               center.y=(MagickRealType) ((image->rows-1)/2.0);
1022           else
1023             if ((flags & AspectValue) == 0)
1024               center.y=(MagickRealType) y_offset+geometry_info.psi;
1025             else
1026               center.y=geometry_info.psi;
1027         }
1028       /*
1029         Shift the pixel offset point as defined by the provided,
1030         displacement/distortion map.  -- Like a lens...
1031       */
1032       GetPixelInfo(image,&pixel);
1033       image_view=AcquireVirtualCacheView(image,exception);
1034       source_view=AcquireVirtualCacheView(source_image,exception);
1035       canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1036       for (y=0; y < (ssize_t) source_image->rows; y++)
1037       {
1038         MagickBooleanType
1039           sync;
1040
1041         register const Quantum
1042           *restrict p;
1043
1044         register Quantum
1045           *restrict q;
1046
1047         register ssize_t
1048           x;
1049
1050         if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1051           continue;
1052         p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1053           exception);
1054         q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1055           exception);
1056         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1057           break;
1058         for (x=0; x < (ssize_t) source_image->columns; x++)
1059         {
1060           if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1061             {
1062               p+=GetPixelChannels(source_image);
1063               continue;
1064             }
1065           /*
1066             Displace the offset.
1067           */
1068           offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1069             (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1070             QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1071             x : 0);
1072           offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1073             (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1074             QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1075             y : 0);
1076           (void) InterpolatePixelInfo(image,image_view,
1077             UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1078             &pixel,exception);
1079           /*
1080             Mask with the 'invalid pixel mask' in alpha channel.
1081           */
1082           pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
1083             (QuantumScale*GetPixelAlpha(source_image,p));
1084           SetPixelViaPixelInfo(canvas_image,&pixel,q);
1085           p+=GetPixelChannels(source_image);
1086           q+=GetPixelChannels(canvas_image);
1087         }
1088         sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1089         if (sync == MagickFalse)
1090           break;
1091       }
1092       canvas_view=DestroyCacheView(canvas_view);
1093       source_view=DestroyCacheView(source_view);
1094       image_view=DestroyCacheView(image_view);
1095       source_image=DestroyImage(source_image);
1096       source_image=canvas_image;
1097       break;
1098     }
1099     case DissolveCompositeOp:
1100     {
1101       const char
1102         *value;
1103
1104       /*
1105         Geometry arguments to dissolve factors.
1106       */
1107       value=GetImageArtifact(image,"compose:args");
1108       if (value != (char *) NULL)
1109         {
1110           flags=ParseGeometry(value,&geometry_info);
1111           source_dissolve=geometry_info.rho/100.0;
1112           canvas_dissolve=1.0;
1113           if ((source_dissolve-MagickEpsilon) < 0.0)
1114             source_dissolve=0.0;
1115           if ((source_dissolve+MagickEpsilon) > 1.0)
1116             {
1117               canvas_dissolve=2.0-source_dissolve;
1118               source_dissolve=1.0;
1119             }
1120           if ((flags & SigmaValue) != 0)
1121             canvas_dissolve=geometry_info.sigma/100.0;
1122           if ((canvas_dissolve-MagickEpsilon) < 0.0)
1123             canvas_dissolve=0.0;
1124         }
1125       break;
1126     }
1127     case BlendCompositeOp:
1128     {
1129       const char
1130         *value;
1131
1132       value=GetImageArtifact(image,"compose:args");
1133       if (value != (char *) NULL)
1134         {
1135           flags=ParseGeometry(value,&geometry_info);
1136           source_dissolve=geometry_info.rho/100.0;
1137           canvas_dissolve=1.0-source_dissolve;
1138           if ((flags & SigmaValue) != 0)
1139             canvas_dissolve=geometry_info.sigma/100.0;
1140         }
1141       break;
1142     }
1143     case MathematicsCompositeOp:
1144     {
1145       const char
1146         *value;
1147
1148       /*
1149         Just collect the values from "compose:args", setting.
1150         Unused values are set to zero automagically.
1151
1152         Arguments are normally a comma separated list, so this probably should
1153         be changed to some 'general comma list' parser, (with a minimum
1154         number of values)
1155       */
1156       SetGeometryInfo(&geometry_info);
1157       value=GetImageArtifact(image,"compose:args");
1158       if (value != (char *) NULL)
1159         (void) ParseGeometry(value,&geometry_info);
1160       break;
1161     }
1162     case ModulateCompositeOp:
1163     {
1164       const char
1165         *value;
1166
1167       /*
1168         Determine the luma and chroma scale.
1169       */
1170       value=GetImageArtifact(image,"compose:args");
1171       if (value != (char *) NULL)
1172         {
1173           flags=ParseGeometry(value,&geometry_info);
1174           percent_luma=geometry_info.rho;
1175           if ((flags & SigmaValue) != 0)
1176             percent_chroma=geometry_info.sigma;
1177         }
1178       break;
1179     }
1180     case ThresholdCompositeOp:
1181     {
1182       const char
1183         *value;
1184
1185       /*
1186         Determine the amount and threshold.
1187       */
1188       value=GetImageArtifact(image,"compose:args");
1189       if (value != (char *) NULL)
1190         {
1191           flags=ParseGeometry(value,&geometry_info);
1192           amount=geometry_info.rho;
1193           threshold=geometry_info.sigma;
1194           if ((flags & SigmaValue) == 0)
1195             threshold=0.05f;
1196         }
1197       threshold*=QuantumRange;
1198       break;
1199     }
1200     default:
1201       break;
1202   }
1203   /*
1204     Composite image.
1205   */
1206   status=MagickTrue;
1207   progress=0;
1208   midpoint=((MagickRealType) QuantumRange+1.0)/2;
1209   source_view=AcquireVirtualCacheView(source_image,exception);
1210   image_view=AcquireAuthenticCacheView(image,exception);
1211 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1212   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1213     magick_threads(source_image,image,image->rows,1)
1214 #endif
1215   for (y=0; y < (ssize_t) image->rows; y++)
1216   {
1217     const Quantum
1218       *pixels;
1219
1220     MagickRealType
1221       blue,
1222       luma,
1223       green,
1224       hue,
1225       red,
1226       chroma;
1227
1228     PixelInfo
1229       canvas_pixel,
1230       source_pixel;
1231
1232     register const Quantum
1233       *restrict p;
1234
1235     register Quantum
1236       *restrict q;
1237
1238     register ssize_t
1239       x;
1240
1241     if (status == MagickFalse)
1242       continue;
1243     if (clip_to_self != MagickFalse)
1244       {
1245         if (y < y_offset)
1246           continue;
1247         if ((y-y_offset) >= (ssize_t) source_image->rows)
1248           continue;
1249       }
1250     /*
1251       If pixels is NULL, y is outside overlay region.
1252     */
1253     pixels=(Quantum *) NULL;
1254     p=(Quantum *) NULL;
1255     if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1256       {
1257         p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1258           source_image->columns,1,exception);
1259         if (p == (const Quantum *) NULL)
1260           {
1261             status=MagickFalse;
1262             continue;
1263           }
1264         pixels=p;
1265         if (x_offset < 0)
1266           p-=x_offset*GetPixelChannels(source_image);
1267       }
1268     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1269     if (q == (Quantum *) NULL)
1270       {
1271         status=MagickFalse;
1272         continue;
1273       }
1274     hue=0.0;
1275     chroma=0.0;
1276     luma=0.0;
1277     GetPixelInfo(image,&canvas_pixel);
1278     GetPixelInfo(source_image,&source_pixel);
1279     for (x=0; x < (ssize_t) image->columns; x++)
1280     {
1281       double
1282         gamma;
1283
1284       MagickRealType
1285         alpha,
1286         Da,
1287         Dc,
1288         Dca,
1289         Sa,
1290         Sc,
1291         Sca;
1292
1293       register ssize_t
1294         i;
1295
1296       size_t
1297         channels;
1298
1299       if (clip_to_self != MagickFalse)
1300         {
1301           if (x < x_offset)
1302             {
1303               q+=GetPixelChannels(image);
1304               continue;
1305             }
1306           if ((x-x_offset) >= (ssize_t) source_image->columns)
1307             break;
1308         }
1309       if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1310           ((x-x_offset) >= (ssize_t) source_image->columns))
1311         {
1312           Quantum
1313             source[MaxPixelChannels];
1314
1315           /*
1316             Virtual composite:
1317               Sc: source color.
1318               Dc: canvas color.
1319           */
1320           (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,
1321             source,exception);
1322           if (GetPixelReadMask(image,q) == 0)
1323             {
1324               q+=GetPixelChannels(image);
1325               continue;
1326             }
1327           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1328           {
1329             MagickRealType
1330               pixel;
1331
1332             PixelChannel channel=GetPixelChannelChannel(image,i);
1333             PixelTrait traits=GetPixelChannelTraits(image,channel);
1334             PixelTrait source_traits=GetPixelChannelTraits(source_image,
1335               channel);
1336             if ((traits == UndefinedPixelTrait) ||
1337                 (source_traits == UndefinedPixelTrait))
1338               continue;
1339             switch (compose)
1340             {
1341               case AlphaCompositeOp:
1342               case ChangeMaskCompositeOp:
1343               case CopyAlphaCompositeOp:
1344               case DstAtopCompositeOp:
1345               case DstInCompositeOp:
1346               case InCompositeOp:
1347               case OutCompositeOp:
1348               case SrcInCompositeOp:
1349               case SrcOutCompositeOp:
1350               {
1351                 if (channel == AlphaPixelChannel)
1352                   pixel=(MagickRealType) TransparentAlpha;
1353                 else
1354                   pixel=(MagickRealType) q[i];
1355                 break;
1356               }
1357               case ClearCompositeOp:
1358               case CopyCompositeOp:
1359               case ReplaceCompositeOp:
1360               case SrcCompositeOp:
1361               {
1362                 if (channel == AlphaPixelChannel)
1363                   pixel=(MagickRealType) TransparentAlpha;
1364                 else
1365                   pixel=0.0;
1366                 break;
1367               }
1368               case BlendCompositeOp:
1369               case DissolveCompositeOp:
1370               {
1371                 if (channel == AlphaPixelChannel)
1372                   pixel=canvas_dissolve*GetPixelAlpha(source_image,
1373                     source);
1374                 else
1375                   pixel=(MagickRealType) source[channel];
1376                 break;
1377               }
1378               default:
1379               {
1380                 pixel=(MagickRealType) source[channel];
1381                 break;
1382               }
1383             }
1384             q[i]=ClampToQuantum(pixel);
1385           }
1386           q+=GetPixelChannels(image);
1387           continue;
1388         }
1389       /*
1390         Authentic composite:
1391           Sa:  normalized source alpha.
1392           Da:  normalized canvas alpha.
1393       */
1394       Sa=QuantumScale*GetPixelAlpha(source_image,p);
1395       Da=QuantumScale*GetPixelAlpha(image,q);
1396       switch (compose)
1397       {
1398         case BumpmapCompositeOp:
1399         {
1400           alpha=GetPixelIntensity(source_image,p)*Sa;
1401           break;
1402         }
1403         case ColorBurnCompositeOp:
1404         case ColorDodgeCompositeOp:
1405         case DarkenCompositeOp:
1406         case DifferenceCompositeOp:
1407         case DivideDstCompositeOp:
1408         case DivideSrcCompositeOp:
1409         case ExclusionCompositeOp:
1410         case HardLightCompositeOp:
1411         case HardMixCompositeOp:
1412         case LinearBurnCompositeOp:
1413         case LinearDodgeCompositeOp:
1414         case LinearLightCompositeOp:
1415         case LightenCompositeOp:
1416         case MathematicsCompositeOp:
1417         case MinusDstCompositeOp:
1418         case MinusSrcCompositeOp:
1419         case ModulusAddCompositeOp:
1420         case ModulusSubtractCompositeOp:
1421         case MultiplyCompositeOp:
1422         case OverlayCompositeOp:
1423         case PegtopLightCompositeOp:
1424         case PinLightCompositeOp:
1425         case ScreenCompositeOp:
1426         case SoftLightCompositeOp:
1427         case VividLightCompositeOp:
1428         {
1429           alpha=RoundToUnity(Sa+Da-Sa*Da);
1430           break;
1431         }
1432         case DstAtopCompositeOp:
1433         case DstInCompositeOp:
1434         case InCompositeOp:
1435         case SrcInCompositeOp:
1436         {
1437           alpha=Sa*Da;
1438           break;
1439         }
1440         case DissolveCompositeOp:
1441         {
1442           alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*
1443             Sa+canvas_dissolve*Da;
1444           break;
1445         }
1446         case DstOverCompositeOp:
1447         {
1448           alpha=Sa+Da-Sa*Da;
1449           break;
1450         }
1451         case DstOutCompositeOp:
1452         {
1453           alpha=Da*(1.0-Sa);
1454           break;
1455         }
1456         case OutCompositeOp:
1457         case SrcOutCompositeOp:
1458         {
1459           alpha=Sa*(1.0-Da);
1460           break;
1461         }
1462         case OverCompositeOp:
1463         case SrcOverCompositeOp:
1464         {
1465           alpha=Sa+Da-Sa*Da;
1466           break;
1467         }
1468         case BlendCompositeOp:
1469         case PlusCompositeOp:
1470         {
1471           alpha=RoundToUnity(Sa+Da);
1472           break;
1473         }
1474         case XorCompositeOp:
1475         {
1476           alpha=Sa+Da-2.0*Sa*Da;
1477           break;
1478         }
1479         default:
1480         {
1481           alpha=1.0;
1482           break;
1483         }
1484       }
1485       if (GetPixelReadMask(image,q) == 0)
1486         {
1487           p+=GetPixelChannels(source_image);
1488           q+=GetPixelChannels(image);
1489           continue;
1490         }
1491       switch (compose)
1492       {
1493         case ColorizeCompositeOp:
1494         case HueCompositeOp:
1495         case LuminizeCompositeOp:
1496         case ModulateCompositeOp:
1497         case SaturateCompositeOp:
1498         {
1499           GetPixelInfoPixel(source_image,p,&source_pixel);
1500           GetPixelInfoPixel(image,q,&canvas_pixel);
1501           break;
1502         }
1503         default:
1504           break;
1505       }
1506       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1507       {
1508         MagickRealType
1509           pixel,
1510           sans;
1511
1512         PixelChannel channel=GetPixelChannelChannel(image,i);
1513         PixelTrait traits=GetPixelChannelTraits(image,channel);
1514         PixelTrait source_traits=GetPixelChannelTraits(source_image,
1515           channel);
1516         if (traits == UndefinedPixelTrait)
1517           continue;
1518         if ((source_traits == UndefinedPixelTrait) &&
1519              (((compose != CopyAlphaCompositeOp) &&
1520                (compose != ChangeMaskCompositeOp)) ||
1521               (channel != AlphaPixelChannel)))
1522             continue;
1523         /*
1524           Sc: source color.
1525           Dc: canvas color.
1526         */
1527         Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1528         Dc=(MagickRealType) q[i];
1529         if ((traits & CopyPixelTrait) != 0)
1530           {
1531             /*
1532               Copy channel.
1533             */
1534             q[i]=ClampToQuantum(Sc);
1535             continue;
1536           }
1537         if (channel == AlphaPixelChannel)
1538           {
1539             /*
1540               Set alpha channel.
1541             */
1542             switch (compose)
1543             {
1544               case AlphaCompositeOp:
1545               {
1546                 pixel=QuantumRange*Sa;
1547                 break;
1548               }
1549               case AtopCompositeOp:
1550               case CopyBlackCompositeOp:
1551               case CopyBlueCompositeOp:
1552               case CopyCyanCompositeOp:
1553               case CopyGreenCompositeOp:
1554               case CopyMagentaCompositeOp:
1555               case CopyRedCompositeOp:
1556               case CopyYellowCompositeOp:
1557               case SrcAtopCompositeOp:
1558               case DstCompositeOp:
1559               case NoCompositeOp:
1560               {
1561                 pixel=QuantumRange*Da;
1562                 break;
1563               }
1564               case ChangeMaskCompositeOp:
1565               {
1566                 MagickBooleanType
1567                   equivalent;
1568
1569                 if (Da > ((MagickRealType) QuantumRange/2.0))
1570                   {
1571                     pixel=(MagickRealType) TransparentAlpha;
1572                     break;
1573                   }
1574                 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1575                 if (equivalent != MagickFalse)
1576                   pixel=(MagickRealType) TransparentAlpha;
1577                 else
1578                   pixel=(MagickRealType) OpaqueAlpha;
1579                 break;
1580               }
1581               case ClearCompositeOp:
1582               {
1583                 pixel=(MagickRealType) TransparentAlpha;
1584                 break;
1585               }
1586               case ColorizeCompositeOp:
1587               case HueCompositeOp:
1588               case LuminizeCompositeOp:
1589               case SaturateCompositeOp:
1590               {
1591                 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1592                   {
1593                     pixel=QuantumRange*Da;
1594                     break;
1595                   }
1596                 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1597                   {
1598                     pixel=QuantumRange*Sa;
1599                     break;
1600                   }
1601                 if (Sa < Da)
1602                   {
1603                     pixel=QuantumRange*Da;
1604                     break;
1605                   }
1606                 pixel=QuantumRange*Sa;
1607                 break;
1608               }
1609               case CopyAlphaCompositeOp:
1610               {
1611                 if (source_traits == UndefinedPixelTrait)
1612                   pixel=GetPixelIntensity(source_image,p);
1613                 else
1614                   pixel=QuantumRange*Sa;
1615                 break;
1616               }
1617               case CopyCompositeOp:
1618               case DisplaceCompositeOp:
1619               case DistortCompositeOp:
1620               case DstAtopCompositeOp:
1621               case ReplaceCompositeOp:
1622               case SrcCompositeOp:
1623               {
1624                 pixel=QuantumRange*Sa;
1625                 break;
1626               }
1627               case DarkenIntensityCompositeOp:
1628               {
1629                 pixel=Sa*GetPixelIntensity(source_image,p) <
1630                   Da*GetPixelIntensity(image,q) ? Sa : Da;
1631                 break;
1632               }
1633               case LightenIntensityCompositeOp:
1634               {
1635                 pixel=Sa*GetPixelIntensity(source_image,p) >
1636                   Da*GetPixelIntensity(image,q) ? Sa : Da;
1637                 break;
1638               }
1639               case ModulateCompositeOp:
1640               {
1641                 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1642                   {
1643                     pixel=QuantumRange*Da;
1644                     break;
1645                   }
1646                 pixel=QuantumRange*Da;
1647                 break;
1648               }
1649               default:
1650               {
1651                 pixel=QuantumRange*alpha;
1652                 break;
1653               }
1654             }
1655             q[i]=ClampToQuantum(pixel);
1656             continue;
1657           }
1658         /*
1659           Porter-Duff compositions:
1660             Sca: source normalized color multiplied by alpha.
1661             Dca: normalized canvas color multiplied by alpha.
1662         */
1663         Sca=QuantumScale*Sa*Sc;
1664         Dca=QuantumScale*Da*Dc;
1665         switch (compose)
1666         {
1667           case DarkenCompositeOp:
1668           case LightenCompositeOp:
1669           case ModulusSubtractCompositeOp:
1670           {
1671             gamma=1.0-alpha;
1672             break;
1673           }
1674           default:
1675             break;
1676         }
1677         gamma=PerceptibleReciprocal(alpha);
1678         pixel=Dc;
1679         switch (compose)
1680         {
1681           case AlphaCompositeOp:
1682           {
1683             pixel=QuantumRange*Sa;
1684             break;
1685           }
1686           case AtopCompositeOp:
1687           case SrcAtopCompositeOp:
1688           {
1689             pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1690             break;
1691           }
1692           case BlendCompositeOp:
1693           {
1694             pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1695             break;
1696           }
1697           case BlurCompositeOp:
1698           case DisplaceCompositeOp:
1699           case DistortCompositeOp:
1700           case CopyCompositeOp:
1701           case ReplaceCompositeOp:
1702           case SrcCompositeOp:
1703           {
1704             pixel=QuantumRange*Sca;
1705             break;
1706           }
1707           case BumpmapCompositeOp:
1708           {
1709             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1710               {
1711                 pixel=Dc;
1712                 break;
1713               }
1714             pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1715             break;
1716           }
1717           case ChangeMaskCompositeOp:
1718           {
1719             pixel=Dc;
1720             break;
1721           }
1722           case ClearCompositeOp:
1723           {
1724             pixel=0.0;
1725             break;
1726           }
1727           case ColorBurnCompositeOp:
1728           {
1729             if ((Sca == 0.0) && (Dca == Da))
1730               {
1731                 pixel=QuantumRange*(Sa*Da+Dca*(1.0-Sa));
1732                 break;
1733               }
1734             if (Sca == 0.0)
1735               {
1736                 pixel=QuantumRange*(Dca*(1.0-Sa));
1737                 break;
1738               }
1739             pixel=QuantumRange*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-Dca/Da)*Sa/Sca)+
1740               Sca*(1.0-Da)+Dca*(1.0-Sa));
1741             break;
1742           }
1743           case ColorDodgeCompositeOp:
1744           {
1745             if ((Sca == Sa) && (Dca == 0.0))
1746               {
1747                 pixel=QuantumRange*(Sca*(1.0-Da));
1748                 break;
1749               }
1750             if (Sca == Sa)
1751               {
1752                 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1753                 break;
1754               }
1755             pixel=QuantumRange*(Sa*Da*MagickMin(1.0,Dca/Da*Sa/(Sa-Sca)));
1756             break;
1757           }
1758           case ColorizeCompositeOp:
1759           {
1760             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1761               {
1762                 pixel=Dc;
1763                 break;
1764               }
1765             if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1766               {
1767                 pixel=Sc;
1768                 break;
1769               }
1770             CompositeHCL(canvas_pixel.red,canvas_pixel.green,
1771               canvas_pixel.blue,&sans,&sans,&luma);
1772             CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1773               &hue,&chroma,&sans);
1774             HCLComposite(hue,chroma,luma,&red,&green,&blue);
1775             switch (channel)
1776             {
1777               case RedPixelChannel: pixel=red; break;
1778               case GreenPixelChannel: pixel=green; break;
1779               case BluePixelChannel: pixel=blue; break;
1780               default: pixel=Dc; break;
1781             }
1782             break;
1783           }
1784           case CopyAlphaCompositeOp:
1785           {
1786             pixel=Dc;
1787             break;
1788           }
1789           case CopyBlackCompositeOp:
1790           {
1791             if (channel == BlackPixelChannel)
1792               pixel=(MagickRealType) (QuantumRange-
1793                 GetPixelBlack(source_image,p));
1794             break;
1795           }
1796           case CopyBlueCompositeOp:
1797           case CopyYellowCompositeOp:
1798           {
1799             if (channel == BluePixelChannel)
1800               pixel=(MagickRealType) GetPixelBlue(source_image,p);
1801             break;
1802           }
1803           case CopyGreenCompositeOp:
1804           case CopyMagentaCompositeOp:
1805           {
1806             if (channel == GreenPixelChannel)
1807               pixel=(MagickRealType) GetPixelGreen(source_image,p);
1808             break;
1809           }
1810           case CopyRedCompositeOp:
1811           case CopyCyanCompositeOp:
1812           {
1813             if (channel == RedPixelChannel)
1814               pixel=(MagickRealType) GetPixelRed(source_image,p);
1815             break;
1816           }
1817           case DarkenCompositeOp:
1818           {
1819             /*
1820               Darken is equivalent to a 'Minimum' method
1821                 OR a greyscale version of a binary 'Or'
1822                 OR the 'Intersection' of pixel sets.
1823             */
1824             if ((Sca*Da) < (Dca*Sa))
1825               {
1826                 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1827                 break;
1828               }
1829             pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1830             break;
1831           }
1832           case DarkenIntensityCompositeOp:
1833           {
1834             pixel=Sa*GetPixelIntensity(source_image,p) <
1835               Da*GetPixelIntensity(image,q) ? Sc : Dc;
1836             break;
1837           }
1838           case DifferenceCompositeOp:
1839           {
1840             pixel=QuantumRange*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1841             break;
1842           }
1843           case DissolveCompositeOp:
1844           {
1845             pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1846               canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1847             break;
1848           }
1849           case DivideDstCompositeOp:
1850           {
1851             if ((fabs((double) Sca) < MagickEpsilon) &&
1852                 (fabs((double) Dca) < MagickEpsilon))
1853               {
1854                 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1855                 break;
1856               }
1857             if (fabs((double) Dca) < MagickEpsilon)
1858               {
1859                 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1860                 break;
1861               }
1862             pixel=QuantumRange*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1863             break;
1864           }
1865           case DivideSrcCompositeOp:
1866           {
1867             if ((fabs((double) Dca) < MagickEpsilon) &&
1868                 (fabs((double) Sca) < MagickEpsilon))
1869               {
1870                 pixel=QuantumRange*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1871                 break;
1872               }
1873             if (fabs((double) Sca) < MagickEpsilon)
1874               {
1875                 pixel=QuantumRange*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1876                 break;
1877               }
1878             pixel=QuantumRange*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1879             break;
1880           }
1881           case DstAtopCompositeOp:
1882           {
1883             pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1884             break;
1885           }
1886           case DstCompositeOp:
1887           case NoCompositeOp:
1888           {
1889             pixel=QuantumRange*Dca;
1890             break;
1891           }
1892           case DstInCompositeOp:
1893           {
1894             pixel=QuantumRange*(Dca*Sa);
1895             break;
1896           }
1897           case DstOutCompositeOp:
1898           {
1899             pixel=QuantumRange*(Dca*(1.0-Sa));
1900             break;
1901           }
1902           case DstOverCompositeOp:
1903           {
1904             pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1905             break;
1906           }
1907           case ExclusionCompositeOp:
1908           {
1909             pixel=QuantumRange*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
1910               (1.0-Sa));
1911             break;
1912           }
1913           case HardLightCompositeOp:
1914           {
1915             if ((2.0*Sca) < Sa)
1916               {
1917                 pixel=QuantumRange*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1918                 break;
1919               }
1920             pixel=QuantumRange*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*
1921               (1.0-Sa));
1922             break;
1923           }
1924           case HardMixCompositeOp:
1925           {
1926             double
1927               gamma;
1928
1929             if ((Sa+Da) < 1.0)
1930               gamma=0.0;
1931             else
1932               gamma=1.0;
1933             pixel=(gamma*(1.0-Sca)*(1.0-Dca))+Sa*(1.0-Sca)*Dca+Da*(1.0-Dca)*Sca;
1934             break;
1935           }
1936           case HueCompositeOp:
1937           {
1938             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1939               {
1940                 pixel=Dc;
1941                 break;
1942               }
1943             if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1944               {
1945                 pixel=Sc;
1946                 break;
1947               }
1948             CompositeHCL(canvas_pixel.red,canvas_pixel.green,
1949               canvas_pixel.blue,&hue,&chroma,&luma);
1950             CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1951               &hue,&sans,&sans);
1952             HCLComposite(hue,chroma,luma,&red,&green,&blue);
1953             switch (channel)
1954             {
1955               case RedPixelChannel: pixel=red; break;
1956               case GreenPixelChannel: pixel=green; break;
1957               case BluePixelChannel: pixel=blue; break;
1958               default: pixel=Dc; break;
1959             }
1960             break;
1961           }
1962           case InCompositeOp:
1963           case SrcInCompositeOp:
1964           {
1965             pixel=QuantumRange*(Sca*Da);
1966             break;
1967           }
1968           case LinearBurnCompositeOp:
1969           {
1970             /*
1971               LinearBurn: as defined by Abode Photoshop, according to
1972               http://www.simplefilter.de/en/basics/mixmods.html is:
1973
1974                 f(Sc,Dc) = Sc + Dc - 1
1975             */
1976             pixel=QuantumRange*(Sca+Dca-Sa*Da);
1977             break;
1978           }
1979           case LinearDodgeCompositeOp:
1980           {
1981             pixel=(Sa*Sc+Da*Dc);
1982             break;
1983           }
1984           case LinearLightCompositeOp:
1985           {
1986             /*
1987               LinearLight: as defined by Abode Photoshop, according to
1988               http://www.simplefilter.de/en/basics/mixmods.html is:
1989
1990                 f(Sc,Dc) = Dc + 2*Sc - 1
1991             */
1992             pixel=QuantumRange*((Sca-Sa)*Da+Sca+Dca);
1993             break;
1994           }
1995           case LightenCompositeOp:
1996           {
1997             if ((Sca*Da) > (Dca*Sa))
1998               {
1999                 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2000                 break;
2001               }
2002             pixel=QuantumRange*(Dca+Sca*(1.0-Da));
2003             break;
2004           }
2005           case LightenIntensityCompositeOp:
2006           {
2007             /*
2008               Lighten is equivalent to a 'Maximum' method
2009                 OR a greyscale version of a binary 'And'
2010                 OR the 'Union' of pixel sets.
2011             */
2012             pixel=Sa*GetPixelIntensity(source_image,p) >
2013               Da*GetPixelIntensity(image,q) ? Sc : Dc;
2014             break;
2015           }
2016           case LuminizeCompositeOp:
2017           {
2018             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2019               {
2020                 pixel=Dc;
2021                 break;
2022               }
2023             if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2024               {
2025                 pixel=Sc;
2026                 break;
2027               }
2028             CompositeHCL(canvas_pixel.red,canvas_pixel.green,
2029               canvas_pixel.blue,&hue,&chroma,&luma);
2030             CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2031               &sans,&sans,&luma);
2032             HCLComposite(hue,chroma,luma,&red,&green,&blue);
2033             switch (channel)
2034             {
2035               case RedPixelChannel: pixel=red; break;
2036               case GreenPixelChannel: pixel=green; break;
2037               case BluePixelChannel: pixel=blue; break;
2038               default: pixel=Dc; break;
2039             }
2040             break;
2041           }
2042           case MathematicsCompositeOp:
2043           {
2044             /*
2045               'Mathematics' a free form user control mathematical composition
2046               is defined as...
2047
2048                 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2049
2050               Where the arguments A,B,C,D are (currently) passed to composite
2051               as a command separated 'geometry' string in "compose:args" image
2052               artifact.
2053
2054                  A = a->rho,   B = a->sigma,  C = a->xi,  D = a->psi
2055
2056               Applying the SVG transparency formula (see above), we get...
2057
2058                Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2059
2060                Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2061                  Dca*(1.0-Sa)
2062             */
2063             pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2064               Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2065               Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2066             break;
2067           }
2068           case MinusDstCompositeOp:
2069           {
2070             pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2071             break;
2072           }
2073           case MinusSrcCompositeOp:
2074           {
2075             /*
2076               Minus source from canvas.
2077
2078                 f(Sc,Dc) = Sc - Dc
2079             */
2080             pixel=QuantumRange*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2081             break;
2082           }
2083           case ModulateCompositeOp:
2084           {
2085             ssize_t
2086               offset;
2087
2088             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2089               {
2090                 pixel=Dc;
2091                 break;
2092               }
2093             offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2094             if (offset == 0)
2095               {
2096                 pixel=Dc;
2097                 break;
2098               }
2099             CompositeHCL(canvas_pixel.red,canvas_pixel.green,
2100               canvas_pixel.blue,&hue,&chroma,&luma);
2101             luma+=(0.01*percent_luma*offset)/midpoint;
2102             chroma*=0.01*percent_chroma;
2103             HCLComposite(hue,chroma,luma,&red,&green,&blue);
2104             switch (channel)
2105             {
2106               case RedPixelChannel: pixel=red; break;
2107               case GreenPixelChannel: pixel=green; break;
2108               case BluePixelChannel: pixel=blue; break;
2109               default: pixel=Dc; break;
2110             }
2111             break;
2112           }
2113           case ModulusAddCompositeOp:
2114           {
2115             pixel=Sc+Dc;
2116             if (pixel > QuantumRange)
2117               pixel-=QuantumRange;
2118             pixel=gamma*(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2119             break;
2120           }
2121           case ModulusSubtractCompositeOp:
2122           {
2123             pixel=Sc-Dc;
2124             if (pixel < 0.0)
2125               pixel+=QuantumRange;
2126             pixel=gamma*(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2127             break;
2128           }
2129           case MultiplyCompositeOp:
2130           {
2131             pixel=QuantumRange*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2132             break;
2133           }
2134           case OutCompositeOp:
2135           case SrcOutCompositeOp:
2136           {
2137             pixel=QuantumRange*(Sca*(1.0-Da));
2138             break;
2139           }
2140           case OverCompositeOp:
2141           case SrcOverCompositeOp:
2142           {
2143             pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2144             break;
2145           }
2146           case OverlayCompositeOp:
2147           {
2148             if ((2.0*Dca) <= Da)
2149               {
2150                 pixel=QuantumRange*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2151                 break;
2152               }
2153             pixel=QuantumRange*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*
2154               (1.0-Sa));
2155             break;
2156           }
2157           case PegtopLightCompositeOp:
2158           {
2159             /*
2160               PegTop: A Soft-Light alternative: A continuous version of the
2161               Softlight function, producing very similar results.
2162
2163                 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2164
2165               http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2166             */
2167             if (fabs((double) Da) < MagickEpsilon)
2168               {
2169                 pixel=QuantumRange*(Sca);
2170                 break;
2171               }
2172             pixel=QuantumRange*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+
2173               Dca*(1.0-Sa));
2174             break;
2175           }
2176           case PinLightCompositeOp:
2177           {
2178             /*
2179               PinLight: A Photoshop 7 composition method
2180               http://www.simplefilter.de/en/basics/mixmods.html
2181
2182                 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc   ? 2*Sc : Dc
2183             */
2184             if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2185               {
2186                 pixel=QuantumRange*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2187                 break;
2188               }
2189             if ((Dca*Sa) > (2.0*Sca*Da))
2190               {
2191                 pixel=QuantumRange*(Sca*Da+Sca+Dca*(1.0-Sa));
2192                 break;
2193               }
2194             pixel=QuantumRange*(Sca*(1.0-Da)+Dca);
2195             break;
2196           }
2197           case PlusCompositeOp:
2198           {
2199             pixel=QuantumRange*(Sca+Dca);
2200             break;
2201           }
2202           case SaturateCompositeOp:
2203           {
2204             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2205               {
2206                 pixel=Dc;
2207                 break;
2208               }
2209             if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2210               {
2211                 pixel=Sc;
2212                 break;
2213               }
2214             CompositeHCL(canvas_pixel.red,canvas_pixel.green,
2215               canvas_pixel.blue,&hue,&chroma,&luma);
2216             CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2217               &sans,&chroma,&sans);
2218             HCLComposite(hue,chroma,luma,&red,&green,&blue);
2219             switch (channel)
2220             {
2221               case RedPixelChannel: pixel=red; break;
2222               case GreenPixelChannel: pixel=green; break;
2223               case BluePixelChannel: pixel=blue; break;
2224               default: pixel=Dc; break;
2225             }
2226             break;
2227           }
2228           case ScreenCompositeOp:
2229           {
2230             /*
2231               Screen:  a negated multiply:
2232
2233                 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2234             */
2235             pixel=QuantumRange*(Sca+Dca-Sca*Dca);
2236             break;
2237           }
2238           case SoftLightCompositeOp:
2239           {
2240             if ((2.0*Sca) < Sa)
2241               {
2242                 pixel=QuantumRange*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+Sca*
2243                   (1.0-Da)+Dca*(1.0-Sa));
2244                 break;
2245               }
2246             if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2247               {
2248                 pixel=QuantumRange*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*(4.0*
2249                   (Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+Dca*
2250                   (1.0-Sa));
2251                 break;
2252               }
2253             pixel=QuantumRange*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-(Dca/
2254               Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2255             break;
2256           }
2257           case ThresholdCompositeOp:
2258           {
2259             MagickRealType
2260               delta;
2261
2262             delta=Sc-Dc;
2263             if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2264               {
2265                 pixel=gamma*Dc;
2266                 break;
2267               }
2268             pixel=gamma*(Dc+delta*amount);
2269             break;
2270           }
2271           case VividLightCompositeOp:
2272           {
2273             /*
2274               VividLight: A Photoshop 7 composition method.  See
2275               http://www.simplefilter.de/en/basics/mixmods.html.
2276
2277                 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2278             */
2279             if ((fabs((double) Sa) < MagickEpsilon) ||
2280                 (fabs((double) (Sca-Sa)) < MagickEpsilon))
2281               {
2282                 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2283                 break;
2284               }
2285             if ((2.0*Sca) <= Sa)
2286               {
2287                 pixel=QuantumRange*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+
2288                   Dca*(1.0-Sa));
2289                 break;
2290               }
2291             pixel=QuantumRange*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*
2292               (1.0-Sa));
2293             break;
2294           }
2295           case XorCompositeOp:
2296           {
2297             pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2298             break;
2299           }
2300           default:
2301           {
2302             pixel=Sc;
2303             break;
2304           }
2305         }
2306         q[i]=ClampPixel(pixel);
2307       }
2308       p+=GetPixelChannels(source_image);
2309       channels=GetPixelChannels(source_image);
2310       if (p >= (pixels+channels*source_image->columns))
2311         p=pixels;
2312       q+=GetPixelChannels(image);
2313     }
2314     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2315       status=MagickFalse;
2316     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2317       {
2318         MagickBooleanType
2319           proceed;
2320
2321 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2322         #pragma omp critical (MagickCore_CompositeImage)
2323 #endif
2324         proceed=SetImageProgress(image,CompositeImageTag,progress++,
2325           image->rows);
2326         if (proceed == MagickFalse)
2327           status=MagickFalse;
2328       }
2329   }
2330   source_view=DestroyCacheView(source_view);
2331   image_view=DestroyCacheView(image_view);
2332   if (canvas_image != (Image * ) NULL)
2333     canvas_image=DestroyImage(canvas_image);
2334   else
2335     source_image=DestroyImage(source_image);
2336   return(status);
2337 }
2338 \f
2339 /*
2340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2341 %                                                                             %
2342 %                                                                             %
2343 %                                                                             %
2344 %     T e x t u r e I m a g e                                                 %
2345 %                                                                             %
2346 %                                                                             %
2347 %                                                                             %
2348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2349 %
2350 %  TextureImage() repeatedly tiles the texture image across and down the image
2351 %  canvas.
2352 %
2353 %  The format of the TextureImage method is:
2354 %
2355 %      MagickBooleanType TextureImage(Image *image,const Image *texture,
2356 %        ExceptionInfo *exception)
2357 %
2358 %  A description of each parameter follows:
2359 %
2360 %    o image: the image.
2361 %
2362 %    o texture_image: This image is the texture to layer on the background.
2363 %
2364 */
2365 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2366   ExceptionInfo *exception)
2367 {
2368 #define TextureImageTag  "Texture/Image"
2369
2370   CacheView
2371     *image_view,
2372     *texture_view;
2373
2374   Image
2375     *texture_image;
2376
2377   MagickBooleanType
2378     status;
2379
2380   ssize_t
2381     y;
2382
2383   assert(image != (Image *) NULL);
2384   if (image->debug != MagickFalse)
2385     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2386   assert(image->signature == MagickSignature);
2387   if (texture == (const Image *) NULL)
2388     return(MagickFalse);
2389   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2390     return(MagickFalse);
2391   texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2392   if (texture_image == (const Image *) NULL)
2393     return(MagickFalse);
2394   (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2395   (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2396     exception);
2397   status=MagickTrue;
2398   if ((image->compose != CopyCompositeOp) &&
2399       ((image->compose != OverCompositeOp) ||
2400        (image->alpha_trait != UndefinedPixelTrait) ||
2401        (texture_image->alpha_trait != UndefinedPixelTrait)))
2402     {
2403       /*
2404         Tile texture onto the image background.
2405       */
2406       for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2407       {
2408         register ssize_t
2409           x;
2410
2411         if (status == MagickFalse)
2412           continue;
2413         for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2414         {
2415           MagickBooleanType
2416             thread_status;
2417
2418           thread_status=CompositeImage(image,texture_image,image->compose,
2419             MagickFalse,x+texture_image->tile_offset.x,y+
2420             texture_image->tile_offset.y,exception);
2421           if (thread_status == MagickFalse)
2422             {
2423               status=thread_status;
2424               break;
2425             }
2426         }
2427         if (image->progress_monitor != (MagickProgressMonitor) NULL)
2428           {
2429             MagickBooleanType
2430               proceed;
2431
2432             proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2433               y,image->rows);
2434             if (proceed == MagickFalse)
2435               status=MagickFalse;
2436           }
2437       }
2438       (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2439         image->rows,image->rows);
2440       texture_image=DestroyImage(texture_image);
2441       return(status);
2442     }
2443   /*
2444     Tile texture onto the image background (optimized).
2445   */
2446   status=MagickTrue;
2447   texture_view=AcquireVirtualCacheView(texture_image,exception);
2448   image_view=AcquireAuthenticCacheView(image,exception);
2449 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2450   #pragma omp parallel for schedule(static,4) shared(status) \
2451     magick_threads(texture_image,image,1,1)
2452 #endif
2453   for (y=0; y < (ssize_t) image->rows; y++)
2454   {
2455     MagickBooleanType
2456       sync;
2457
2458     register const Quantum
2459       *p,
2460       *pixels;
2461
2462     register ssize_t
2463       x;
2464
2465     register Quantum
2466       *q;
2467
2468     size_t
2469       width;
2470
2471     if (status == MagickFalse)
2472       continue;
2473     pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2474       (y+texture_image->tile_offset.y) % texture_image->rows,
2475       texture_image->columns,1,exception);
2476     q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2477     if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2478       {
2479         status=MagickFalse;
2480         continue;
2481       }
2482     for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2483     {
2484       register ssize_t
2485         j;
2486
2487       p=pixels;
2488       width=texture_image->columns;
2489       if ((x+(ssize_t) width) > (ssize_t) image->columns)
2490         width=image->columns-x;
2491       for (j=0; j < (ssize_t) width; j++)
2492       {
2493         register ssize_t
2494           i;
2495
2496         if (GetPixelReadMask(image,q) == 0)
2497           {
2498             p+=GetPixelChannels(texture_image);
2499             q+=GetPixelChannels(image);
2500             continue;
2501           }
2502         for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2503         {
2504           PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2505           PixelTrait traits=GetPixelChannelTraits(image,channel);
2506           PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2507             channel);
2508           if ((traits == UndefinedPixelTrait) ||
2509               (texture_traits == UndefinedPixelTrait))
2510             continue;
2511           SetPixelChannel(image,channel,p[i],q);
2512         }
2513         p+=GetPixelChannels(texture_image);
2514         q+=GetPixelChannels(image);
2515       }
2516     }
2517     sync=SyncCacheViewAuthenticPixels(image_view,exception);
2518     if (sync == MagickFalse)
2519       status=MagickFalse;
2520     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2521       {
2522         MagickBooleanType
2523           proceed;
2524
2525         proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2526           image->rows);
2527         if (proceed == MagickFalse)
2528           status=MagickFalse;
2529       }
2530   }
2531   texture_view=DestroyCacheView(texture_view);
2532   image_view=DestroyCacheView(image_view);
2533   texture_image=DestroyImage(texture_image);
2534   return(status);
2535 }