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