]> granicus.if.org Git - imagemagick/blob - MagickCore/composite.c
14cd5a41fcd01a2b2c31dcf01283cf3d0aa16882
[imagemagick] / MagickCore / composite.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %        CCCC   OOO   M   M  PPPP    OOO   SSSSS  IIIII  TTTTT  EEEEE         %
7 %       C      O   O  MM MM  P   P  O   O  SS       I      T    E             %
8 %       C      O   O  M M M  PPPP   O   O   SSS     I      T    EEE           %
9 %       C      O   O  M   M  P      O   O     SS    I      T    E             %
10 %        CCCC   OOO   M   M  P       OOO   SSSSS  IIIII    T    EEEEE         %
11 %                                                                             %
12 %                                                                             %
13 %                     MagickCore Image Composite Methods                      %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-private.h"
47 #include "MagickCore/cache-view.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/color-private.h"
52 #include "MagickCore/colorspace.h"
53 #include "MagickCore/colorspace-private.h"
54 #include "MagickCore/composite.h"
55 #include "MagickCore/composite-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/draw.h"
58 #include "MagickCore/fx.h"
59 #include "MagickCore/gem.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/image.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/list.h"
64 #include "MagickCore/log.h"
65 #include "MagickCore/monitor.h"
66 #include "MagickCore/monitor-private.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/option.h"
69 #include "MagickCore/pixel-accessor.h"
70 #include "MagickCore/property.h"
71 #include "MagickCore/quantum.h"
72 #include "MagickCore/resample.h"
73 #include "MagickCore/resource_.h"
74 #include "MagickCore/string_.h"
75 #include "MagickCore/thread-private.h"
76 #include "MagickCore/threshold.h"
77 #include "MagickCore/token.h"
78 #include "MagickCore/utility.h"
79 #include "MagickCore/utility-private.h"
80 #include "MagickCore/version.h"
81 \f
82 /*
83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84 %                                                                             %
85 %                                                                             %
86 %                                                                             %
87 %   C o m p o s i t e I m a g e                                               %
88 %                                                                             %
89 %                                                                             %
90 %                                                                             %
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 %
93 %  CompositeImage() returns the second image composited onto the first
94 %  at the specified offset, using the specified composite method.
95 %
96 %  The format of the CompositeImage method is:
97 %
98 %      MagickBooleanType CompositeImage(Image *image,
99 %        const Image *source_image,const CompositeOperator compose,
100 %        const MagickBooleanType clip_to_self,const ssize_t x_offset,
101 %        const ssize_t y_offset,ExceptionInfo *exception)
102 %
103 %  A description of each parameter follows:
104 %
105 %    o image: the canvas image, modified by he composition
106 %
107 %    o source_image: the source image.
108 %
109 %    o compose: This operator affects how the composite is applied to
110 %      the image.  The operators and how they are utilized are listed here
111 %      http://www.w3.org/TR/SVG12/#compositing.
112 %
113 %    o clip_to_self: set to MagickTrue to limit composition to area composed.
114 %
115 %    o x_offset: the column offset of the composited image.
116 %
117 %    o y_offset: the row offset of the composited image.
118 %
119 %  Extra Controls from Image meta-data in 'image' (artifacts)
120 %
121 %    o "compose:args"
122 %        A string containing extra numerical arguments for specific compose
123 %        methods, generally expressed as a 'geometry' or a comma separated list
124 %        of numbers.
125 %
126 %        Compose methods needing such arguments include "BlendCompositeOp" and
127 %        "DisplaceCompositeOp".
128 %
129 %    o exception: return any errors or warnings in this structure.
130 %
131 */
132
133 /*
134    Composition based on the SVG specification:
135
136    A Composition is defined by...
137       Color Function :  f(Sc,Dc)  where Sc and Dc are the normizalized colors
138       Blending areas :  X = 1     for area of overlap, ie: f(Sc,Dc)
139                         Y = 1     for source preserved
140                         Z = 1     for canvas preserved
141
142    Conversion to transparency (then optimized)
143       Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
144       Da'  = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
145
146    Where...
147       Sca = Sc*Sa     normalized Source color divided by Source alpha
148       Dca = Dc*Da     normalized Dest color divided by Dest alpha
149       Dc' = Dca'/Da'  the desired color value for this channel.
150
151    Da' in in the follow formula as 'gamma'  The resulting alpla value.
152
153    Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
154    the following optimizations...
155       gamma = Sa+Da-Sa*Da;
156       gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
157       opacity = QuantiumScale*alpha*beta;  // over blend, optimized 1-Gamma
158
159    The above SVG definitions also definate that Mathematical Composition
160    methods should use a 'Over' blending mode for Alpha Channel.
161    It however was not applied for composition modes of 'Plus', 'Minus',
162    the modulus versions of 'Add' and 'Subtract'.
163
164    Mathematical operator changes to be applied from IM v6.7...
165
166     1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
167        'ModulusAdd' and 'ModulusSubtract' for clarity.
168
169     2) All mathematical compositions work as per the SVG specification
170        with regard to blending.  This now includes 'ModulusAdd' and
171        'ModulusSubtract'.
172
173     3) When the special channel flag 'sync' (syncronize channel updates)
174        is turned off (enabled by default) then mathematical compositions are
175        only performed on the channels specified, and are applied
176        independantally of each other.  In other words the mathematics is
177        performed as 'pure' mathematical operations, rather than as image
178        operations.
179 */
180
181 static void HCLComposite(const MagickRealType hue,const MagickRealType chroma,
182   const MagickRealType luma,MagickRealType *red,MagickRealType *green,
183   MagickRealType *blue)
184 {
185   MagickRealType
186     b,
187     c,
188     g,
189     h,
190     m,
191     r,
192     x;
193
194   /*
195     Convert HCL to RGB colorspace.
196   */
197   assert(red != (MagickRealType *) NULL);
198   assert(green != (MagickRealType *) NULL);
199   assert(blue != (MagickRealType *) NULL);
200   h=6.0*hue;
201   c=chroma;
202   x=c*(1.0-fabs(fmod(h,2.0)-1.0));
203   r=0.0;
204   g=0.0;
205   b=0.0;
206   if ((0.0 <= h) && (h < 1.0))
207     {
208       r=c;
209       g=x;
210     }
211   else
212     if ((1.0 <= h) && (h < 2.0))
213       {
214         r=x;
215         g=c;
216       }
217     else
218       if ((2.0 <= h) && (h < 3.0))
219         {
220           g=c;
221           b=x;
222         }
223       else
224         if ((3.0 <= h) && (h < 4.0))
225           {
226             g=x;
227             b=c;
228           }
229         else
230           if ((4.0 <= h) && (h < 5.0))
231             {
232               r=x;
233               b=c;
234             }
235           else
236             if ((5.0 <= h) && (h < 6.0))
237               {
238                 r=c;
239                 b=x;
240               }
241   m=luma-(0.298839*r+0.586811*g+0.114350*b);
242   *red=QuantumRange*(r+m);
243   *green=QuantumRange*(g+m);
244   *blue=QuantumRange*(b+m);
245 }
246
247 static void CompositeHCL(const MagickRealType red,const MagickRealType green,
248   const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma,
249   MagickRealType *luma)
250 {
251   MagickRealType
252     b,
253     c,
254     g,
255     h,
256     max,
257     r;
258
259   /*
260     Convert RGB to HCL colorspace.
261   */
262   assert(hue != (MagickRealType *) NULL);
263   assert(chroma != (MagickRealType *) NULL);
264   assert(luma != (MagickRealType *) NULL);
265   r=red;
266   g=green;
267   b=blue;
268   max=MagickMax(r,MagickMax(g,b));
269   c=max-(MagickRealType) MagickMin(r,MagickMin(g,b));
270   h=0.0;
271   if (c == 0)
272     h=0.0;
273   else
274     if (red == max)
275       h=fmod((g-b)/c+6.0,6.0);
276     else
277       if (green == max)
278         h=((b-r)/c)+2.0;
279       else
280         if (blue == max)
281           h=((r-g)/c)+4.0;
282   *hue=(h/6.0);
283   *chroma=QuantumScale*c;
284   *luma=QuantumScale*(0.298839*r+0.586811*g+0.114350*b);
285 }
286
287 static MagickBooleanType CompositeOverImage(Image *image,
288   const Image *source_image,const MagickBooleanType clip_to_self,
289   const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
290 {
291 #define CompositeImageTag  "Composite/Image"
292
293   CacheView
294     *source_view,
295     *image_view;
296
297   const char
298      *value;
299
300   MagickBooleanType
301     clamp,
302     status;
303
304   MagickOffsetType
305     progress;
306
307   ssize_t
308     y;
309
310   /*
311     Composite image.
312   */
313   status=MagickTrue;
314   progress=0;
315   clamp=MagickTrue;
316   value=GetImageArtifact(image,"compose:clamp");
317   if (value != (const char *) NULL)
318     clamp=IsStringTrue(value);
319   source_view=AcquireVirtualCacheView(source_image,exception);
320   image_view=AcquireAuthenticCacheView(image,exception);
321 #if defined(MAGICKCORE_OPENMP_SUPPORT)
322   #pragma omp parallel for schedule(static,4) shared(progress,status) \
323     magick_threads(source_image,image,image->rows,1)
324 #endif
325   for (y=0; y < (ssize_t) image->rows; y++)
326   {
327     const Quantum
328       *pixels;
329
330     register const Quantum
331       *restrict p;
332
333     register Quantum
334       *restrict q;
335
336     register ssize_t
337       x;
338
339     size_t
340       channels;
341
342     if (status == MagickFalse)
343       continue;
344     if (clip_to_self != MagickFalse)
345       {
346         if (y < y_offset)
347           continue;
348         if ((y-y_offset) >= (ssize_t) source_image->rows)
349           continue;
350       }
351     /*
352       If pixels is NULL, y is outside overlay region.
353     */
354     pixels=(Quantum *) NULL;
355     p=(Quantum *) NULL;
356     if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
357       {
358         p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
359           source_image->columns,1,exception);
360         if (p == (const Quantum *) NULL)
361           {
362             status=MagickFalse;
363             continue;
364           }
365         pixels=p;
366         if (x_offset < 0)
367           p-=x_offset*GetPixelChannels(source_image);
368       }
369     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
370     if (q == (Quantum *) NULL)
371       {
372         status=MagickFalse;
373         continue;
374       }
375     for (x=0; x < (ssize_t) image->columns; x++)
376     {
377       MagickRealType
378         Da,
379         Dc,
380         Dca,
381         gamma,
382         Sa,
383         Sc,
384         Sca;
385
386       register ssize_t
387         i;
388
389       if (clip_to_self != MagickFalse)
390         {
391           if (x < x_offset)
392             {
393               q+=GetPixelChannels(image);
394               continue;
395             }
396           if ((x-x_offset) >= (ssize_t) source_image->columns)
397             break;
398         }
399       if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
400           ((x-x_offset) >= (ssize_t) source_image->columns))
401         {
402           Quantum
403             source[MaxPixelChannels];
404
405           /*
406             Virtual composite:
407               Sc: source color.
408               Dc: canvas color.
409           */
410           if (GetPixelReadMask(image,q) == 0)
411             {
412               q+=GetPixelChannels(image);
413               continue;
414             }
415           (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,
416             source,exception);
417           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
418           {
419             PixelChannel channel=GetPixelChannelChannel(image,i);
420             PixelTrait traits=GetPixelChannelTraits(image,channel);
421             PixelTrait source_traits=GetPixelChannelTraits(source_image,
422               channel);
423             if ((traits == UndefinedPixelTrait) ||
424                 (source_traits == UndefinedPixelTrait))
425               continue;
426             q[i]=source[channel];
427           }
428           q+=GetPixelChannels(image);
429           continue;
430         }
431       /*
432         Authentic composite:
433           Sa:  normalized source alpha.
434           Da:  normalized canvas alpha.
435       */
436       if (GetPixelReadMask(source_image,p) == 0)
437         {
438           p+=GetPixelChannels(source_image);
439           channels=GetPixelChannels(source_image);
440           if (p >= (pixels+channels*source_image->columns))
441             p=pixels;
442           q+=GetPixelChannels(image);
443           continue;
444         }
445       Sa=QuantumScale*GetPixelAlpha(source_image,p);
446       Da=QuantumScale*GetPixelAlpha(image,q);
447       gamma=Sa+Da-Sa*Da;
448       gamma=PerceptibleReciprocal(gamma);
449       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
450       {
451         PixelChannel channel=GetPixelChannelChannel(image,i);
452         PixelTrait traits=GetPixelChannelTraits(image,channel);
453         PixelTrait source_traits=GetPixelChannelTraits(source_image,
454           channel);
455         if ((traits == UndefinedPixelTrait) ||
456             (source_traits == UndefinedPixelTrait))
457           continue;
458         if ((traits & CopyPixelTrait) != 0)
459           {
460             /*
461               Copy channel.
462             */
463             q[i]=GetPixelChannel(source_image,channel,p);
464             continue;
465           }
466         if (channel == AlphaPixelChannel)
467           {
468             /*
469               Set alpha channel.
470             */
471             q[i]=clamp != MagickFalse ?
472               ClampPixel(QuantumRange*(Sa+Da-Sa*Da)) :
473               ClampToQuantum(QuantumRange*(Sa+Da-Sa*Da));
474             continue;
475           }
476         /*
477           Sc: source color.
478           Dc: canvas color.
479         */
480         Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
481         Dc=(MagickRealType) q[i];
482         Sca=QuantumScale*Sa*Sc;
483         Dca=QuantumScale*Da*Dc;
484         q[i]=clamp != MagickFalse ?
485           ClampPixel(gamma*QuantumRange*(Sca+Dca*(1.0-Sa))) :
486           ClampToQuantum(gamma*QuantumRange*(Sca+Dca*(1.0-Sa)));
487       }
488       p+=GetPixelChannels(source_image);
489       channels=GetPixelChannels(source_image);
490       if (p >= (pixels+channels*source_image->columns))
491         p=pixels;
492       q+=GetPixelChannels(image);
493     }
494     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
495       status=MagickFalse;
496     if (image->progress_monitor != (MagickProgressMonitor) NULL)
497       {
498         MagickBooleanType
499           proceed;
500
501 #if defined(MAGICKCORE_OPENMP_SUPPORT)
502         #pragma omp critical (MagickCore_CompositeImage)
503 #endif
504         proceed=SetImageProgress(image,CompositeImageTag,progress++,
505           image->rows);
506         if (proceed == MagickFalse)
507           status=MagickFalse;
508       }
509   }
510   source_view=DestroyCacheView(source_view);
511   image_view=DestroyCacheView(image_view);
512   return(status);
513 }
514
515 MagickExport MagickBooleanType CompositeImage(Image *image,
516   const Image *composite,const CompositeOperator compose,
517   const MagickBooleanType clip_to_self,const ssize_t x_offset,
518   const ssize_t y_offset,ExceptionInfo *exception)
519 {
520 #define CompositeImageTag  "Composite/Image"
521
522   CacheView
523     *source_view,
524     *image_view;
525
526   const char
527     *value;
528
529   GeometryInfo
530     geometry_info;
531
532   Image
533     *canvas_image,
534     *source_image;
535
536   MagickBooleanType
537     clamp,
538     status;
539
540   MagickOffsetType
541     progress;
542
543   MagickRealType
544     amount,
545     canvas_dissolve,
546     midpoint,
547     percent_luma,
548     percent_chroma,
549     source_dissolve,
550     threshold;
551
552   MagickStatusType
553     flags;
554
555   ssize_t
556     y;
557
558   assert(image != (Image *) NULL);
559   assert(image->signature == MagickCoreSignature);
560   if (image->debug != MagickFalse)
561     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
562   assert(composite!= (Image *) NULL);
563   assert(composite->signature == MagickCoreSignature);
564   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
565     return(MagickFalse);
566   source_image=CloneImage(composite,0,0,MagickTrue,exception);
567   if (source_image == (const Image *) NULL)
568     return(MagickFalse);
569   if (IsGrayColorspace(image->colorspace) != MagickFalse)
570     (void) SetImageColorspace(image,sRGBColorspace,exception);
571   (void) SetImageColorspace(source_image,image->colorspace,exception);
572   if ((image->alpha_trait != UndefinedPixelTrait) &&
573       (source_image->alpha_trait == UndefinedPixelTrait))
574     (void) SetImageAlphaChannel(source_image,SetAlphaChannel,exception);
575   if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
576     {
577       status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
578         y_offset,exception);
579       source_image=DestroyImage(source_image);
580       return(status);
581     }
582   amount=0.5;
583   canvas_image=(Image *) NULL;
584   canvas_dissolve=1.0;
585   clamp=MagickTrue;
586   value=GetImageArtifact(image,"compose:clamp");
587   if (value != (const char *) NULL)
588     clamp=IsStringTrue(value);
589   SetGeometryInfo(&geometry_info);
590   percent_luma=100.0;
591   percent_chroma=100.0;
592   source_dissolve=1.0;
593   threshold=0.05f;
594   switch (compose)
595   {
596     case CopyCompositeOp:
597     {
598       if ((x_offset < 0) || (y_offset < 0))
599         break;
600       if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
601         break;
602       if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
603         break;
604       status=MagickTrue;
605       source_view=AcquireVirtualCacheView(source_image,exception);
606       image_view=AcquireAuthenticCacheView(image,exception);
607 #if defined(MAGICKCORE_OPENMP_SUPPORT)
608       #pragma omp parallel for schedule(static,4) shared(status) \
609         magick_threads(source_image,image,source_image->rows,1)
610 #endif
611       for (y=0; y < (ssize_t) source_image->rows; y++)
612       {
613         MagickBooleanType
614           sync;
615
616         register const Quantum
617           *p;
618
619         register Quantum
620           *q;
621
622         register ssize_t
623           x;
624
625         if (status == MagickFalse)
626           continue;
627         p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
628           exception);
629         q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
630           source_image->columns,1,exception);
631         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
632           {
633             status=MagickFalse;
634             continue;
635           }
636         for (x=0; x < (ssize_t) source_image->columns; x++)
637         {
638           register ssize_t
639             i;
640
641           if (GetPixelReadMask(source_image,p) == 0)
642             {
643               p+=GetPixelChannels(source_image);
644               q+=GetPixelChannels(image);
645               continue;
646             }
647           for (i=0; i < (ssize_t) GetPixelChannels(source_image); i++)
648           {
649             PixelChannel channel=GetPixelChannelChannel(source_image,i);
650             PixelTrait source_traits=GetPixelChannelTraits(source_image,
651               channel);
652             PixelTrait traits=GetPixelChannelTraits(image,channel);
653             if ((traits == UndefinedPixelTrait) ||
654                 (source_traits == UndefinedPixelTrait))
655               continue;
656             SetPixelChannel(image,channel,p[i],q);
657           }
658           p+=GetPixelChannels(source_image);
659           q+=GetPixelChannels(image);
660         }
661         sync=SyncCacheViewAuthenticPixels(image_view,exception);
662         if (sync == MagickFalse)
663           status=MagickFalse;
664         if (image->progress_monitor != (MagickProgressMonitor) NULL)
665           {
666             MagickBooleanType
667               proceed;
668
669 #if defined(MAGICKCORE_OPENMP_SUPPORT)
670             #pragma omp critical (MagickCore_CompositeImage)
671 #endif
672             proceed=SetImageProgress(image,CompositeImageTag,
673               (MagickOffsetType) y,image->rows);
674             if (proceed == MagickFalse)
675               status=MagickFalse;
676           }
677       }
678       source_view=DestroyCacheView(source_view);
679       image_view=DestroyCacheView(image_view);
680       source_image=DestroyImage(source_image);
681       return(status);
682     }
683     case IntensityCompositeOp:
684     {
685       if ((x_offset < 0) || (y_offset < 0))
686         break;
687       if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
688         break;
689       if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
690         break;
691       status=MagickTrue;
692       source_view=AcquireVirtualCacheView(source_image,exception);
693       image_view=AcquireAuthenticCacheView(image,exception);
694 #if defined(MAGICKCORE_OPENMP_SUPPORT)
695       #pragma omp parallel for schedule(static,4) shared(status) \
696         magick_threads(source_image,image,source_image->rows,1)
697 #endif
698       for (y=0; y < (ssize_t) source_image->rows; y++)
699       {
700         MagickBooleanType
701           sync;
702
703         register const Quantum
704           *p;
705
706         register Quantum
707           *q;
708
709         register ssize_t
710           x;
711
712         if (status == MagickFalse)
713           continue;
714         p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
715           exception);
716         q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
717           source_image->columns,1,exception);
718         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
719           {
720             status=MagickFalse;
721             continue;
722           }
723         for (x=0; x < (ssize_t) source_image->columns; x++)
724         {
725           if (GetPixelReadMask(source_image,p) == 0)
726             {
727               p+=GetPixelChannels(source_image);
728               q+=GetPixelChannels(image);
729               continue;
730             }
731           SetPixelAlpha(image,clamp != MagickFalse ?
732             ClampPixel(GetPixelIntensity(source_image,p)) :
733             ClampToQuantum(GetPixelIntensity(source_image,p)),q);
734           p+=GetPixelChannels(source_image);
735           q+=GetPixelChannels(image);
736         }
737         sync=SyncCacheViewAuthenticPixels(image_view,exception);
738         if (sync == MagickFalse)
739           status=MagickFalse;
740         if (image->progress_monitor != (MagickProgressMonitor) NULL)
741           {
742             MagickBooleanType
743               proceed;
744
745 #if defined(MAGICKCORE_OPENMP_SUPPORT)
746             #pragma omp critical (MagickCore_CompositeImage)
747 #endif
748             proceed=SetImageProgress(image,CompositeImageTag,
749               (MagickOffsetType) y,image->rows);
750             if (proceed == MagickFalse)
751               status=MagickFalse;
752           }
753       }
754       source_view=DestroyCacheView(source_view);
755       image_view=DestroyCacheView(image_view);
756       source_image=DestroyImage(source_image);
757       return(status);
758     }
759     case CopyAlphaCompositeOp:
760     case ChangeMaskCompositeOp:
761     {
762       /*
763         Modify canvas outside the overlaid region and require an alpha
764         channel to exist, to add transparency.
765       */
766       if (image->alpha_trait == UndefinedPixelTrait)
767         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
768       break;
769     }
770     case BlurCompositeOp:
771     {
772       CacheView
773         *canvas_view,
774         *source_view;
775
776       const char
777         *value;
778
779       MagickRealType
780         angle_range,
781         angle_start,
782         height,
783         width;
784
785       PixelInfo
786         pixel;
787
788       ResampleFilter
789         *resample_filter;
790
791       SegmentInfo
792         blur;
793
794       /*
795         Blur Image by resampling.
796
797         Blur Image dictated by an overlay gradient map: X = red_channel;
798           Y = green_channel; compose:args =  x_scale[,y_scale[,angle]].
799       */
800       canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
801         exception);
802       if (canvas_image == (Image *) NULL)
803         {
804           source_image=DestroyImage(source_image);
805           return(MagickFalse);
806         }
807       /*
808         Gather the maximum blur sigma values from user.
809       */
810       flags=NoValue;
811       value=GetImageArtifact(image,"compose:args");
812       if (value != (const char *) NULL)
813         flags=ParseGeometry(value,&geometry_info);
814       if ((flags & WidthValue) == 0)
815         {
816           (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
817             "InvalidSetting","'%s' '%s'","compose:args",value);
818           source_image=DestroyImage(source_image);
819           canvas_image=DestroyImage(canvas_image);
820           return(MagickFalse);
821         }
822       /*
823         Users input sigma now needs to be converted to the EWA ellipse size.
824         The filter defaults to a sigma of 0.5 so to make this match the
825         users input the ellipse size needs to be doubled.
826       */
827       width=height=geometry_info.rho*2.0;
828       if ((flags & HeightValue) != 0 )
829         height=geometry_info.sigma*2.0;
830       /*
831         Default the unrotated ellipse width and height axis vectors.
832       */
833       blur.x1=width;
834       blur.x2=0.0;
835       blur.y1=0.0;
836       blur.y2=height;
837       /* rotate vectors if a rotation angle is given */
838       if ((flags & XValue) != 0 )
839         {
840           MagickRealType
841             angle;
842
843           angle=DegreesToRadians(geometry_info.xi);
844           blur.x1=width*cos(angle);
845           blur.x2=width*sin(angle);
846           blur.y1=(-height*sin(angle));
847           blur.y2=height*cos(angle);
848         }
849       /* Otherwise lets set a angle range and calculate in the loop */
850       angle_start=0.0;
851       angle_range=0.0;
852       if ((flags & YValue) != 0 )
853         {
854           angle_start=DegreesToRadians(geometry_info.xi);
855           angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
856         }
857       /*
858         Set up a gaussian cylindrical filter for EWA Bluring.
859
860         As the minimum ellipse radius of support*1.0 the EWA algorithm
861         can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
862         This means that even 'No Blur' will be still a little blurry!
863
864         The solution (as well as the problem of preventing any user
865         expert filter settings, is to set our own user settings, then
866         restore them afterwards.
867       */
868       resample_filter=AcquireResampleFilter(image,exception);
869       SetResampleFilter(resample_filter,GaussianFilter);
870
871       /* do the variable blurring of each pixel in image */
872       GetPixelInfo(image,&pixel);
873       source_view=AcquireVirtualCacheView(source_image,exception);
874       canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
875       for (y=0; y < (ssize_t) source_image->rows; y++)
876       {
877         MagickBooleanType
878           sync;
879
880         register const Quantum
881           *restrict p;
882
883         register Quantum
884           *restrict q;
885
886         register ssize_t
887           x;
888
889         if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
890           continue;
891         p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
892           exception);
893         q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
894           exception);
895         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
896           break;
897         for (x=0; x < (ssize_t) source_image->columns; x++)
898         {
899           if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
900             {
901               p+=GetPixelChannels(source_image);
902               continue;
903             }
904           if (fabs((double) angle_range) > MagickEpsilon)
905             {
906               MagickRealType
907                 angle;
908
909               angle=angle_start+angle_range*QuantumScale*
910                 GetPixelBlue(source_image,p);
911               blur.x1=width*cos(angle);
912               blur.x2=width*sin(angle);
913               blur.y1=(-height*sin(angle));
914               blur.y2=height*cos(angle);
915             }
916 #if 0
917           if ( x == 10 && y == 60 ) {
918             (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
919               blur.x2,blur.y1, blur.y2);
920             (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
921               GetPixelRed(p),QuantumScale*GetPixelGreen(p));
922 #endif
923           ScaleResampleFilter(resample_filter,
924             blur.x1*QuantumScale*GetPixelRed(source_image,p),
925             blur.y1*QuantumScale*GetPixelGreen(source_image,p),
926             blur.x2*QuantumScale*GetPixelRed(source_image,p),
927             blur.y2*QuantumScale*GetPixelGreen(source_image,p) );
928           (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
929             (double) y_offset+y,&pixel,exception);
930           SetPixelViaPixelInfo(canvas_image,&pixel,q);
931           p+=GetPixelChannels(source_image);
932           q+=GetPixelChannels(canvas_image);
933         }
934         sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
935         if (sync == MagickFalse)
936           break;
937       }
938       resample_filter=DestroyResampleFilter(resample_filter);
939       source_view=DestroyCacheView(source_view);
940       canvas_view=DestroyCacheView(canvas_view);
941       source_image=DestroyImage(source_image);
942       source_image=canvas_image;
943       break;
944     }
945     case DisplaceCompositeOp:
946     case DistortCompositeOp:
947     {
948       CacheView
949         *source_view,
950         *canvas_view,
951         *image_view;
952
953       const char
954         *value;
955
956       MagickRealType
957         horizontal_scale,
958         vertical_scale;
959
960       PixelInfo
961         pixel;
962
963       PointInfo
964         center,
965         offset;
966
967       /*
968         Displace/Distort based on overlay gradient map:
969           X = red_channel;  Y = green_channel;
970           compose:args = x_scale[,y_scale[,center.x,center.y]]
971       */
972       canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
973         exception);
974       if (canvas_image == (Image *) NULL)
975         {
976           source_image=DestroyImage(source_image);
977           return(MagickFalse);
978         }
979       SetGeometryInfo(&geometry_info);
980       flags=NoValue;
981       value=GetImageArtifact(image,"compose:args");
982       if (value != (char *) NULL)
983         flags=ParseGeometry(value,&geometry_info);
984       if ((flags & (WidthValue | HeightValue)) == 0 )
985         {
986           if ((flags & AspectValue) == 0)
987             {
988               horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
989               vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
990             }
991           else
992             {
993               horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
994               vertical_scale=(MagickRealType) (image->rows-1)/2.0;
995             }
996         }
997       else
998         {
999           horizontal_scale=geometry_info.rho;
1000           vertical_scale=geometry_info.sigma;
1001           if ((flags & PercentValue) != 0)
1002             {
1003               if ((flags & AspectValue) == 0)
1004                 {
1005                   horizontal_scale*=(source_image->columns-1)/200.0;
1006                   vertical_scale*=(source_image->rows-1)/200.0;
1007                 }
1008               else
1009                 {
1010                   horizontal_scale*=(image->columns-1)/200.0;
1011                   vertical_scale*=(image->rows-1)/200.0;
1012                 }
1013             }
1014           if ((flags & HeightValue) == 0)
1015             vertical_scale=horizontal_scale;
1016         }
1017       /*
1018         Determine fixed center point for absolute distortion map
1019          Absolute distort ==
1020            Displace offset relative to a fixed absolute point
1021            Select that point according to +X+Y user inputs.
1022            default = center of overlay image
1023            arg flag '!' = locations/percentage relative to background image
1024       */
1025       center.x=(MagickRealType) x_offset;
1026       center.y=(MagickRealType) y_offset;
1027       if (compose == DistortCompositeOp)
1028         {
1029           if ((flags & XValue) == 0)
1030             if ((flags & AspectValue) != 0)
1031               center.x=(MagickRealType) ((image->columns-1)/2.0);
1032             else
1033               center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1034                 2.0);
1035           else
1036             if ((flags & AspectValue) != 0)
1037               center.x=geometry_info.xi;
1038             else
1039               center.x=(MagickRealType) (x_offset+geometry_info.xi);
1040           if ((flags & YValue) == 0)
1041             if ((flags & AspectValue) != 0)
1042               center.y=(MagickRealType) ((image->rows-1)/2.0);
1043             else
1044               center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1045           else
1046             if ((flags & AspectValue) != 0)
1047               center.y=geometry_info.psi;
1048             else
1049               center.y=(MagickRealType) (y_offset+geometry_info.psi);
1050         }
1051       /*
1052         Shift the pixel offset point as defined by the provided,
1053         displacement/distortion map.  -- Like a lens...
1054       */
1055       GetPixelInfo(image,&pixel);
1056       image_view=AcquireVirtualCacheView(image,exception);
1057       source_view=AcquireVirtualCacheView(source_image,exception);
1058       canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1059       for (y=0; y < (ssize_t) source_image->rows; y++)
1060       {
1061         MagickBooleanType
1062           sync;
1063
1064         register const Quantum
1065           *restrict p;
1066
1067         register Quantum
1068           *restrict q;
1069
1070         register ssize_t
1071           x;
1072
1073         if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1074           continue;
1075         p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1076           exception);
1077         q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1078           exception);
1079         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1080           break;
1081         for (x=0; x < (ssize_t) source_image->columns; x++)
1082         {
1083           if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1084             {
1085               p+=GetPixelChannels(source_image);
1086               continue;
1087             }
1088           /*
1089             Displace the offset.
1090           */
1091           offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1092             (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1093             QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1094             x : 0);
1095           offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1096             (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1097             QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1098             y : 0);
1099           (void) InterpolatePixelInfo(image,image_view,
1100             UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1101             &pixel,exception);
1102           /*
1103             Mask with the 'invalid pixel mask' in alpha channel.
1104           */
1105           pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
1106             (QuantumScale*GetPixelAlpha(source_image,p));
1107           SetPixelViaPixelInfo(canvas_image,&pixel,q);
1108           p+=GetPixelChannels(source_image);
1109           q+=GetPixelChannels(canvas_image);
1110         }
1111         sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1112         if (sync == MagickFalse)
1113           break;
1114       }
1115       canvas_view=DestroyCacheView(canvas_view);
1116       source_view=DestroyCacheView(source_view);
1117       image_view=DestroyCacheView(image_view);
1118       source_image=DestroyImage(source_image);
1119       source_image=canvas_image;
1120       break;
1121     }
1122     case DissolveCompositeOp:
1123     {
1124       const char
1125         *value;
1126
1127       /*
1128         Geometry arguments to dissolve factors.
1129       */
1130       value=GetImageArtifact(image,"compose:args");
1131       if (value != (char *) NULL)
1132         {
1133           flags=ParseGeometry(value,&geometry_info);
1134           source_dissolve=geometry_info.rho/100.0;
1135           canvas_dissolve=1.0;
1136           if ((source_dissolve-MagickEpsilon) < 0.0)
1137             source_dissolve=0.0;
1138           if ((source_dissolve+MagickEpsilon) > 1.0)
1139             {
1140               canvas_dissolve=2.0-source_dissolve;
1141               source_dissolve=1.0;
1142             }
1143           if ((flags & SigmaValue) != 0)
1144             canvas_dissolve=geometry_info.sigma/100.0;
1145           if ((canvas_dissolve-MagickEpsilon) < 0.0)
1146             canvas_dissolve=0.0;
1147         }
1148       break;
1149     }
1150     case BlendCompositeOp:
1151     {
1152       const char
1153         *value;
1154
1155       value=GetImageArtifact(image,"compose:args");
1156       if (value != (char *) NULL)
1157         {
1158           flags=ParseGeometry(value,&geometry_info);
1159           source_dissolve=geometry_info.rho/100.0;
1160           canvas_dissolve=1.0-source_dissolve;
1161           if ((flags & SigmaValue) != 0)
1162             canvas_dissolve=geometry_info.sigma/100.0;
1163         }
1164       break;
1165     }
1166     case MathematicsCompositeOp:
1167     {
1168       const char
1169         *value;
1170
1171       /*
1172         Just collect the values from "compose:args", setting.
1173         Unused values are set to zero automagically.
1174
1175         Arguments are normally a comma separated list, so this probably should
1176         be changed to some 'general comma list' parser, (with a minimum
1177         number of values)
1178       */
1179       SetGeometryInfo(&geometry_info);
1180       value=GetImageArtifact(image,"compose:args");
1181       if (value != (char *) NULL)
1182         (void) ParseGeometry(value,&geometry_info);
1183       break;
1184     }
1185     case ModulateCompositeOp:
1186     {
1187       const char
1188         *value;
1189
1190       /*
1191         Determine the luma and chroma scale.
1192       */
1193       value=GetImageArtifact(image,"compose:args");
1194       if (value != (char *) NULL)
1195         {
1196           flags=ParseGeometry(value,&geometry_info);
1197           percent_luma=geometry_info.rho;
1198           if ((flags & SigmaValue) != 0)
1199             percent_chroma=geometry_info.sigma;
1200         }
1201       break;
1202     }
1203     case ThresholdCompositeOp:
1204     {
1205       const char
1206         *value;
1207
1208       /*
1209         Determine the amount and threshold.
1210       */
1211       value=GetImageArtifact(image,"compose:args");
1212       if (value != (char *) NULL)
1213         {
1214           flags=ParseGeometry(value,&geometry_info);
1215           amount=geometry_info.rho;
1216           threshold=geometry_info.sigma;
1217           if ((flags & SigmaValue) == 0)
1218             threshold=0.05f;
1219         }
1220       threshold*=QuantumRange;
1221       break;
1222     }
1223     default:
1224       break;
1225   }
1226   /*
1227     Composite image.
1228   */
1229   status=MagickTrue;
1230   progress=0;
1231   midpoint=((MagickRealType) QuantumRange+1.0)/2;
1232   source_view=AcquireVirtualCacheView(source_image,exception);
1233   image_view=AcquireAuthenticCacheView(image,exception);
1234 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1235   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1236     magick_threads(source_image,image,image->rows,1)
1237 #endif
1238   for (y=0; y < (ssize_t) image->rows; y++)
1239   {
1240     const Quantum
1241       *pixels;
1242
1243     MagickRealType
1244       blue,
1245       chroma,
1246       green,
1247       hue,
1248       luma,
1249       red;
1250
1251     PixelInfo
1252       canvas_pixel,
1253       source_pixel;
1254
1255     register const Quantum
1256       *restrict p;
1257
1258     register Quantum
1259       *restrict q;
1260
1261     register ssize_t
1262       x;
1263
1264     if (status == MagickFalse)
1265       continue;
1266     if (clip_to_self != MagickFalse)
1267       {
1268         if (y < y_offset)
1269           continue;
1270         if ((y-y_offset) >= (ssize_t) source_image->rows)
1271           continue;
1272       }
1273     /*
1274       If pixels is NULL, y is outside overlay region.
1275     */
1276     pixels=(Quantum *) NULL;
1277     p=(Quantum *) NULL;
1278     if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1279       {
1280         p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1281           source_image->columns,1,exception);
1282         if (p == (const Quantum *) NULL)
1283           {
1284             status=MagickFalse;
1285             continue;
1286           }
1287         pixels=p;
1288         if (x_offset < 0)
1289           p-=x_offset*GetPixelChannels(source_image);
1290       }
1291     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1292     if (q == (Quantum *) NULL)
1293       {
1294         status=MagickFalse;
1295         continue;
1296       }
1297     hue=0.0;
1298     chroma=0.0;
1299     luma=0.0;
1300     GetPixelInfo(image,&canvas_pixel);
1301     GetPixelInfo(source_image,&source_pixel);
1302     for (x=0; x < (ssize_t) image->columns; x++)
1303     {
1304       double
1305         gamma;
1306
1307       MagickRealType
1308         alpha,
1309         Da,
1310         Dc,
1311         Dca,
1312         Sa,
1313         Sc,
1314         Sca;
1315
1316       register ssize_t
1317         i;
1318
1319       size_t
1320         channels;
1321
1322       if (clip_to_self != MagickFalse)
1323         {
1324           if (x < x_offset)
1325             {
1326               q+=GetPixelChannels(image);
1327               continue;
1328             }
1329           if ((x-x_offset) >= (ssize_t) source_image->columns)
1330             break;
1331         }
1332       if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1333           ((x-x_offset) >= (ssize_t) source_image->columns))
1334         {
1335           Quantum
1336             source[MaxPixelChannels];
1337
1338           /*
1339             Virtual composite:
1340               Sc: source color.
1341               Dc: canvas color.
1342           */
1343           (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
1344             exception);
1345           if (GetPixelReadMask(image,q) == 0)
1346             {
1347               q+=GetPixelChannels(image);
1348               continue;
1349             }
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 ModulusAddCompositeOp:
1443         case ModulusSubtractCompositeOp:
1444         case MultiplyCompositeOp:
1445         case OverlayCompositeOp:
1446         case PegtopLightCompositeOp:
1447         case PinLightCompositeOp:
1448         case ScreenCompositeOp:
1449         case SoftLightCompositeOp:
1450         case VividLightCompositeOp:
1451         {
1452           alpha=RoundToUnity(Sa+Da-Sa*Da);
1453           break;
1454         }
1455         case DstAtopCompositeOp:
1456         case DstInCompositeOp:
1457         case InCompositeOp:
1458         case SrcInCompositeOp:
1459         {
1460           alpha=Sa*Da;
1461           break;
1462         }
1463         case DissolveCompositeOp:
1464         {
1465           alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
1466             canvas_dissolve*Da;
1467           break;
1468         }
1469         case DstOverCompositeOp:
1470         case OverCompositeOp:
1471         case SrcOverCompositeOp:
1472         {
1473           alpha=Sa+Da-Sa*Da;
1474           break;
1475         }
1476         case DstOutCompositeOp:
1477         {
1478           alpha=Da*(1.0-Sa);
1479           break;
1480         }
1481         case OutCompositeOp:
1482         case SrcOutCompositeOp:
1483         {
1484           alpha=Sa*(1.0-Da);
1485           break;
1486         }
1487         case BlendCompositeOp:
1488         case PlusCompositeOp:
1489         {
1490           alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
1491           break;
1492         }
1493         case XorCompositeOp:
1494         {
1495           alpha=Sa+Da-2.0*Sa*Da;
1496           break;
1497         }
1498         default:
1499         {
1500           alpha=1.0;
1501           break;
1502         }
1503       }
1504       if (GetPixelReadMask(image,q) == 0)
1505         {
1506           p+=GetPixelChannels(source_image);
1507           q+=GetPixelChannels(image);
1508           continue;
1509         }
1510       switch (compose)
1511       {
1512         case ColorizeCompositeOp:
1513         case HueCompositeOp:
1514         case LuminizeCompositeOp:
1515         case ModulateCompositeOp:
1516         case SaturateCompositeOp:
1517         {
1518           GetPixelInfoPixel(source_image,p,&source_pixel);
1519           GetPixelInfoPixel(image,q,&canvas_pixel);
1520           break;
1521         }
1522         default:
1523           break;
1524       }
1525       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1526       {
1527         MagickRealType
1528           pixel,
1529           sans;
1530
1531         PixelChannel channel=GetPixelChannelChannel(image,i);
1532         PixelTrait traits=GetPixelChannelTraits(image,channel);
1533         PixelTrait source_traits=GetPixelChannelTraits(source_image,channel);
1534         if (traits == UndefinedPixelTrait)
1535           continue;
1536         if ((source_traits == UndefinedPixelTrait) &&
1537              (((compose != CopyAlphaCompositeOp) &&
1538                (compose != ChangeMaskCompositeOp)) ||
1539               (channel != AlphaPixelChannel)))
1540             continue;
1541         /*
1542           Sc: source color.
1543           Dc: canvas color.
1544         */
1545         Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1546         Dc=(MagickRealType) q[i];
1547         if ((traits & CopyPixelTrait) != 0)
1548           {
1549             /*
1550               Copy channel.
1551             */
1552             q[i]=Sc;
1553             continue;
1554           }
1555         if (channel == AlphaPixelChannel)
1556           {
1557             /*
1558               Set alpha channel.
1559             */
1560             switch (compose)
1561             {
1562               case AlphaCompositeOp:
1563               {
1564                 pixel=QuantumRange*Sa;
1565                 break;
1566               }
1567               case AtopCompositeOp:
1568               case CopyBlackCompositeOp:
1569               case CopyBlueCompositeOp:
1570               case CopyCyanCompositeOp:
1571               case CopyGreenCompositeOp:
1572               case CopyMagentaCompositeOp:
1573               case CopyRedCompositeOp:
1574               case CopyYellowCompositeOp:
1575               case SrcAtopCompositeOp:
1576               case DstCompositeOp:
1577               case NoCompositeOp:
1578               {
1579                 pixel=QuantumRange*Da;
1580                 break;
1581               }
1582               case ChangeMaskCompositeOp:
1583               {
1584                 MagickBooleanType
1585                   equivalent;
1586
1587                 if (Da > ((MagickRealType) QuantumRange/2.0))
1588                   {
1589                     pixel=(MagickRealType) TransparentAlpha;
1590                     break;
1591                   }
1592                 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1593                 if (equivalent != MagickFalse)
1594                   pixel=(MagickRealType) TransparentAlpha;
1595                 else
1596                   pixel=(MagickRealType) OpaqueAlpha;
1597                 break;
1598               }
1599               case ClearCompositeOp:
1600               {
1601                 pixel=(MagickRealType) TransparentAlpha;
1602                 break;
1603               }
1604               case ColorizeCompositeOp:
1605               case HueCompositeOp:
1606               case LuminizeCompositeOp:
1607               case SaturateCompositeOp:
1608               {
1609                 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1610                   {
1611                     pixel=QuantumRange*Da;
1612                     break;
1613                   }
1614                 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1615                   {
1616                     pixel=QuantumRange*Sa;
1617                     break;
1618                   }
1619                 if (Sa < Da)
1620                   {
1621                     pixel=QuantumRange*Da;
1622                     break;
1623                   }
1624                 pixel=QuantumRange*Sa;
1625                 break;
1626               }
1627               case CopyAlphaCompositeOp:
1628               {
1629                 if ((source_traits & BlendPixelTrait) == 0)
1630                   pixel=GetPixelIntensity(source_image,p);
1631                 else
1632                   pixel=QuantumRange*Sa;
1633                 break;
1634               }
1635               case CopyCompositeOp:
1636               case DisplaceCompositeOp:
1637               case DistortCompositeOp:
1638               case DstAtopCompositeOp:
1639               case ReplaceCompositeOp:
1640               case SrcCompositeOp:
1641               {
1642                 pixel=QuantumRange*Sa;
1643                 break;
1644               }
1645               case DarkenIntensityCompositeOp:
1646               {
1647                 pixel=Sa*GetPixelIntensity(source_image,p) <
1648                   Da*GetPixelIntensity(image,q) ? Sa : Da;
1649                 break;
1650               }
1651               case LightenIntensityCompositeOp:
1652               {
1653                 pixel=Sa*GetPixelIntensity(source_image,p) >
1654                   Da*GetPixelIntensity(image,q) ? Sa : Da;
1655                 break;
1656               }
1657               case ModulateCompositeOp:
1658               {
1659                 pixel=QuantumRange*Da;
1660                 break;
1661               }
1662               default:
1663               {
1664                 pixel=QuantumRange*alpha;
1665                 break;
1666               }
1667             }
1668             q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1669               ClampToQuantum(pixel);
1670             continue;
1671           }
1672         /*
1673           Porter-Duff compositions:
1674             Sca: source normalized color multiplied by alpha.
1675             Dca: normalized canvas color multiplied by alpha.
1676         */
1677         Sca=QuantumScale*Sa*Sc;
1678         Dca=QuantumScale*Da*Dc;
1679         switch (compose)
1680         {
1681           case DarkenCompositeOp:
1682           case LightenCompositeOp:
1683           case ModulusSubtractCompositeOp:
1684           {
1685             gamma=PerceptibleReciprocal(1.0-alpha);
1686             break;
1687           }
1688           default:
1689           {
1690             gamma=PerceptibleReciprocal(alpha);
1691             break;
1692           }
1693         }
1694         pixel=Dc;
1695         switch (compose)
1696         {
1697           case AlphaCompositeOp:
1698           {
1699             pixel=QuantumRange*Sa;
1700             break;
1701           }
1702           case AtopCompositeOp:
1703           case SrcAtopCompositeOp:
1704           {
1705             pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1706             break;
1707           }
1708           case BlendCompositeOp:
1709           {
1710             pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1711             break;
1712           }
1713           case BlurCompositeOp:
1714           case CopyCompositeOp:
1715           case ReplaceCompositeOp:
1716           case SrcCompositeOp:
1717           {
1718             pixel=QuantumRange*Sca;
1719             break;
1720           }
1721           case DisplaceCompositeOp:
1722           case DistortCompositeOp:
1723           {
1724             pixel=Sc;
1725             break;
1726           }
1727           case BumpmapCompositeOp:
1728           {
1729             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1730               {
1731                 pixel=Dc;
1732                 break;
1733               }
1734             pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1735             break;
1736           }
1737           case ChangeMaskCompositeOp:
1738           {
1739             pixel=Dc;
1740             break;
1741           }
1742           case ClearCompositeOp:
1743           {
1744             pixel=0.0;
1745             break;
1746           }
1747           case ColorBurnCompositeOp:
1748           {
1749             if ((Sca == 0.0) && (Dca == Da))
1750               {
1751                 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1752                 break;
1753               }
1754             if (Sca == 0.0)
1755               {
1756                 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1757                 break;
1758               }
1759             pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-Dca/Da)*Sa/
1760               Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
1761             break;
1762           }
1763           case ColorDodgeCompositeOp:
1764           {
1765             if ((Sca == Sa) && (Dca == 0.0))
1766               {
1767                 pixel=QuantumRange*gamma*(Sca*(1.0-Da));
1768                 break;
1769               }
1770             if (Sca == Sa)
1771               {
1772                 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1773                 break;
1774               }
1775             pixel=QuantumRange*gamma*(Sa*Da*MagickMin(1.0,Dca/Da*Sa/(Sa-Sca)));
1776             break;
1777           }
1778           case ColorizeCompositeOp:
1779           {
1780             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1781               {
1782                 pixel=Dc;
1783                 break;
1784               }
1785             if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1786               {
1787                 pixel=Sc;
1788                 break;
1789               }
1790             CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1791               &sans,&sans,&luma);
1792             CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1793               &hue,&chroma,&sans);
1794             HCLComposite(hue,chroma,luma,&red,&green,&blue);
1795             switch (channel)
1796             {
1797               case RedPixelChannel: pixel=red; break;
1798               case GreenPixelChannel: pixel=green; break;
1799               case BluePixelChannel: pixel=blue; break;
1800               default: pixel=Dc; break;
1801             }
1802             break;
1803           }
1804           case CopyAlphaCompositeOp:
1805           {
1806             pixel=Dc;
1807             break;
1808           }
1809           case CopyBlackCompositeOp:
1810           {
1811             if (channel == BlackPixelChannel)
1812               pixel=(MagickRealType) (QuantumRange-
1813                 GetPixelBlack(source_image,p));
1814             break;
1815           }
1816           case CopyBlueCompositeOp:
1817           case CopyYellowCompositeOp:
1818           {
1819             if (channel == BluePixelChannel)
1820               pixel=(MagickRealType) GetPixelBlue(source_image,p);
1821             break;
1822           }
1823           case CopyGreenCompositeOp:
1824           case CopyMagentaCompositeOp:
1825           {
1826             if (channel == GreenPixelChannel)
1827               pixel=(MagickRealType) GetPixelGreen(source_image,p);
1828             break;
1829           }
1830           case CopyRedCompositeOp:
1831           case CopyCyanCompositeOp:
1832           {
1833             if (channel == RedPixelChannel)
1834               pixel=(MagickRealType) GetPixelRed(source_image,p);
1835             break;
1836           }
1837           case DarkenCompositeOp:
1838           {
1839             /*
1840               Darken is equivalent to a 'Minimum' method
1841                 OR a greyscale version of a binary 'Or'
1842                 OR the 'Intersection' of pixel sets.
1843             */
1844             if ((Sca*Da) < (Dca*Sa))
1845               {
1846                 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1847                 break;
1848               }
1849             pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1850             break;
1851           }
1852           case DarkenIntensityCompositeOp:
1853           {
1854             pixel=Sa*GetPixelIntensity(source_image,p) <
1855               Da*GetPixelIntensity(image,q) ? Sc : Dc;
1856             break;
1857           }
1858           case DifferenceCompositeOp:
1859           {
1860             pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1861             break;
1862           }
1863           case DissolveCompositeOp:
1864           {
1865             pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1866               canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1867             break;
1868           }
1869           case DivideDstCompositeOp:
1870           {
1871             if ((fabs((double) Sca) < MagickEpsilon) &&
1872                 (fabs((double) Dca) < MagickEpsilon))
1873               {
1874                 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1875                 break;
1876               }
1877             if (fabs((double) Dca) < MagickEpsilon)
1878               {
1879                 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1880                 break;
1881               }
1882             pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1883             break;
1884           }
1885           case DivideSrcCompositeOp:
1886           {
1887             if ((fabs((double) Dca) < MagickEpsilon) &&
1888                 (fabs((double) Sca) < MagickEpsilon))
1889               {
1890                 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1891                 break;
1892               }
1893             if (fabs((double) Sca) < MagickEpsilon)
1894               {
1895                 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1896                 break;
1897               }
1898             pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1899             break;
1900           }
1901           case DstAtopCompositeOp:
1902           {
1903             pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1904             break;
1905           }
1906           case DstCompositeOp:
1907           case NoCompositeOp:
1908           {
1909             pixel=QuantumRange*Dca;
1910             break;
1911           }
1912           case DstInCompositeOp:
1913           {
1914             pixel=QuantumRange*(Dca*Sa);
1915             break;
1916           }
1917           case DstOutCompositeOp:
1918           {
1919             pixel=QuantumRange*(Dca*(1.0-Sa));
1920             break;
1921           }
1922           case DstOverCompositeOp:
1923           {
1924             pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da));
1925             break;
1926           }
1927           case ExclusionCompositeOp:
1928           {
1929             pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1930               Dca*(1.0-Sa));
1931             break;
1932           }
1933           case HardLightCompositeOp:
1934           {
1935             if ((2.0*Sca) < Sa)
1936               {
1937                 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-
1938                   Sa));
1939                 break;
1940               }
1941             pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1942               Dca*(1.0-Sa));
1943             break;
1944           }
1945           case HardMixCompositeOp:
1946           {
1947             pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange);
1948             break;
1949           }
1950           case HueCompositeOp:
1951           {
1952             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1953               {
1954                 pixel=Dc;
1955                 break;
1956               }
1957             if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1958               {
1959                 pixel=Sc;
1960                 break;
1961               }
1962             CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1963               &hue,&chroma,&luma);
1964             CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1965               &hue,&sans,&sans);
1966             HCLComposite(hue,chroma,luma,&red,&green,&blue);
1967             switch (channel)
1968             {
1969               case RedPixelChannel: pixel=red; break;
1970               case GreenPixelChannel: pixel=green; break;
1971               case BluePixelChannel: pixel=blue; break;
1972               default: pixel=Dc; break;
1973             }
1974             break;
1975           }
1976           case InCompositeOp:
1977           case SrcInCompositeOp:
1978           {
1979             pixel=QuantumRange*(Sca*Da);
1980             break;
1981           }
1982           case LinearBurnCompositeOp:
1983           {
1984             /*
1985               LinearBurn: as defined by Abode Photoshop, according to
1986               http://www.simplefilter.de/en/basics/mixmods.html is:
1987
1988                 f(Sc,Dc) = Sc + Dc - 1
1989             */
1990             pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1991             break;
1992           }
1993           case LinearDodgeCompositeOp:
1994           {
1995             pixel=gamma*(Sa*Sc+Da*Dc);
1996             break;
1997           }
1998           case LinearLightCompositeOp:
1999           {
2000             /*
2001               LinearLight: as defined by Abode Photoshop, according to
2002               http://www.simplefilter.de/en/basics/mixmods.html is:
2003
2004                 f(Sc,Dc) = Dc + 2*Sc - 1
2005             */
2006             pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
2007             break;
2008           }
2009           case LightenCompositeOp:
2010           {
2011             if ((Sca*Da) > (Dca*Sa))
2012               {
2013                 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2014                 break;
2015               }
2016             pixel=QuantumRange*(Dca+Sca*(1.0-Da));
2017             break;
2018           }
2019           case LightenIntensityCompositeOp:
2020           {
2021             /*
2022               Lighten is equivalent to a 'Maximum' method
2023                 OR a greyscale version of a binary 'And'
2024                 OR the 'Union' of pixel sets.
2025             */
2026             pixel=Sa*GetPixelIntensity(source_image,p) >
2027               Da*GetPixelIntensity(image,q) ? Sc : Dc;
2028             break;
2029           }
2030           case LuminizeCompositeOp:
2031           {
2032             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2033               {
2034                 pixel=Dc;
2035                 break;
2036               }
2037             if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2038               {
2039                 pixel=Sc;
2040                 break;
2041               }
2042             CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2043               &hue,&chroma,&luma);
2044             CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2045               &sans,&sans,&luma);
2046             HCLComposite(hue,chroma,luma,&red,&green,&blue);
2047             switch (channel)
2048             {
2049               case RedPixelChannel: pixel=red; break;
2050               case GreenPixelChannel: pixel=green; break;
2051               case BluePixelChannel: pixel=blue; break;
2052               default: pixel=Dc; break;
2053             }
2054             break;
2055           }
2056           case MathematicsCompositeOp:
2057           {
2058             /*
2059               'Mathematics' a free form user control mathematical composition
2060               is defined as...
2061
2062                 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2063
2064               Where the arguments A,B,C,D are (currently) passed to composite
2065               as a command separated 'geometry' string in "compose:args" image
2066               artifact.
2067
2068                  A = a->rho,   B = a->sigma,  C = a->xi,  D = a->psi
2069
2070               Applying the SVG transparency formula (see above), we get...
2071
2072                Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2073
2074                Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2075                  Dca*(1.0-Sa)
2076             */
2077             pixel=gamma*(geometry_info.rho*Sca*Dca+
2078               geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2079               geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2080             break;
2081           }
2082           case MinusDstCompositeOp:
2083           {
2084             pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2085             break;
2086           }
2087           case MinusSrcCompositeOp:
2088           {
2089             /*
2090               Minus source from canvas.
2091
2092                 f(Sc,Dc) = Sc - Dc
2093             */
2094             pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2095             break;
2096           }
2097           case ModulateCompositeOp:
2098           {
2099             ssize_t
2100               offset;
2101
2102             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2103               {
2104                 pixel=Dc;
2105                 break;
2106               }
2107             offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2108             if (offset == 0)
2109               {
2110                 pixel=Dc;
2111                 break;
2112               }
2113             CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2114               &hue,&chroma,&luma);
2115             luma+=(0.01*percent_luma*offset)/midpoint;
2116             chroma*=0.01*percent_chroma;
2117             HCLComposite(hue,chroma,luma,&red,&green,&blue);
2118             switch (channel)
2119             {
2120               case RedPixelChannel: pixel=red; break;
2121               case GreenPixelChannel: pixel=green; break;
2122               case BluePixelChannel: pixel=blue; break;
2123               default: pixel=Dc; break;
2124             }
2125             break;
2126           }
2127           case ModulusAddCompositeOp:
2128           {
2129             pixel=Sc+Dc;
2130             if (pixel > QuantumRange)
2131               pixel-=QuantumRange;
2132             pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2133             break;
2134           }
2135           case ModulusSubtractCompositeOp:
2136           {
2137             pixel=Sc-Dc;
2138             if (pixel < 0.0)
2139               pixel+=QuantumRange;
2140             pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2141             break;
2142           }
2143           case MultiplyCompositeOp:
2144           {
2145             pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2146             break;
2147           }
2148           case OutCompositeOp:
2149           case SrcOutCompositeOp:
2150           {
2151             pixel=QuantumRange*(Sca*(1.0-Da));
2152             break;
2153           }
2154           case OverCompositeOp:
2155           case SrcOverCompositeOp:
2156           {
2157             pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
2158             break;
2159           }
2160           case OverlayCompositeOp:
2161           {
2162             if ((2.0*Dca) <= Da)
2163               {
2164                 pixel=QuantumRange*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2165                 break;
2166               }
2167             pixel=QuantumRange*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*
2168               (1.0-Sa));
2169             break;
2170           }
2171           case PegtopLightCompositeOp:
2172           {
2173             /*
2174               PegTop: A Soft-Light alternative: A continuous version of the
2175               Softlight function, producing very similar results.
2176
2177                 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2178
2179               http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2180             */
2181             if (fabs((double) Da) < MagickEpsilon)
2182               {
2183                 pixel=QuantumRange*gamma*(Sca);
2184                 break;
2185               }
2186             pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2187               Da)+Dca*(1.0-Sa));
2188             break;
2189           }
2190           case PinLightCompositeOp:
2191           {
2192             /*
2193               PinLight: A Photoshop 7 composition method
2194               http://www.simplefilter.de/en/basics/mixmods.html
2195
2196                 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc   ? 2*Sc : Dc
2197             */
2198             if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2199               {
2200                 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2201                 break;
2202               }
2203             if ((Dca*Sa) > (2.0*Sca*Da))
2204               {
2205                 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2206                 break;
2207               }
2208             pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2209             break;
2210           }
2211           case PlusCompositeOp:
2212           {
2213             pixel=QuantumRange*(Sca+Dca);
2214             break;
2215           }
2216           case SaturateCompositeOp:
2217           {
2218             if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2219               {
2220                 pixel=Dc;
2221                 break;
2222               }
2223             if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2224               {
2225                 pixel=Sc;
2226                 break;
2227               }
2228             CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2229               &hue,&chroma,&luma);
2230             CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2231               &sans,&chroma,&sans);
2232             HCLComposite(hue,chroma,luma,&red,&green,&blue);
2233             switch (channel)
2234             {
2235               case RedPixelChannel: pixel=red; break;
2236               case GreenPixelChannel: pixel=green; break;
2237               case BluePixelChannel: pixel=blue; break;
2238               default: pixel=Dc; break;
2239             }
2240             break;
2241           }
2242           case ScreenCompositeOp:
2243           {
2244             /*
2245               Screen:  a negated multiply:
2246
2247                 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2248             */
2249             pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2250             break;
2251           }
2252           case SoftLightCompositeOp:
2253           {
2254             if ((2.0*Sca) < Sa)
2255               {
2256                 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2257                   Sca*(1.0-Da)+Dca*(1.0-Sa));
2258                 break;
2259               }
2260             if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2261               {
2262                 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2263                   (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2264                   Dca*(1.0-Sa));
2265                 break;
2266               }
2267             pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2268               (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2269             break;
2270           }
2271           case ThresholdCompositeOp:
2272           {
2273             MagickRealType
2274               delta;
2275
2276             delta=Sc-Dc;
2277             if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2278               {
2279                 pixel=gamma*Dc;
2280                 break;
2281               }
2282             pixel=gamma*(Dc+delta*amount);
2283             break;
2284           }
2285           case VividLightCompositeOp:
2286           {
2287             /*
2288               VividLight: A Photoshop 7 composition method.  See
2289               http://www.simplefilter.de/en/basics/mixmods.html.
2290
2291                 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2292             */
2293             if ((fabs((double) Sa) < MagickEpsilon) ||
2294                 (fabs((double) (Sca-Sa)) < MagickEpsilon))
2295               {
2296                 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2297                 break;
2298               }
2299             if ((2.0*Sca) <= Sa)
2300               {
2301                 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2302                   (1.0-Da)+Dca*(1.0-Sa));
2303                 break;
2304               }
2305             pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*
2306               (1.0-Sa));
2307             break;
2308           }
2309           case XorCompositeOp:
2310           {
2311             pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2312             break;
2313           }
2314           default:
2315           {
2316             pixel=Sc;
2317             break;
2318           }
2319         }
2320         q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
2321       }
2322       p+=GetPixelChannels(source_image);
2323       channels=GetPixelChannels(source_image);
2324       if (p >= (pixels+channels*source_image->columns))
2325         p=pixels;
2326       q+=GetPixelChannels(image);
2327     }
2328     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2329       status=MagickFalse;
2330     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2331       {
2332         MagickBooleanType
2333           proceed;
2334
2335 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2336         #pragma omp critical (MagickCore_CompositeImage)
2337 #endif
2338         proceed=SetImageProgress(image,CompositeImageTag,progress++,
2339           image->rows);
2340         if (proceed == MagickFalse)
2341           status=MagickFalse;
2342       }
2343   }
2344   source_view=DestroyCacheView(source_view);
2345   image_view=DestroyCacheView(image_view);
2346   if (canvas_image != (Image * ) NULL)
2347     canvas_image=DestroyImage(canvas_image);
2348   else
2349     source_image=DestroyImage(source_image);
2350   return(status);
2351 }
2352 \f
2353 /*
2354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2355 %                                                                             %
2356 %                                                                             %
2357 %                                                                             %
2358 %     T e x t u r e I m a g e                                                 %
2359 %                                                                             %
2360 %                                                                             %
2361 %                                                                             %
2362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2363 %
2364 %  TextureImage() repeatedly tiles the texture image across and down the image
2365 %  canvas.
2366 %
2367 %  The format of the TextureImage method is:
2368 %
2369 %      MagickBooleanType TextureImage(Image *image,const Image *texture,
2370 %        ExceptionInfo *exception)
2371 %
2372 %  A description of each parameter follows:
2373 %
2374 %    o image: the image.
2375 %
2376 %    o texture_image: This image is the texture to layer on the background.
2377 %
2378 */
2379 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2380   ExceptionInfo *exception)
2381 {
2382 #define TextureImageTag  "Texture/Image"
2383
2384   CacheView
2385     *image_view,
2386     *texture_view;
2387
2388   Image
2389     *texture_image;
2390
2391   MagickBooleanType
2392     status;
2393
2394   ssize_t
2395     y;
2396
2397   assert(image != (Image *) NULL);
2398   if (image->debug != MagickFalse)
2399     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2400   assert(image->signature == MagickCoreSignature);
2401   if (texture == (const Image *) NULL)
2402     return(MagickFalse);
2403   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2404     return(MagickFalse);
2405   texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2406   if (texture_image == (const Image *) NULL)
2407     return(MagickFalse);
2408   (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2409   (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2410     exception);
2411   status=MagickTrue;
2412   if ((image->compose != CopyCompositeOp) &&
2413       ((image->compose != OverCompositeOp) ||
2414        (image->alpha_trait != UndefinedPixelTrait) ||
2415        (texture_image->alpha_trait != UndefinedPixelTrait)))
2416     {
2417       /*
2418         Tile texture onto the image background.
2419       */
2420       for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2421       {
2422         register ssize_t
2423           x;
2424
2425         if (status == MagickFalse)
2426           continue;
2427         for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2428         {
2429           MagickBooleanType
2430             thread_status;
2431
2432           thread_status=CompositeImage(image,texture_image,image->compose,
2433             MagickFalse,x+texture_image->tile_offset.x,y+
2434             texture_image->tile_offset.y,exception);
2435           if (thread_status == MagickFalse)
2436             {
2437               status=thread_status;
2438               break;
2439             }
2440         }
2441         if (image->progress_monitor != (MagickProgressMonitor) NULL)
2442           {
2443             MagickBooleanType
2444               proceed;
2445
2446             proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2447               image->rows);
2448             if (proceed == MagickFalse)
2449               status=MagickFalse;
2450           }
2451       }
2452       (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2453         image->rows,image->rows);
2454       texture_image=DestroyImage(texture_image);
2455       return(status);
2456     }
2457   /*
2458     Tile texture onto the image background (optimized).
2459   */
2460   status=MagickTrue;
2461   texture_view=AcquireVirtualCacheView(texture_image,exception);
2462   image_view=AcquireAuthenticCacheView(image,exception);
2463 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2464   #pragma omp parallel for schedule(static,4) shared(status) \
2465     magick_threads(texture_image,image,1,1)
2466 #endif
2467   for (y=0; y < (ssize_t) image->rows; y++)
2468   {
2469     MagickBooleanType
2470       sync;
2471
2472     register const Quantum
2473       *p,
2474       *pixels;
2475
2476     register ssize_t
2477       x;
2478
2479     register Quantum
2480       *q;
2481
2482     size_t
2483       width;
2484
2485     if (status == MagickFalse)
2486       continue;
2487     pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2488       (y+texture_image->tile_offset.y) % texture_image->rows,
2489       texture_image->columns,1,exception);
2490     q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2491     if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2492       {
2493         status=MagickFalse;
2494         continue;
2495       }
2496     for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2497     {
2498       register ssize_t
2499         j;
2500
2501       p=pixels;
2502       width=texture_image->columns;
2503       if ((x+(ssize_t) width) > (ssize_t) image->columns)
2504         width=image->columns-x;
2505       for (j=0; j < (ssize_t) width; j++)
2506       {
2507         register ssize_t
2508           i;
2509
2510         if (GetPixelReadMask(image,q) == 0)
2511           {
2512             p+=GetPixelChannels(texture_image);
2513             q+=GetPixelChannels(image);
2514             continue;
2515           }
2516         for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2517         {
2518           PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2519           PixelTrait traits=GetPixelChannelTraits(image,channel);
2520           PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2521             channel);
2522           if ((traits == UndefinedPixelTrait) ||
2523               (texture_traits == UndefinedPixelTrait))
2524             continue;
2525           SetPixelChannel(image,channel,p[i],q);
2526         }
2527         p+=GetPixelChannels(texture_image);
2528         q+=GetPixelChannels(image);
2529       }
2530     }
2531     sync=SyncCacheViewAuthenticPixels(image_view,exception);
2532     if (sync == MagickFalse)
2533       status=MagickFalse;
2534     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2535       {
2536         MagickBooleanType
2537           proceed;
2538
2539         proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2540           image->rows);
2541         if (proceed == MagickFalse)
2542           status=MagickFalse;
2543       }
2544   }
2545   texture_view=DestroyCacheView(texture_view);
2546   image_view=DestroyCacheView(image_view);
2547   texture_image=DestroyImage(texture_image);
2548   return(status);
2549 }