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