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