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