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