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