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