]> 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]=ClampPixel(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]=ClampPixel(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,ClampPixel(GetPixelIntensity(source_image,p)),q);
709           p+=GetPixelChannels(source_image);
710           q+=GetPixelChannels(image);
711         }
712         sync=SyncCacheViewAuthenticPixels(image_view,exception);
713         if (sync == MagickFalse)
714           status=MagickFalse;
715         if (image->progress_monitor != (MagickProgressMonitor) NULL)
716           {
717             MagickBooleanType
718               proceed;
719
720 #if defined(MAGICKCORE_OPENMP_SUPPORT)
721             #pragma omp critical (MagickCore_CompositeImage)
722 #endif
723             proceed=SetImageProgress(image,CompositeImageTag,
724               (MagickOffsetType) y,image->rows);
725             if (proceed == MagickFalse)
726               status=MagickFalse;
727           }
728       }
729       source_view=DestroyCacheView(source_view);
730       image_view=DestroyCacheView(image_view);
731       source_image=DestroyImage(source_image);
732       return(status);
733     }
734     case CopyAlphaCompositeOp:
735     case ChangeMaskCompositeOp:
736     {
737       /*
738         Modify canvas outside the overlaid region and require an alpha
739         channel to exist, to add transparency.
740       */
741       if (image->alpha_trait == UndefinedPixelTrait)
742         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
743       break;
744     }
745     case BlurCompositeOp:
746     {
747       CacheView
748         *canvas_view,
749         *source_view;
750
751       const char
752         *value;
753
754       MagickRealType
755         angle_range,
756         angle_start,
757         height,
758         width;
759
760       PixelInfo
761         pixel;
762
763       ResampleFilter
764         *resample_filter;
765
766       SegmentInfo
767         blur;
768
769       /*
770         Blur Image by resampling.
771
772         Blur Image dictated by an overlay gradient map: X = red_channel;
773           Y = green_channel; compose:args =  x_scale[,y_scale[,angle]].
774       */
775       canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
776         exception);
777       if (canvas_image == (Image *) NULL)
778         {
779           source_image=DestroyImage(source_image);
780           return(MagickFalse);
781         }
782       /*
783         Gather the maximum blur sigma values from user.
784       */
785       SetGeometryInfo(&geometry_info);
786       flags=NoValue;
787       value=GetImageArtifact(image,"compose:args");
788       if (value != (const char *) NULL)
789         flags=ParseGeometry(value,&geometry_info);
790       if ((flags & WidthValue) == 0)
791         {
792           (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
793             "InvalidSetting","'%s' '%s'","compose:args",value);
794           source_image=DestroyImage(source_image);
795           canvas_image=DestroyImage(canvas_image);
796           return(MagickFalse);
797         }
798       /*
799         Users input sigma now needs to be converted to the EWA ellipse size.
800         The filter defaults to a sigma of 0.5 so to make this match the
801         users input the ellipse size needs to be doubled.
802       */
803       width=height=geometry_info.rho*2.0;
804       if ((flags & HeightValue) != 0 )
805         height=geometry_info.sigma*2.0;
806       /*
807         Default the unrotated ellipse width and height axis vectors.
808       */
809       blur.x1=width;
810       blur.x2=0.0;
811       blur.y1=0.0;
812       blur.y2=height;
813       /* rotate vectors if a rotation angle is given */
814       if ((flags & XValue) != 0 )
815         {
816           MagickRealType
817             angle;
818
819           angle=DegreesToRadians(geometry_info.xi);
820           blur.x1=width*cos(angle);
821           blur.x2=width*sin(angle);
822           blur.y1=(-height*sin(angle));
823           blur.y2=height*cos(angle);
824         }
825       /* Otherwise lets set a angle range and calculate in the loop */
826       angle_start=0.0;
827       angle_range=0.0;
828       if ((flags & YValue) != 0 )
829         {
830           angle_start=DegreesToRadians(geometry_info.xi);
831           angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
832         }
833       /*
834         Set up a gaussian cylindrical filter for EWA Bluring.
835
836         As the minimum ellipse radius of support*1.0 the EWA algorithm
837         can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
838         This means that even 'No Blur' will be still a little blurry!
839
840         The solution (as well as the problem of preventing any user
841         expert filter settings, is to set our own user settings, then
842         restore them afterwards.
843       */
844       resample_filter=AcquireResampleFilter(image,exception);
845       SetResampleFilter(resample_filter,GaussianFilter);
846
847       /* do the variable blurring of each pixel in image */
848       GetPixelInfo(image,&pixel);
849       source_view=AcquireVirtualCacheView(source_image,exception);
850       canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
851       for (y=0; y < (ssize_t) source_image->rows; y++)
852       {
853         MagickBooleanType
854           sync;
855
856         register const Quantum
857           *restrict p;
858
859         register Quantum
860           *restrict q;
861
862         register ssize_t
863           x;
864
865         if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
866           continue;
867         p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,
868           1,exception);
869         q=QueueCacheViewAuthenticPixels(canvas_view,0,y,
870           canvas_image->columns,1,exception);
871         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
872           break;
873         for (x=0; x < (ssize_t) source_image->columns; x++)
874         {
875           if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
876             {
877               p+=GetPixelChannels(source_image);
878               continue;
879             }
880           if (fabs((double) angle_range) > MagickEpsilon)
881             {
882               MagickRealType
883                 angle;
884
885               angle=angle_start+angle_range*QuantumScale*
886                 GetPixelBlue(source_image,p);
887               blur.x1=width*cos(angle);
888               blur.x2=width*sin(angle);
889               blur.y1=(-height*sin(angle));
890               blur.y2=height*cos(angle);
891             }
892 #if 0
893           if ( x == 10 && y == 60 ) {
894             (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
895               blur.x2,blur.y1, blur.y2);
896             (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
897               GetPixelRed(p),QuantumScale*GetPixelGreen(p));
898 #endif
899           ScaleResampleFilter(resample_filter,
900             blur.x1*QuantumScale*GetPixelRed(source_image,p),
901             blur.y1*QuantumScale*GetPixelGreen(source_image,p),
902             blur.x2*QuantumScale*GetPixelRed(source_image,p),
903             blur.y2*QuantumScale*GetPixelGreen(source_image,p) );
904           (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
905             (double) y_offset+y,&pixel,exception);
906           SetPixelViaPixelInfo(canvas_image,&pixel,q);
907           p+=GetPixelChannels(source_image);
908           q+=GetPixelChannels(canvas_image);
909         }
910         sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
911         if (sync == MagickFalse)
912           break;
913       }
914       resample_filter=DestroyResampleFilter(resample_filter);
915       source_view=DestroyCacheView(source_view);
916       canvas_view=DestroyCacheView(canvas_view);
917       source_image=DestroyImage(source_image);
918       source_image=canvas_image;
919       break;
920     }
921     case DisplaceCompositeOp:
922     case DistortCompositeOp:
923     {
924       CacheView
925         *source_view,
926         *canvas_view,
927         *image_view;
928
929       const char
930         *value;
931
932       MagickRealType
933         horizontal_scale,
934         vertical_scale;
935
936       PixelInfo
937         pixel;
938
939       PointInfo
940         center,
941         offset;
942
943       /*
944         Displace/Distort based on overlay gradient map:
945           X = red_channel;  Y = green_channel;
946           compose:args = x_scale[,y_scale[,center.x,center.y]]
947       */
948       canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
949         exception);
950       if (canvas_image == (Image *) NULL)
951         {
952           source_image=DestroyImage(source_image);
953           return(MagickFalse);
954         }
955       SetGeometryInfo(&geometry_info);
956       flags=NoValue;
957       value=GetImageArtifact(image,"compose:args");
958       if (value != (char *) NULL)
959         flags=ParseGeometry(value,&geometry_info);
960       if ((flags & (WidthValue | HeightValue)) == 0 )
961         {
962           if ((flags & AspectValue) == 0)
963             {
964               horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
965               vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
966             }
967           else
968             {
969               horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
970               vertical_scale=(MagickRealType) (image->rows-1)/2.0;
971             }
972         }
973       else
974         {
975           horizontal_scale=geometry_info.rho;
976           vertical_scale=geometry_info.sigma;
977           if ((flags & PercentValue) != 0)
978             {
979               if ((flags & AspectValue) == 0)
980                 {
981                   horizontal_scale*=(source_image->columns-1)/200.0;
982                   vertical_scale*=(source_image->rows-1)/200.0;
983                 }
984               else
985                 {
986                   horizontal_scale*=(image->columns-1)/200.0;
987                   vertical_scale*=(image->rows-1)/200.0;
988                 }
989             }
990           if ((flags & HeightValue) == 0)
991             vertical_scale=horizontal_scale;
992         }
993       /*
994         Determine fixed center point for absolute distortion map
995          Absolute distort ==
996            Displace offset relative to a fixed absolute point
997            Select that point according to +X+Y user inputs.
998            default = center of overlay image
999            arg flag '!' = locations/percentage relative to background image
1000       */
1001       center.x=(MagickRealType) x_offset;
1002       center.y=(MagickRealType) y_offset;
1003       if (compose == DistortCompositeOp)
1004         {
1005           if ((flags & XValue) == 0)
1006             if ((flags & AspectValue) != 0)
1007               center.x=(MagickRealType) ((image->columns-1)/2.0);
1008             else
1009               center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1010                 2.0);
1011           else
1012             if ((flags & AspectValue) != 0)
1013               center.x=geometry_info.xi;
1014             else
1015               center.x=(MagickRealType) (x_offset+geometry_info.xi);
1016           if ((flags & YValue) == 0)
1017             if ((flags & AspectValue) != 0)
1018               center.y=(MagickRealType) ((image->rows-1)/2.0);
1019             else
1020               center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1021           else
1022             if ((flags & AspectValue) != 0)
1023               center.y=geometry_info.psi;
1024             else
1025               center.y=(MagickRealType) (y_offset+geometry_info.psi);
1026         }
1027       /*
1028         Shift the pixel offset point as defined by the provided,
1029         displacement/distortion map.  -- Like a lens...
1030       */
1031       GetPixelInfo(image,&pixel);
1032       image_view=AcquireVirtualCacheView(image,exception);
1033       source_view=AcquireVirtualCacheView(source_image,exception);
1034       canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1035       for (y=0; y < (ssize_t) source_image->rows; y++)
1036       {
1037         MagickBooleanType
1038           sync;
1039
1040         register const Quantum
1041           *restrict p;
1042
1043         register Quantum
1044           *restrict q;
1045
1046         register ssize_t
1047           x;
1048
1049         if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1050           continue;
1051         p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1052           exception);
1053         q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1054           exception);
1055         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1056           break;
1057         for (x=0; x < (ssize_t) source_image->columns; x++)
1058         {
1059           if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1060             {
1061               p+=GetPixelChannels(source_image);
1062               continue;
1063             }
1064           /*
1065             Displace the offset.
1066           */
1067           offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1068             (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1069             QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1070             x : 0);
1071           offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1072             (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1073             QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1074             y : 0);
1075           (void) InterpolatePixelInfo(image,image_view,
1076             UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1077             &pixel,exception);
1078           /*
1079             Mask with the 'invalid pixel mask' in alpha channel.
1080           */
1081           pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
1082             (QuantumScale*GetPixelAlpha(source_image,p));
1083           SetPixelViaPixelInfo(canvas_image,&pixel,q);
1084           p+=GetPixelChannels(source_image);
1085           q+=GetPixelChannels(canvas_image);
1086         }
1087         sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1088         if (sync == MagickFalse)
1089           break;
1090       }
1091       canvas_view=DestroyCacheView(canvas_view);
1092       source_view=DestroyCacheView(source_view);
1093       image_view=DestroyCacheView(image_view);
1094       source_image=DestroyImage(source_image);
1095       source_image=canvas_image;
1096       break;
1097     }
1098     case DissolveCompositeOp:
1099     {
1100       const char
1101         *value;
1102
1103       /*
1104         Geometry arguments to dissolve factors.
1105       */
1106       value=GetImageArtifact(image,"compose:args");
1107       if (value != (char *) NULL)
1108         {
1109           flags=ParseGeometry(value,&geometry_info);
1110           source_dissolve=geometry_info.rho/100.0;
1111           canvas_dissolve=1.0;
1112           if ((source_dissolve-MagickEpsilon) < 0.0)
1113             source_dissolve=0.0;
1114           if ((source_dissolve+MagickEpsilon) > 1.0)
1115             {
1116               canvas_dissolve=2.0-source_dissolve;
1117               source_dissolve=1.0;
1118             }
1119           if ((flags & SigmaValue) != 0)
1120             canvas_dissolve=geometry_info.sigma/100.0;
1121           if ((canvas_dissolve-MagickEpsilon) < 0.0)
1122             canvas_dissolve=0.0;
1123         }
1124       break;
1125     }
1126     case BlendCompositeOp:
1127     {
1128       const char
1129         *value;
1130
1131       value=GetImageArtifact(image,"compose:args");
1132       if (value != (char *) NULL)
1133         {
1134           flags=ParseGeometry(value,&geometry_info);
1135           source_dissolve=geometry_info.rho/100.0;
1136           canvas_dissolve=1.0-source_dissolve;
1137           if ((flags & SigmaValue) != 0)
1138             canvas_dissolve=geometry_info.sigma/100.0;
1139         }
1140       break;
1141     }
1142     case MathematicsCompositeOp:
1143     {
1144       const char
1145         *value;
1146
1147       /*
1148         Just collect the values from "compose:args", setting.
1149         Unused values are set to zero automagically.
1150
1151         Arguments are normally a comma separated list, so this probably should
1152         be changed to some 'general comma list' parser, (with a minimum
1153         number of values)
1154       */
1155       SetGeometryInfo(&geometry_info);
1156       value=GetImageArtifact(image,"compose:args");
1157       if (value != (char *) NULL)
1158         (void) ParseGeometry(value,&geometry_info);
1159       break;
1160     }
1161     case ModulateCompositeOp:
1162     {
1163       const char
1164         *value;
1165
1166       /*
1167         Determine the luma and chroma scale.
1168       */
1169       value=GetImageArtifact(image,"compose:args");
1170       if (value != (char *) NULL)
1171         {
1172           flags=ParseGeometry(value,&geometry_info);
1173           percent_luma=geometry_info.rho;
1174           if ((flags & SigmaValue) != 0)
1175             percent_chroma=geometry_info.sigma;
1176         }
1177       break;
1178     }
1179     case ThresholdCompositeOp:
1180     {
1181       const char
1182         *value;
1183
1184       /*
1185         Determine the amount and threshold.
1186       */
1187       value=GetImageArtifact(image,"compose:args");
1188       if (value != (char *) NULL)
1189         {
1190           flags=ParseGeometry(value,&geometry_info);
1191           amount=geometry_info.rho;
1192           threshold=geometry_info.sigma;
1193           if ((flags & SigmaValue) == 0)
1194             threshold=0.05f;
1195         }
1196       threshold*=QuantumRange;
1197       break;
1198     }
1199     default:
1200       break;
1201   }
1202   /*
1203     Composite image.
1204   */
1205   status=MagickTrue;
1206   progress=0;
1207   midpoint=((MagickRealType) QuantumRange+1.0)/2;
1208   source_view=AcquireVirtualCacheView(source_image,exception);
1209   image_view=AcquireAuthenticCacheView(image,exception);
1210 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1211   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1212     magick_threads(source_image,image,image->rows,1)
1213 #endif
1214   for (y=0; y < (ssize_t) image->rows; y++)
1215   {
1216     const Quantum
1217       *pixels;
1218
1219     MagickRealType
1220       blue,
1221       chroma,
1222       green,
1223       hue,
1224       luma,
1225       red;
1226
1227     PixelInfo
1228       canvas_pixel,
1229       source_pixel;
1230
1231     register const Quantum
1232       *restrict p;
1233
1234     register Quantum
1235       *restrict q;
1236
1237     register ssize_t
1238       x;
1239
1240     if (status == MagickFalse)
1241       continue;
1242     if (clip_to_self != MagickFalse)
1243       {
1244         if (y < y_offset)
1245           continue;
1246         if ((y-y_offset) >= (ssize_t) source_image->rows)
1247           continue;
1248       }
1249     /*
1250       If pixels is NULL, y is outside overlay region.
1251     */
1252     pixels=(Quantum *) NULL;
1253     p=(Quantum *) NULL;
1254     if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1255       {
1256         p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1257           source_image->columns,1,exception);
1258         if (p == (const Quantum *) NULL)
1259           {
1260             status=MagickFalse;
1261             continue;
1262           }
1263         pixels=p;
1264         if (x_offset < 0)
1265           p-=x_offset*GetPixelChannels(source_image);
1266       }
1267     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1268     if (q == (Quantum *) NULL)
1269       {
1270         status=MagickFalse;
1271         continue;
1272       }
1273     hue=0.0;
1274     chroma=0.0;
1275     luma=0.0;
1276     GetPixelInfo(image,&canvas_pixel);
1277     GetPixelInfo(source_image,&source_pixel);
1278     for (x=0; x < (ssize_t) image->columns; x++)
1279     {
1280       double
1281         gamma;
1282
1283       MagickRealType
1284         alpha,
1285         Da,
1286         Dc,
1287         Dca,
1288         Sa,
1289         Sc,
1290         Sca;
1291
1292       register ssize_t
1293         i;
1294
1295       size_t
1296         channels;
1297
1298       if (clip_to_self != MagickFalse)
1299         {
1300           if (x < x_offset)
1301             {
1302               q+=GetPixelChannels(image);
1303               continue;
1304             }
1305           if ((x-x_offset) >= (ssize_t) source_image->columns)
1306             break;
1307         }
1308       if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1309           ((x-x_offset) >= (ssize_t) source_image->columns))
1310         {
1311           Quantum
1312             source[MaxPixelChannels];
1313
1314           /*
1315             Virtual composite:
1316               Sc: source color.
1317               Dc: canvas color.
1318           */
1319           (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,
1320             source,exception);
1321           if (GetPixelReadMask(image,q) == 0)
1322             {
1323               q+=GetPixelChannels(image);
1324               continue;
1325             }
1326           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1327           {
1328             MagickRealType
1329               pixel;
1330
1331             PixelChannel channel=GetPixelChannelChannel(image,i);
1332             PixelTrait traits=GetPixelChannelTraits(image,channel);
1333             PixelTrait source_traits=GetPixelChannelTraits(source_image,
1334               channel);
1335             if ((traits == UndefinedPixelTrait) ||
1336                 (source_traits == UndefinedPixelTrait))
1337               continue;
1338             switch (compose)
1339             {
1340               case AlphaCompositeOp:
1341               case ChangeMaskCompositeOp:
1342               case CopyAlphaCompositeOp:
1343               case DstAtopCompositeOp:
1344               case DstInCompositeOp:
1345               case InCompositeOp:
1346               case OutCompositeOp:
1347               case SrcInCompositeOp:
1348               case SrcOutCompositeOp:
1349               {
1350                 if (channel == AlphaPixelChannel)
1351                   pixel=(MagickRealType) TransparentAlpha;
1352                 else
1353                   pixel=(MagickRealType) q[i];
1354                 break;
1355               }
1356               case ClearCompositeOp:
1357               case CopyCompositeOp:
1358               case ReplaceCompositeOp:
1359               case SrcCompositeOp:
1360               {
1361                 if (channel == AlphaPixelChannel)
1362                   pixel=(MagickRealType) TransparentAlpha;
1363                 else
1364                   pixel=0.0;
1365                 break;
1366               }
1367               case BlendCompositeOp:
1368               case DissolveCompositeOp:
1369               {
1370                 if (channel == AlphaPixelChannel)
1371                   pixel=canvas_dissolve*GetPixelAlpha(source_image,source);
1372                 else
1373                   pixel=(MagickRealType) source[channel];
1374                 break;
1375               }
1376               default:
1377               {
1378                 pixel=(MagickRealType) source[channel];
1379                 break;
1380               }
1381             }
1382             q[i]=ClampPixel(pixel);
1383           }
1384           q+=GetPixelChannels(image);
1385           continue;
1386         }
1387       /*
1388         Authentic composite:
1389           Sa:  normalized source alpha.
1390           Da:  normalized canvas alpha.
1391       */
1392       Sa=QuantumScale*GetPixelAlpha(source_image,p);
1393       Da=QuantumScale*GetPixelAlpha(image,q);
1394       switch (compose)
1395       {
1396         case BumpmapCompositeOp:
1397         {
1398           alpha=GetPixelIntensity(source_image,p)*Sa;
1399           break;
1400         }
1401         case ColorBurnCompositeOp:
1402         case ColorDodgeCompositeOp:
1403         case DarkenCompositeOp:
1404         case DifferenceCompositeOp:
1405         case DivideDstCompositeOp:
1406         case DivideSrcCompositeOp:
1407         case ExclusionCompositeOp:
1408         case HardLightCompositeOp:
1409         case HardMixCompositeOp:
1410         case LinearBurnCompositeOp:
1411         case LinearDodgeCompositeOp:
1412         case LinearLightCompositeOp:
1413         case LightenCompositeOp:
1414         case MathematicsCompositeOp:
1415         case MinusDstCompositeOp:
1416         case MinusSrcCompositeOp:
1417         case ModulusAddCompositeOp:
1418         case ModulusSubtractCompositeOp:
1419         case MultiplyCompositeOp:
1420         case OverlayCompositeOp:
1421         case PegtopLightCompositeOp:
1422         case PinLightCompositeOp:
1423         case ScreenCompositeOp:
1424         case SoftLightCompositeOp:
1425         case VividLightCompositeOp:
1426         {
1427           alpha=RoundToUnity(Sa+Da-Sa*Da);
1428           break;
1429         }
1430         case DstAtopCompositeOp:
1431         case DstInCompositeOp:
1432         case InCompositeOp:
1433         case SrcInCompositeOp:
1434         {
1435           alpha=Sa*Da;
1436           break;
1437         }
1438         case DissolveCompositeOp:
1439         {
1440           alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*
1441             Sa+canvas_dissolve*Da;
1442           break;
1443         }
1444         case DstOverCompositeOp:
1445         {
1446           alpha=Sa+Da-Sa*Da;
1447           break;
1448         }
1449         case DstOutCompositeOp:
1450         {
1451           alpha=Da*(1.0-Sa);
1452           break;
1453         }
1454         case OutCompositeOp:
1455         case SrcOutCompositeOp:
1456         {
1457           alpha=Sa*(1.0-Da);
1458           break;
1459         }
1460         case OverCompositeOp:
1461         case SrcOverCompositeOp:
1462         {
1463           alpha=Sa+Da-Sa*Da;
1464           break;
1465         }
1466         case BlendCompositeOp:
1467         case PlusCompositeOp:
1468         {
1469           alpha=RoundToUnity(Sa+Da);
1470           break;
1471         }
1472         case XorCompositeOp:
1473         {
1474           alpha=Sa+Da-2.0*Sa*Da;
1475           break;
1476         }
1477         default:
1478         {
1479           alpha=1.0;
1480           break;
1481         }
1482       }
1483       if (GetPixelReadMask(image,q) == 0)
1484         {
1485           p+=GetPixelChannels(source_image);
1486           q+=GetPixelChannels(image);
1487           continue;
1488         }
1489       switch (compose)
1490       {
1491         case ColorizeCompositeOp:
1492         case HueCompositeOp:
1493         case LuminizeCompositeOp:
1494         case ModulateCompositeOp:
1495         case SaturateCompositeOp:
1496         {
1497           GetPixelInfoPixel(source_image,p,&source_pixel);
1498           GetPixelInfoPixel(image,q,&canvas_pixel);
1499           break;
1500         }
1501         default:
1502           break;
1503       }
1504       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1505       {
1506         MagickRealType
1507           pixel,
1508           sans;
1509
1510         PixelChannel channel=GetPixelChannelChannel(image,i);
1511         PixelTrait traits=GetPixelChannelTraits(image,channel);
1512         PixelTrait source_traits=GetPixelChannelTraits(source_image,
1513           channel);
1514         if (traits == UndefinedPixelTrait)
1515           continue;
1516         if ((source_traits == UndefinedPixelTrait) &&
1517              (((compose != CopyAlphaCompositeOp) &&
1518                (compose != ChangeMaskCompositeOp)) ||
1519               (channel != AlphaPixelChannel)))
1520             continue;
1521         /*
1522           Sc: source color.
1523           Dc: canvas color.
1524         */
1525         Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1526         Dc=(MagickRealType) q[i];
1527         if ((traits & CopyPixelTrait) != 0)
1528           {
1529             /*
1530               Copy channel.
1531             */
1532             q[i]=ClampPixel(Sc);
1533             continue;
1534           }
1535         if (channel == AlphaPixelChannel)
1536           {
1537             /*
1538               Set alpha channel.
1539             */
1540             switch (compose)
1541             {
1542               case AlphaCompositeOp:
1543               {
1544                 pixel=QuantumRange*Sa;
1545                 break;
1546               }
1547               case AtopCompositeOp:
1548               case CopyBlackCompositeOp:
1549               case CopyBlueCompositeOp:
1550               case CopyCyanCompositeOp:
1551               case CopyGreenCompositeOp:
1552               case CopyMagentaCompositeOp:
1553               case CopyRedCompositeOp:
1554               case CopyYellowCompositeOp:
1555               case SrcAtopCompositeOp:
1556               case DstCompositeOp:
1557               case NoCompositeOp:
1558               {
1559                 pixel=QuantumRange*Da;
1560                 break;
1561               }
1562               case ChangeMaskCompositeOp:
1563               {
1564                 MagickBooleanType
1565                   equivalent;
1566
1567                 if (Da > ((MagickRealType) QuantumRange/2.0))
1568                   {
1569                     pixel=(MagickRealType) TransparentAlpha;
1570                     break;
1571                   }
1572                 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1573                 if (equivalent != MagickFalse)
1574                   pixel=(MagickRealType) TransparentAlpha;
1575                 else
1576                   pixel=(MagickRealType) OpaqueAlpha;
1577                 break;
1578               }
1579               case ClearCompositeOp:
1580               {
1581                 pixel=(MagickRealType) TransparentAlpha;
1582                 break;
1583               }
1584               case ColorizeCompositeOp:
1585               case HueCompositeOp:
1586               case LuminizeCompositeOp:
1587               case SaturateCompositeOp:
1588               {
1589                 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1590                   {
1591                     pixel=QuantumRange*Da;
1592                     break;
1593                   }
1594                 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1595                   {
1596                     pixel=QuantumRange*Sa;
1597                     break;
1598                   }
1599                 if (Sa < Da)
1600                   {
1601                     pixel=QuantumRange*Da;
1602                     break;
1603                   }
1604                 pixel=QuantumRange*Sa;
1605                 break;
1606               }
1607               case CopyAlphaCompositeOp:
1608               {
1609                 if ((source_traits & BlendPixelTrait) == 0)
1610                   pixel=GetPixelIntensity(source_image,p);
1611                 else
1612                   pixel=QuantumRange*Sa;
1613                 break;
1614               }
1615               case CopyCompositeOp:
1616               case DisplaceCompositeOp:
1617               case DistortCompositeOp:
1618               case DstAtopCompositeOp:
1619               case ReplaceCompositeOp:
1620               case SrcCompositeOp:
1621               {
1622                 pixel=QuantumRange*Sa;
1623                 break;
1624               }
1625               case DarkenIntensityCompositeOp:
1626               {
1627                 pixel=Sa*GetPixelIntensity(source_image,p) <
1628                   Da*GetPixelIntensity(image,q) ? Sa : Da;
1629                 break;
1630               }
1631               case LightenIntensityCompositeOp:
1632               {
1633                 pixel=Sa*GetPixelIntensity(source_image,p) >
1634                   Da*GetPixelIntensity(image,q) ? Sa : Da;
1635                 break;
1636               }
1637               case ModulateCompositeOp:
1638               {
1639                 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1640                   {
1641                     pixel=QuantumRange*Da;
1642                     break;
1643                   }
1644                 pixel=QuantumRange*Da;
1645                 break;
1646               }
1647               default:
1648               {
1649                 pixel=QuantumRange*alpha;
1650                 break;
1651               }
1652             }
1653             q[i]=ClampPixel(pixel);
1654             continue;
1655           }
1656         /*
1657           Porter-Duff compositions:
1658             Sca: source normalized color multiplied by alpha.
1659             Dca: normalized canvas color multiplied by alpha.
1660         */
1661         Sca=QuantumScale*Sa*Sc;
1662         Dca=QuantumScale*Da*Dc;
1663         switch (compose)
1664         {
1665           case DarkenCompositeOp:
1666           case LightenCompositeOp:
1667           case ModulusSubtractCompositeOp:
1668           {
1669             gamma=1.0-alpha;
1670             break;
1671           }
1672           default:
1673             break;
1674         }
1675         gamma=PerceptibleReciprocal(alpha);
1676         pixel=Dc;
1677         switch (compose)
1678         {
1679           case AlphaCompositeOp:
1680           {
1681             pixel=QuantumRange*Sa;
1682             break;
1683           }
1684           case AtopCompositeOp:
1685           case SrcAtopCompositeOp:
1686           {
1687             pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1688             break;
1689           }
1690           case BlendCompositeOp:
1691           {
1692             pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1693             break;
1694           }
1695           case BlurCompositeOp:
1696           case CopyCompositeOp:
1697           case ReplaceCompositeOp:
1698           case SrcCompositeOp:
1699           {
1700             pixel=QuantumRange*Sca;
1701             break;
1702           }
1703           case DisplaceCompositeOp:
1704           case DistortCompositeOp:
1705           {
1706             pixel=Sc;
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*QuantumRange*(geometry_info.rho*Sca*Dca+
2066               geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2067               geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(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 }