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