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