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