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