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