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