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