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