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