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