]> granicus.if.org Git - imagemagick/blob - MagickCore/composite.c
(no commit message)
[imagemagick] / MagickCore / composite.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %        CCCC   OOO   M   M  PPPP    OOO   SSSSS  IIIII  TTTTT  EEEEE         %
7 %       C      O   O  MM MM  P   P  O   O  SS       I      T    E             %
8 %       C      O   O  M M M  PPPP   O   O   SSS     I      T    EEE           %
9 %       C      O   O  M   M  P      O   O     SS    I      T    E             %
10 %        CCCC   OOO   M   M  P       OOO   SSSSS  IIIII    T    EEEEE         %
11 %                                                                             %
12 %                                                                             %
13 %                     MagickCore Image Composite Methods                      %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-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,double *red,double *green,
1492   double *blue)
1493 {
1494   double
1495     f,
1496     h,
1497     p,
1498     q,
1499     t;
1500
1501   /*
1502     Convert HSB to RGB colorspace.
1503   */
1504   assert(red != (double *) NULL);
1505   assert(green != (double *) NULL);
1506   assert(blue != (double *) NULL);
1507   if (saturation == 0.0)
1508     {
1509       *red=(double) 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=(double) QuantumRange*brightness;
1525       *green=(double) QuantumRange*t;
1526       *blue=(double) QuantumRange*p;
1527       break;
1528     }
1529     case 1:
1530     {
1531       *red=(double) QuantumRange*q;
1532       *green=(double) QuantumRange*brightness;
1533       *blue=(double) QuantumRange*p;
1534       break;
1535     }
1536     case 2:
1537     {
1538       *red=(double) QuantumRange*p;
1539       *green=(double) QuantumRange*brightness;
1540       *blue=(double) QuantumRange*t;
1541       break;
1542     }
1543     case 3:
1544     {
1545       *red=(double) QuantumRange*p;
1546       *green=(double) QuantumRange*q;
1547       *blue=(double) QuantumRange*brightness;
1548       break;
1549     }
1550     case 4:
1551     {
1552       *red=(double) QuantumRange*t;
1553       *green=(double) QuantumRange*p;
1554       *blue=(double) QuantumRange*brightness;
1555       break;
1556     }
1557     case 5:
1558     {
1559       *red=(double) QuantumRange*brightness;
1560       *green=(double) QuantumRange*p;
1561       *blue=(double) 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   ssize_t
1609     y;
1610
1611   /*
1612     Prepare composite image.
1613   */
1614   assert(image != (Image *) NULL);
1615   assert(image->signature == MagickSignature);
1616   if (image->debug != MagickFalse)
1617     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1618   assert(composite_image != (Image *) NULL);
1619   assert(composite_image->signature == MagickSignature);
1620   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1621     return(MagickFalse);
1622   destination_image=(Image *) NULL;
1623   amount=0.5;
1624   destination_dissolve=1.0;
1625   modify_outside_overlay=MagickFalse;
1626   percent_brightness=100.0;
1627   percent_saturation=100.0;
1628   source_dissolve=1.0;
1629   threshold=0.05f;
1630   switch (compose)
1631   {
1632     case ClearCompositeOp:
1633     case DstAtopCompositeOp:
1634     case DstInCompositeOp:
1635     case InCompositeOp:
1636     case OutCompositeOp:
1637     case SrcCompositeOp:
1638     case SrcInCompositeOp:
1639     case SrcOutCompositeOp:
1640     {
1641       /*
1642         Modify destination outside the overlaid region.
1643       */
1644       modify_outside_overlay=MagickTrue;
1645       break;
1646     }
1647     case CopyCompositeOp:
1648     {
1649       if ((x_offset < 0) || (y_offset < 0))
1650         break;
1651       if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
1652         break;
1653       if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
1654         break;
1655       status=MagickTrue;
1656       image_view=AcquireCacheView(image);
1657       composite_view=AcquireCacheView(composite_image);
1658 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1659 #pragma omp parallel for schedule(dynamic,4) shared(status)
1660 #endif
1661       for (y=0; y < (ssize_t) composite_image->rows; y++)
1662       {
1663         MagickBooleanType
1664           sync;
1665
1666         register const Quantum
1667           *p;
1668
1669         register Quantum
1670           *q;
1671
1672         register ssize_t
1673           x;
1674
1675         if (status == MagickFalse)
1676           continue;
1677         p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1678           1,exception);
1679         q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1680           composite_image->columns,1,exception);
1681         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1682           {
1683             status=MagickFalse;
1684             continue;
1685           }
1686         for (x=0; x < (ssize_t) composite_image->columns; x++)
1687         {
1688           SetPixelRed(image,GetPixelRed(composite_image,p),q);
1689           SetPixelGreen(image,GetPixelGreen(composite_image,p),q);
1690           SetPixelBlue(image,GetPixelBlue(composite_image,p),q);
1691           SetPixelAlpha(image,GetPixelAlpha(composite_image,p),q);
1692           if (image->colorspace == CMYKColorspace)
1693             SetPixelBlack(image,GetPixelBlack(composite_image,p),q);
1694           p+=GetPixelChannels(composite_image);
1695           q+=GetPixelChannels(image);
1696         }
1697         sync=SyncCacheViewAuthenticPixels(image_view,exception);
1698         if (sync == MagickFalse)
1699           status=MagickFalse;
1700         if (image->progress_monitor != (MagickProgressMonitor) NULL)
1701           {
1702             MagickBooleanType
1703               proceed;
1704
1705 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1706 #pragma omp critical (MagickCore_CompositeImage)
1707 #endif
1708             proceed=SetImageProgress(image,CompositeImageTag,
1709               (MagickOffsetType) y,image->rows);
1710             if (proceed == MagickFalse)
1711               status=MagickFalse;
1712           }
1713       }
1714       composite_view=DestroyCacheView(composite_view);
1715       image_view=DestroyCacheView(image_view);
1716       return(status);
1717     }
1718     case CopyOpacityCompositeOp:
1719     case ChangeMaskCompositeOp:
1720     {
1721       /*
1722         Modify destination outside the overlaid region and require an alpha
1723         channel to exist, to add transparency.
1724       */
1725       if (image->matte == MagickFalse)
1726         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1727       modify_outside_overlay=MagickTrue;
1728       break;
1729     }
1730     case BlurCompositeOp:
1731     {
1732       CacheView
1733         *composite_view,
1734         *destination_view;
1735
1736       PixelInfo
1737         pixel;
1738
1739       MagickRealType
1740         angle_range,
1741         angle_start,
1742         height,
1743         width;
1744
1745       ResampleFilter
1746         *resample_filter;
1747
1748       SegmentInfo
1749         blur;
1750
1751       /*
1752         Blur Image dictated by an overlay gradient map: X = red_channel;
1753           Y = green_channel; compose:args =  x_scale[,y_scale[,angle]].
1754       */
1755       destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1756         exception);
1757       if (destination_image == (Image *) NULL)
1758         return(MagickFalse);
1759       /*
1760         Determine the horizontal and vertical maximim blur.
1761       */
1762       SetGeometryInfo(&geometry_info);
1763       flags=NoValue;
1764       value=GetImageArtifact(composite_image,"compose:args");
1765       if (value != (char *) NULL)
1766         flags=ParseGeometry(value,&geometry_info);
1767       if ((flags & WidthValue) == 0 )
1768         {
1769           destination_image=DestroyImage(destination_image);
1770           return(MagickFalse);
1771         }
1772       width=geometry_info.rho;
1773       height=geometry_info.sigma;
1774       blur.x1=geometry_info.rho;
1775       blur.x2=0.0;
1776       blur.y1=0.0;
1777       blur.y2=geometry_info.sigma;
1778       angle_start=0.0;
1779       angle_range=0.0;
1780       if ((flags & HeightValue) == 0)
1781         blur.y2=blur.x1;
1782       if ((flags & XValue) != 0 )
1783         {
1784           MagickRealType
1785             angle;
1786
1787           angle=DegreesToRadians(geometry_info.xi);
1788           blur.x1=width*cos(angle);
1789           blur.x2=width*sin(angle);
1790           blur.y1=(-height*sin(angle));
1791           blur.y2=height*cos(angle);
1792         }
1793       if ((flags & YValue) != 0 )
1794         {
1795           angle_start=DegreesToRadians(geometry_info.xi);
1796           angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1797         }
1798       /*
1799         Blur Image by resampling.
1800       */
1801       resample_filter=AcquireResampleFilter(image,exception);
1802       SetResampleFilter(resample_filter,CubicFilter,2.0);
1803       destination_view=AcquireCacheView(destination_image);
1804       composite_view=AcquireCacheView(composite_image);
1805       for (y=0; y < (ssize_t) composite_image->rows; y++)
1806       {
1807         MagickBooleanType
1808           sync;
1809
1810         register const Quantum
1811           *restrict p;
1812
1813         register Quantum
1814           *restrict q;
1815
1816         register ssize_t
1817           x;
1818
1819         if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1820           continue;
1821         p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1822           1,exception);
1823         q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1824           destination_image->columns,1,exception);
1825         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1826           break;
1827         for (x=0; x < (ssize_t) composite_image->columns; x++)
1828         {
1829           if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1830             {
1831               p+=GetPixelChannels(composite_image);
1832               continue;
1833             }
1834           if (fabs(angle_range) > MagickEpsilon)
1835             {
1836               MagickRealType
1837                 angle;
1838
1839               angle=angle_start+angle_range*QuantumScale*
1840                 GetPixelBlue(composite_image,p);
1841               blur.x1=width*cos(angle);
1842               blur.x2=width*sin(angle);
1843               blur.y1=(-height*sin(angle));
1844               blur.y2=height*cos(angle);
1845             }
1846           ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
1847             GetPixelRed(composite_image,p),blur.y1*QuantumScale*
1848             GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
1849             GetPixelRed(composite_image,p),blur.y2*QuantumScale*
1850             GetPixelGreen(composite_image,p));
1851           (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1852             (double) y_offset+y,&pixel);
1853           SetPixelInfoPixel(destination_image,&pixel,q);
1854           p+=GetPixelChannels(composite_image);
1855           q+=GetPixelChannels(destination_image);
1856         }
1857         sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1858         if (sync == MagickFalse)
1859           break;
1860       }
1861       resample_filter=DestroyResampleFilter(resample_filter);
1862       composite_view=DestroyCacheView(composite_view);
1863       destination_view=DestroyCacheView(destination_view);
1864       composite_image=destination_image;
1865       break;
1866     }
1867     case DisplaceCompositeOp:
1868     case DistortCompositeOp:
1869     {
1870       CacheView
1871         *composite_view,
1872         *destination_view,
1873         *image_view;
1874
1875       PixelInfo
1876         pixel;
1877
1878       MagickRealType
1879         horizontal_scale,
1880         vertical_scale;
1881
1882       PointInfo
1883         center,
1884         offset;
1885
1886       /*
1887         Displace/Distort based on overlay gradient map:
1888           X = red_channel;  Y = green_channel;
1889           compose:args = x_scale[,y_scale[,center.x,center.y]]
1890       */
1891       destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1892         exception);
1893       if (destination_image == (Image *) NULL)
1894         return(MagickFalse);
1895       SetGeometryInfo(&geometry_info);
1896       flags=NoValue;
1897       value=GetImageArtifact(composite_image,"compose:args");
1898       if (value != (char *) NULL)
1899         flags=ParseGeometry(value,&geometry_info);
1900       if ((flags & (WidthValue|HeightValue)) == 0 )
1901         {
1902           if ((flags & AspectValue) == 0)
1903             {
1904               horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1905                 2.0;
1906               vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1907             }
1908           else
1909             {
1910               horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1911               vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1912             }
1913         }
1914       else
1915         {
1916           horizontal_scale=geometry_info.rho;
1917           vertical_scale=geometry_info.sigma;
1918           if ((flags & PercentValue) != 0)
1919             {
1920               if ((flags & AspectValue) == 0)
1921                 {
1922                   horizontal_scale*=(composite_image->columns-1.0)/200.0;
1923                   vertical_scale*=(composite_image->rows-1.0)/200.0;
1924                 }
1925               else
1926                 {
1927                   horizontal_scale*=(image->columns-1.0)/200.0;
1928                   vertical_scale*=(image->rows-1.0)/200.0;
1929                 }
1930             }
1931           if ((flags & HeightValue) == 0)
1932             vertical_scale=horizontal_scale;
1933         }
1934       /*
1935         Determine fixed center point for absolute distortion map
1936          Absolute distort ==
1937            Displace offset relative to a fixed absolute point
1938            Select that point according to +X+Y user inputs.
1939            default = center of overlay image
1940            arg flag '!' = locations/percentage relative to background image
1941       */
1942       center.x=(MagickRealType) x_offset;
1943       center.y=(MagickRealType) y_offset;
1944       if (compose == DistortCompositeOp)
1945         {
1946           if ((flags & XValue) == 0)
1947             if ((flags & AspectValue) == 0)
1948               center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
1949                 2.0;
1950             else
1951               center.x=((MagickRealType) image->columns-1)/2.0;
1952           else
1953             if ((flags & AspectValue) == 0)
1954               center.x=(MagickRealType) x_offset+geometry_info.xi;
1955             else
1956               center.x=geometry_info.xi;
1957           if ((flags & YValue) == 0)
1958             if ((flags & AspectValue) == 0)
1959               center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
1960             else
1961               center.y=((MagickRealType) image->rows-1)/2.0;
1962           else
1963             if ((flags & AspectValue) == 0)
1964               center.y=(MagickRealType) y_offset+geometry_info.psi;
1965             else
1966               center.y=geometry_info.psi;
1967         }
1968       /*
1969         Shift the pixel offset point as defined by the provided,
1970         displacement/distortion map.  -- Like a lens...
1971       */
1972       GetPixelInfo(image,&pixel);
1973       image_view=AcquireCacheView(image);
1974       destination_view=AcquireCacheView(destination_image);
1975       composite_view=AcquireCacheView(composite_image);
1976       for (y=0; y < (ssize_t) composite_image->rows; y++)
1977       {
1978         MagickBooleanType
1979           sync;
1980
1981         register const Quantum
1982           *restrict p;
1983
1984         register Quantum
1985           *restrict q;
1986
1987         register ssize_t
1988           x;
1989
1990         if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1991           continue;
1992         p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1993           1,exception);
1994         q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1995           destination_image->columns,1,exception);
1996         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1997           break;
1998         for (x=0; x < (ssize_t) composite_image->columns; x++)
1999         {
2000           if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
2001             {
2002               p+=GetPixelChannels(composite_image);
2003               continue;
2004             }
2005           /*
2006             Displace the offset.
2007           */
2008           offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
2009             (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2010             QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
2011             x : 0);
2012           offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
2013             (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2014             QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
2015             y : 0);
2016           (void) InterpolatePixelInfo(image,image_view,
2017             UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2018             &pixel,exception);
2019           /*
2020             Mask with the 'invalid pixel mask' in alpha channel.
2021           */
2022           pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
2023             pixel.alpha)*(1.0-QuantumScale*
2024             GetPixelAlpha(composite_image,p)));
2025           SetPixelInfoPixel(destination_image,&pixel,q);
2026           p+=GetPixelChannels(composite_image);
2027           q+=GetPixelChannels(destination_image);
2028         }
2029         sync=SyncCacheViewAuthenticPixels(destination_view,exception);
2030         if (sync == MagickFalse)
2031           break;
2032       }
2033       destination_view=DestroyCacheView(destination_view);
2034       composite_view=DestroyCacheView(composite_view);
2035       image_view=DestroyCacheView(image_view);
2036       composite_image=destination_image;
2037       break;
2038     }
2039     case DissolveCompositeOp:
2040     {
2041       /*
2042         Geometry arguments to dissolve factors.
2043       */
2044       value=GetImageArtifact(composite_image,"compose:args");
2045       if (value != (char *) NULL)
2046         {
2047           flags=ParseGeometry(value,&geometry_info);
2048           source_dissolve=geometry_info.rho/100.0;
2049           destination_dissolve=1.0;
2050           if ((source_dissolve-MagickEpsilon) < 0.0)
2051             source_dissolve=0.0;
2052           if ((source_dissolve+MagickEpsilon) > 1.0)
2053             {
2054               destination_dissolve=2.0-source_dissolve;
2055               source_dissolve=1.0;
2056             }
2057           if ((flags & SigmaValue) != 0)
2058             destination_dissolve=geometry_info.sigma/100.0;
2059           if ((destination_dissolve-MagickEpsilon) < 0.0)
2060             destination_dissolve=0.0;
2061           modify_outside_overlay=MagickTrue;
2062           if ((destination_dissolve+MagickEpsilon) > 1.0 )
2063             {
2064               destination_dissolve=1.0;
2065               modify_outside_overlay=MagickFalse;
2066             }
2067         }
2068       break;
2069     }
2070     case BlendCompositeOp:
2071     {
2072       value=GetImageArtifact(composite_image,"compose:args");
2073       if (value != (char *) NULL)
2074         {
2075           flags=ParseGeometry(value,&geometry_info);
2076           source_dissolve=geometry_info.rho/100.0;
2077           destination_dissolve=1.0-source_dissolve;
2078           if ((flags & SigmaValue) != 0)
2079             destination_dissolve=geometry_info.sigma/100.0;
2080           modify_outside_overlay=MagickTrue;
2081           if ((destination_dissolve+MagickEpsilon) > 1.0)
2082             modify_outside_overlay=MagickFalse;
2083         }
2084       break;
2085     }
2086     case MathematicsCompositeOp:
2087     {
2088       /*
2089         Just collect the values from "compose:args", setting.
2090         Unused values are set to zero automagically.
2091
2092         Arguments are normally a comma separated list, so this probably should
2093         be changed to some 'general comma list' parser, (with a minimum
2094         number of values)
2095       */
2096       SetGeometryInfo(&geometry_info);
2097       value=GetImageArtifact(composite_image,"compose:args");
2098       if (value != (char *) NULL)
2099         (void) ParseGeometry(value,&geometry_info);
2100       break;
2101     }
2102     case ModulateCompositeOp:
2103     {
2104       /*
2105         Determine the brightness and saturation scale.
2106       */
2107       value=GetImageArtifact(composite_image,"compose:args");
2108       if (value != (char *) NULL)
2109         {
2110           flags=ParseGeometry(value,&geometry_info);
2111           percent_brightness=geometry_info.rho;
2112           if ((flags & SigmaValue) != 0)
2113             percent_saturation=geometry_info.sigma;
2114         }
2115       break;
2116     }
2117     case ThresholdCompositeOp:
2118     {
2119       /*
2120         Determine the amount and threshold.
2121       */
2122       value=GetImageArtifact(composite_image,"compose:args");
2123       if (value != (char *) NULL)
2124         {
2125           flags=ParseGeometry(value,&geometry_info);
2126           amount=geometry_info.rho;
2127           threshold=geometry_info.sigma;
2128           if ((flags & SigmaValue) == 0)
2129             threshold=0.05f;
2130         }
2131       threshold*=QuantumRange;
2132       break;
2133     }
2134     default:
2135       break;
2136   }
2137   value=GetImageArtifact(composite_image,"compose:outside-overlay");
2138   if (value != (const char *) NULL)
2139     modify_outside_overlay=IsMagickTrue(value);
2140   /*
2141     Composite image.
2142   */
2143   status=MagickTrue;
2144   progress=0;
2145   midpoint=((MagickRealType) QuantumRange+1.0)/2;
2146   image_view=AcquireCacheView(image);
2147   composite_view=AcquireCacheView(composite_image);
2148 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2149 //  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2150 #endif
2151   for (y=0; y < (ssize_t) image->rows; y++)
2152   {
2153     const Quantum
2154       *pixels;
2155
2156     double
2157       brightness,
2158       hue,
2159       saturation;
2160
2161     register const Quantum
2162       *restrict p;
2163
2164     register Quantum
2165       *restrict q;
2166
2167     register ssize_t
2168       x;
2169
2170 MagickBooleanType composite_channels;
2171
2172     if (status == MagickFalse)
2173       continue;
2174     if (modify_outside_overlay == MagickFalse)
2175       {
2176         if (y < y_offset)
2177           continue;
2178         if ((y-y_offset) >= (ssize_t) composite_image->rows)
2179           continue;
2180       }
2181     /*
2182       If pixels is NULL, y is outside overlay region.
2183     */
2184     pixels=(Quantum *) NULL;
2185     p=(Quantum *) NULL;
2186     if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
2187       {
2188         p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
2189           composite_image->columns,1,exception);
2190         if (p == (const Quantum *) NULL)
2191           {
2192             status=MagickFalse;
2193             continue;
2194           }
2195         pixels=p;
2196         if (x_offset < 0)
2197           p-=x_offset*GetPixelChannels(composite_image);
2198       }
2199     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2200     if (q == (Quantum *) NULL)
2201       {
2202         status=MagickFalse;
2203         continue;
2204       }
2205     hue=0.0;
2206     saturation=0.0;
2207     brightness=0.0;
2208     for (x=0; x < (ssize_t) image->columns; x++)
2209     {
2210       if (modify_outside_overlay == MagickFalse)
2211         {
2212           if (x < x_offset)
2213             {
2214               q+=GetPixelChannels(image);
2215               continue;
2216             }
2217           if ((x-x_offset) >= (ssize_t) composite_image->columns)
2218             break;
2219         }
2220       composite_channels=MagickFalse;
2221       switch (compose)
2222       {
2223         case AtopCompositeOp:
2224         case ClearCompositeOp:
2225         case CopyCompositeOp:
2226         case DstAtopCompositeOp:
2227         case DstCompositeOp:
2228         case DstInCompositeOp:
2229         case DstOverCompositeOp:
2230         case DstOutCompositeOp:
2231         case InCompositeOp:
2232         case OutCompositeOp:
2233         case OverCompositeOp:
2234         case ReplaceCompositeOp:
2235         case SrcAtopCompositeOp:
2236         case SrcCompositeOp:
2237         case SrcInCompositeOp:
2238         case SrcOutCompositeOp:
2239         case SrcOverCompositeOp:
2240         case XorCompositeOp:
2241         {
2242           composite_channels=MagickTrue;
2243           break;
2244         }
2245         default:
2246           break;
2247       }
2248       if (composite_channels != MagickFalse) {
2249         MagickRealType
2250           Da,
2251           Dc,
2252           gamma,
2253           Sa,
2254           Sc;
2255
2256         register ssize_t
2257           i;
2258
2259         if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2260             ((x-x_offset) >= (ssize_t) composite_image->columns))
2261           {
2262             Quantum
2263               source[MaxPixelChannels];
2264
2265             /*
2266               Virtual composite:
2267                 Sc: source color.
2268                 Dc: destination color.
2269             */
2270             (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
2271               source,exception);
2272             for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2273             {
2274               MagickRealType
2275                 pixel;
2276
2277               PixelChannel
2278                 channel;
2279
2280               PixelTrait
2281                 traits;
2282
2283               channel=GetPixelChannelMapChannel(image,i);
2284               traits=GetPixelChannelMapTraits(image,channel);
2285               if (traits == UndefinedPixelTrait)
2286                 continue;
2287               switch (compose)
2288               {
2289                 case ClearCompositeOp:
2290                 case CopyCompositeOp:
2291                 case ReplaceCompositeOp:
2292                 case SrcCompositeOp:
2293                 {
2294                   pixel=0.0;
2295                   if (channel == AlphaPixelChannel)
2296                     pixel=(MagickRealType) TransparentAlpha;
2297                   break;
2298                 }
2299                 case DstAtopCompositeOp:
2300                 case InCompositeOp:
2301                 case OutCompositeOp:
2302                 case SrcInCompositeOp:
2303                 case SrcOutCompositeOp:
2304                 {
2305                   pixel=(MagickRealType) q[i];
2306                   if (channel == AlphaPixelChannel)
2307                     pixel=(MagickRealType) TransparentAlpha;
2308                   break;
2309                 }
2310                 default:
2311                 {
2312                   pixel=source[channel];
2313                   break;
2314                 }
2315               }
2316               q[i]=ClampToQuantum(pixel);
2317             }
2318             q+=GetPixelChannels(image);
2319             continue;
2320           }
2321         /*
2322           Authentic composite:
2323             Sa: source normalized alpha.
2324             Da: destination normalized alpha.
2325         */
2326         Sa=QuantumScale*GetPixelAlpha(composite_image,p);
2327         Da=QuantumScale*GetPixelAlpha(image,q);
2328         switch (compose)
2329         {
2330           case DstOverCompositeOp:
2331           {
2332             gamma=Da*(-Sa)+Da+Sa;
2333             break;
2334           }
2335           case DstAtopCompositeOp:
2336           case DstInCompositeOp:
2337           case InCompositeOp:
2338           case SrcInCompositeOp:
2339           {
2340             gamma=Sa*Da;
2341             break;
2342           }
2343           case DstOutCompositeOp:
2344           {
2345             gamma=Da*(1.0-Sa);
2346             break;
2347           }
2348           case OutCompositeOp:
2349           case SrcOutCompositeOp:
2350           {
2351             gamma=Sa*(1.0-Da);
2352             break;
2353           }
2354           case OverCompositeOp:
2355           case SrcOverCompositeOp:
2356           {
2357             gamma=Sa*(-Da)+Sa+Da;
2358             break;
2359           }
2360           case XorCompositeOp:
2361           {
2362             gamma=Sa+Da-2.0*Sa*Da;
2363             break;
2364           }
2365           default:
2366           {
2367             gamma=1.0;
2368             break;
2369           }
2370         }
2371         for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2372         {
2373           MagickRealType
2374             pixel;
2375
2376           PixelChannel
2377             channel;
2378
2379           PixelTrait
2380             composite_traits,
2381             traits;
2382
2383           channel=GetPixelChannelMapChannel(image,i);
2384           traits=GetPixelChannelMapTraits(image,channel);
2385           composite_traits=GetPixelChannelMapTraits(composite_image,channel);
2386           if ((traits == UndefinedPixelTrait) ||
2387               (composite_traits == UndefinedPixelTrait))
2388             continue;
2389           /*
2390             Sc: source color.
2391             Dc: destination color.
2392           */
2393           Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
2394           Dc=(MagickRealType) q[i];
2395           if ((traits & CopyPixelTrait) != 0)
2396             {
2397               if (channel != AlphaPixelChannel)
2398                 {
2399                   /*
2400                     Copy channel.
2401                   */
2402                   q[i]=Sc;
2403                   continue;
2404                 }
2405               /*
2406                 Set alpha channel.
2407               */
2408               pixel=0.0;
2409               switch (compose)
2410               {
2411                 case CopyCompositeOp:
2412                 case DstAtopCompositeOp:
2413                 case ReplaceCompositeOp:
2414                 case SrcCompositeOp:
2415                 {
2416                   pixel=QuantumRange*Sa;
2417                   break;
2418                 }
2419                 case AtopCompositeOp:
2420                 case SrcAtopCompositeOp:
2421                 case DstCompositeOp:
2422                 {
2423                   pixel=QuantumRange*Da;
2424                   break;
2425                 }
2426                 default:
2427                 {
2428                   pixel=QuantumRange*gamma;
2429                   break;
2430                 }
2431               }
2432               q[i]=ClampToQuantum(pixel);
2433               continue;
2434             }
2435           /*
2436             Porter-Duff compositions.
2437           */
2438           pixel=0.0;
2439           switch (compose)
2440           {
2441             case AtopCompositeOp:
2442             case SrcAtopCompositeOp:
2443             {
2444               pixel=Sc*Sa+Dc*(1.0-Sa);
2445               break;
2446             }
2447             case CopyCompositeOp:
2448             case ReplaceCompositeOp:
2449             case SrcCompositeOp:
2450             {
2451               pixel=Sc;
2452               break;
2453             }
2454             case DstAtopCompositeOp:
2455             {
2456               pixel=Dc*Da+Sc*(1.0-Da);
2457               break;
2458             }
2459             case DstCompositeOp:
2460             {
2461               pixel=Dc;
2462               break;
2463             }
2464             case DstInCompositeOp:
2465             {
2466               pixel=gamma*(Sa*Dc*Sa);
2467               break;
2468             }
2469             case DstOutCompositeOp:
2470             {
2471               pixel=gamma*(Da*Dc*(1.0-Sa));
2472               break;
2473             }
2474             case DstOverCompositeOp:
2475             {
2476               pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
2477               break;
2478             }
2479             case InCompositeOp:
2480             case SrcInCompositeOp:
2481             {
2482               pixel=gamma*(Da*Sc*Da);
2483               break;
2484             }
2485             case OutCompositeOp:
2486             case SrcOutCompositeOp:
2487             {
2488               pixel=gamma*(Sa*Sc*(1.0-Da));
2489               break;
2490             }
2491             case OverCompositeOp:
2492             case SrcOverCompositeOp:
2493             {
2494               pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
2495               break;
2496             }
2497             case XorCompositeOp:
2498             {
2499               pixel=gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
2500               break;
2501             }
2502             default:
2503               break;
2504           }
2505           q[i]=ClampToQuantum(pixel);
2506         }
2507       } else {
2508         PixelInfo
2509           composite,
2510           destination,
2511           source,
2512           zero;
2513
2514         GetPixelInfo(image,&zero);
2515         source=zero;
2516         destination=zero;
2517         destination.red=(MagickRealType) GetPixelRed(image,q);
2518         destination.green=(MagickRealType) GetPixelGreen(image,q);
2519         destination.blue=(MagickRealType) GetPixelBlue(image,q);
2520         if (image->colorspace == CMYKColorspace)
2521           destination.black=(MagickRealType) GetPixelBlack(image,q);
2522         if (image->colorspace == CMYKColorspace)
2523           {
2524             destination.red=(MagickRealType) QuantumRange-destination.red;
2525             destination.green=(MagickRealType) QuantumRange-destination.green;
2526             destination.blue=(MagickRealType) QuantumRange-destination.blue;
2527             destination.black=(MagickRealType) QuantumRange-destination.black;
2528           }
2529         if (image->matte != MagickFalse)
2530           destination.alpha=(MagickRealType) GetPixelAlpha(image,q);
2531         /*
2532           Handle destination modifications outside overlaid region.
2533         */
2534         composite=destination;
2535         if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2536             ((x-x_offset) >= (ssize_t) composite_image->columns))
2537           {
2538             switch (compose)
2539             {
2540               case DissolveCompositeOp:
2541               case BlendCompositeOp:
2542               {
2543                 composite.alpha=destination_dissolve*(composite.alpha);
2544                 break;
2545               }
2546               case ClearCompositeOp:
2547               case SrcCompositeOp:
2548               {
2549                 CompositeClear(&destination,&composite);
2550                 break;
2551               }
2552               case InCompositeOp:
2553               case SrcInCompositeOp:
2554               case OutCompositeOp:
2555               case SrcOutCompositeOp:
2556               case DstInCompositeOp:
2557               case DstAtopCompositeOp:
2558               case CopyOpacityCompositeOp:
2559               case ChangeMaskCompositeOp:
2560               {
2561                 composite.alpha=(MagickRealType) TransparentAlpha;
2562                 break;
2563               }
2564               default:
2565               {
2566                 (void) GetOneVirtualPixelInfo(composite_image,
2567                   GetPixelCacheVirtualMethod(composite_image),x-x_offset,y-
2568                   y_offset,&composite,exception);
2569                 break;
2570               }
2571             }
2572             if (image->colorspace == CMYKColorspace)
2573               {
2574                 composite.red=(MagickRealType) QuantumRange-composite.red;
2575                 composite.green=(MagickRealType) QuantumRange-composite.green;
2576                 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2577                 composite.black=(MagickRealType) QuantumRange-composite.black;
2578               }
2579             SetPixelRed(image,ClampToQuantum(composite.red),q);
2580             SetPixelGreen(image,ClampToQuantum(composite.green),q);
2581             SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2582             if (image->matte != MagickFalse)
2583               SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2584             if (image->colorspace == CMYKColorspace)
2585               SetPixelBlack(image,ClampToQuantum(composite.black),q);
2586             q+=GetPixelChannels(image);
2587             continue;
2588           }
2589         /*
2590           Handle normal overlay of source onto destination.
2591         */
2592         source.red=(MagickRealType) GetPixelRed(composite_image,p);
2593         source.green=(MagickRealType) GetPixelGreen(composite_image,p);
2594         source.blue=(MagickRealType) GetPixelBlue(composite_image,p);
2595         if (composite_image->colorspace == CMYKColorspace)
2596           source.black=(MagickRealType) GetPixelBlack(composite_image,p);
2597         if (composite_image->colorspace == CMYKColorspace)
2598           {
2599             source.red=(MagickRealType) QuantumRange-source.red;
2600             source.green=(MagickRealType) QuantumRange-source.green;
2601             source.blue=(MagickRealType) QuantumRange-source.blue;
2602             source.black=(MagickRealType) QuantumRange-source.black;
2603           }
2604         if (composite_image->matte != MagickFalse)
2605           source.alpha=(MagickRealType) GetPixelAlpha(composite_image,p);
2606         /*
2607           Porter-Duff compositions.
2608         */
2609         switch (compose)
2610         {
2611           case ClearCompositeOp:
2612           {
2613             CompositeClear(&destination,&composite);
2614             break;
2615           }
2616           case SrcCompositeOp:
2617           case CopyCompositeOp:
2618           case ReplaceCompositeOp:
2619           {
2620             composite=source;
2621             break;
2622           }
2623           case NoCompositeOp:
2624           case DstCompositeOp:
2625             break;
2626           case OverCompositeOp:
2627           case SrcOverCompositeOp:
2628           {
2629             CompositePixelInfoOver(&source,source.alpha,&destination,
2630               destination.alpha,&composite);
2631             break;
2632           }
2633           case DstOverCompositeOp:
2634           {
2635             CompositePixelInfoOver(&destination,destination.alpha,&source,
2636               source.alpha,&composite);
2637             break;
2638           }
2639           case SrcInCompositeOp:
2640           case InCompositeOp:
2641           {
2642             CompositeIn(&source,&destination,&composite);
2643             break;
2644           }
2645           case DstInCompositeOp:
2646           {
2647             CompositeIn(&destination,&source,&composite);
2648             break;
2649           }
2650           case OutCompositeOp:
2651           case SrcOutCompositeOp:
2652           {
2653             CompositeOut(&source,&destination,&composite);
2654             break;
2655           }
2656           case DstOutCompositeOp:
2657           {
2658             CompositeOut(&destination,&source,&composite);
2659             break;
2660           }
2661           case AtopCompositeOp:
2662           case SrcAtopCompositeOp:
2663           {
2664             CompositeAtop(&source,&destination,&composite);
2665             break;
2666           }
2667           case DstAtopCompositeOp:
2668           {
2669             CompositeAtop(&destination,&source,&composite);
2670             break;
2671           }
2672           case XorCompositeOp:
2673           {
2674             CompositeXor(&source,&destination,&composite);
2675             break;
2676           }
2677           case PlusCompositeOp:
2678           {
2679             CompositePlus(image,&source,&destination,&composite);
2680             break;
2681           }
2682           case MinusDstCompositeOp:
2683           {
2684             CompositeMinus(image,&source,&destination,&composite);
2685             break;
2686           }
2687           case MinusSrcCompositeOp:
2688           {
2689             CompositeMinus(image,&destination,&source,&composite);
2690             break;
2691           }
2692           case ModulusAddCompositeOp:
2693           {
2694             CompositeModulusAdd(image,&source,&destination,&composite);
2695             break;
2696           }
2697           case ModulusSubtractCompositeOp:
2698           {
2699             CompositeModulusSubtract(image,&source,&destination,&composite);
2700             break;
2701           }
2702           case DifferenceCompositeOp:
2703           {
2704             CompositeDifference(image,&source,&destination,&composite);
2705             break;
2706           }
2707           case ExclusionCompositeOp:
2708           {
2709             CompositeExclusion(image,&source,&destination,&composite);
2710             break;
2711           }
2712           case MultiplyCompositeOp:
2713           {
2714             CompositeMultiply(image,&source,&destination,&composite);
2715             break;
2716           }
2717           case ScreenCompositeOp:
2718           {
2719             CompositeScreen(image,&source,&destination,&composite);
2720             break;
2721           }
2722           case DivideDstCompositeOp:
2723           {
2724             CompositeDivide(image,&source,&destination,&composite);
2725             break;
2726           }
2727           case DivideSrcCompositeOp:
2728           {
2729             CompositeDivide(image,&destination,&source,&composite);
2730             break;
2731           }
2732           case DarkenCompositeOp:
2733           {
2734             CompositeDarken(image,&source,&destination,&composite);
2735             break;
2736           }
2737           case LightenCompositeOp:
2738           {
2739             CompositeLighten(image,&source,&destination,&composite);
2740             break;
2741           }
2742           case DarkenIntensityCompositeOp:
2743           {
2744             CompositeDarkenIntensity(image,&source,&destination,&composite);
2745             break;
2746           }
2747           case LightenIntensityCompositeOp:
2748           {
2749             CompositeLightenIntensity(image,&source,&destination,&composite);
2750             break;
2751           }
2752           case MathematicsCompositeOp:
2753           {
2754             CompositeMathematics(image,&source,&destination,&geometry_info,
2755               &composite);
2756             break;
2757           }
2758           case ColorDodgeCompositeOp:
2759           {
2760             CompositeColorDodge(&source,&destination,&composite);
2761             break;
2762           }
2763           case ColorBurnCompositeOp:
2764           {
2765             CompositeColorBurn(&source,&destination,&composite);
2766             break;
2767           }
2768           case LinearDodgeCompositeOp:
2769           {
2770             CompositeLinearDodge(&source,&destination,&composite);
2771             break;
2772           }
2773           case LinearBurnCompositeOp:
2774           {
2775             CompositeLinearBurn(&source,&destination,&composite);
2776             break;
2777           }
2778           case HardLightCompositeOp:
2779           {
2780             CompositeHardLight(&source,&destination,&composite);
2781             break;
2782           }
2783           case OverlayCompositeOp:
2784           {
2785             CompositeHardLight(&destination,&source,&composite);
2786             break;
2787           }
2788           case SoftLightCompositeOp:
2789           {
2790             CompositeSoftLight(&source,&destination,&composite);
2791             break;
2792           }
2793           case LinearLightCompositeOp:
2794           {
2795             CompositeLinearLight(&source,&destination,&composite);
2796             break;
2797           }
2798           case PegtopLightCompositeOp:
2799           {
2800             CompositePegtopLight(&source,&destination,&composite);
2801             break;
2802           }
2803           case VividLightCompositeOp:
2804           {
2805             CompositeVividLight(&source,&destination,&composite);
2806             break;
2807           }
2808           case PinLightCompositeOp:
2809           {
2810             CompositePinLight(&source,&destination,&composite);
2811             break;
2812           }
2813           case ChangeMaskCompositeOp:
2814           {
2815             if ((composite.alpha > ((MagickRealType) QuantumRange/2.0)) ||
2816                 (IsFuzzyEquivalencePixelInfo(&source,&destination) != MagickFalse))
2817               composite.alpha=(MagickRealType) TransparentAlpha;
2818             else
2819               composite.alpha=(MagickRealType) OpaqueAlpha;
2820             break;
2821           }
2822           case BumpmapCompositeOp:
2823           {
2824             if (source.alpha == TransparentAlpha)
2825               break;
2826             CompositeBumpmap(&source,&destination,&composite);
2827             break;
2828           }
2829           case DissolveCompositeOp:
2830           {
2831             CompositePixelInfoOver(&source,source_dissolve*source.alpha,
2832               &destination,(MagickRealType) (destination_dissolve*
2833               destination.alpha),&composite);
2834             break;
2835           }
2836           case BlendCompositeOp:
2837           {
2838             CompositePixelInfoBlend(&source,source_dissolve,&destination,
2839               destination_dissolve,&composite);
2840             break;
2841           }
2842           case ThresholdCompositeOp:
2843           {
2844             CompositeThreshold(&source,&destination,threshold,amount,&composite);
2845             break;
2846           }
2847           case ModulateCompositeOp:
2848           {
2849             double
2850               blue,
2851               green,
2852               red;
2853
2854             ssize_t
2855               offset;
2856
2857             if (source.alpha == TransparentAlpha)
2858               break;
2859             offset=(ssize_t) (GetPixelInfoIntensity(&source)-midpoint);
2860             if (offset == 0)
2861               break;
2862             CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2863               &saturation,&brightness);
2864             brightness+=(0.01*percent_brightness*offset)/midpoint;
2865             saturation*=0.01*percent_saturation;
2866             HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2867             composite.red=red;
2868             composite.green=green;
2869             composite.blue=blue;
2870             break;
2871           }
2872           case HueCompositeOp:
2873           {
2874             if (source.alpha == TransparentAlpha)
2875               break;
2876             if (destination.alpha == TransparentAlpha)
2877               {
2878                 composite=source;
2879                 break;
2880               }
2881             CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2882               &saturation,&brightness);
2883             CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
2884             HSBComposite(hue,saturation,brightness,&composite.red,
2885               &composite.green,&composite.blue);
2886             if (source.alpha < destination.alpha)
2887               composite.alpha=source.alpha;
2888             break;
2889           }
2890           case SaturateCompositeOp:
2891           {
2892             if (source.alpha == TransparentAlpha)
2893               break;
2894             if (destination.alpha == TransparentAlpha)
2895               {
2896                 composite=source;
2897                 break;
2898               }
2899             CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2900               &saturation,&brightness);
2901             CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
2902               &sans);
2903             HSBComposite(hue,saturation,brightness,&composite.red,
2904               &composite.green,&composite.blue);
2905             if (source.alpha < destination.alpha)
2906               composite.alpha=source.alpha;
2907             break;
2908           }
2909           case LuminizeCompositeOp:
2910           {
2911             if (source.alpha == TransparentAlpha)
2912               break;
2913             if (destination.alpha == TransparentAlpha)
2914               {
2915                 composite=source;
2916                 break;
2917               }
2918             CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2919               &saturation,&brightness);
2920             CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
2921               &brightness);
2922             HSBComposite(hue,saturation,brightness,&composite.red,
2923               &composite.green,&composite.blue);
2924             if (source.alpha < destination.alpha)
2925               composite.alpha=source.alpha;
2926             break;
2927           }
2928           case ColorizeCompositeOp:
2929           {
2930             if (source.alpha == TransparentAlpha)
2931               break;
2932             if (destination.alpha == TransparentAlpha)
2933               {
2934                 composite=source;
2935                 break;
2936               }
2937             CompositeHSB(destination.red,destination.green,destination.blue,&sans,
2938               &sans,&brightness);
2939             CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
2940               &sans);
2941             HSBComposite(hue,saturation,brightness,&composite.red,
2942               &composite.green,&composite.blue);
2943             if (source.alpha < destination.alpha)
2944               composite.alpha=source.alpha;
2945             break;
2946           }
2947           case CopyRedCompositeOp:
2948           case CopyCyanCompositeOp:
2949           {
2950             composite.red=source.red;
2951             break;
2952           }
2953           case CopyGreenCompositeOp:
2954           case CopyMagentaCompositeOp:
2955           {
2956             composite.green=source.green;
2957             break;
2958           }
2959           case CopyBlueCompositeOp:
2960           case CopyYellowCompositeOp:
2961           {
2962             composite.blue=source.blue;
2963             break;
2964           }
2965           case CopyOpacityCompositeOp:
2966           {
2967             if (source.matte == MagickFalse)
2968               {
2969                 composite.alpha=(MagickRealType) GetPixelInfoIntensity(&source);
2970                 break;
2971               }
2972             composite.alpha=source.alpha;
2973             break;
2974           }
2975           case CopyBlackCompositeOp:
2976           {
2977             if (source.colorspace != CMYKColorspace)
2978               ConvertRGBToCMYK(&source);
2979             composite.black=source.black;
2980             break;
2981           }
2982           case BlurCompositeOp:
2983           case DisplaceCompositeOp:
2984           case DistortCompositeOp:
2985           {
2986             composite=source;
2987             break;
2988           }
2989           default:
2990             break;
2991         }
2992         if (image->colorspace == CMYKColorspace)
2993           {
2994             composite.red=(MagickRealType) QuantumRange-composite.red;
2995             composite.green=(MagickRealType) QuantumRange-composite.green;
2996             composite.blue=(MagickRealType) QuantumRange-composite.blue;
2997             composite.black=(MagickRealType) QuantumRange-composite.black;
2998           }
2999         SetPixelRed(image,ClampToQuantum(composite.red),q);
3000         SetPixelGreen(image,ClampToQuantum(composite.green),q);
3001         SetPixelBlue(image,ClampToQuantum(composite.blue),q);
3002         if (image->colorspace == CMYKColorspace)
3003           SetPixelBlack(image,ClampToQuantum(composite.black),q);
3004         SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
3005       }
3006       p+=GetPixelChannels(composite_image);
3007       if (p >= (pixels+composite_image->columns*GetPixelChannels(composite_image)))
3008         p=pixels;
3009       q+=GetPixelChannels(image);
3010     }
3011     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3012       status=MagickFalse;
3013     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3014       {
3015         MagickBooleanType
3016           proceed;
3017
3018 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3019   #pragma omp critical (MagickCore_CompositeImage)
3020 #endif
3021         proceed=SetImageProgress(image,CompositeImageTag,progress++,
3022           image->rows);
3023         if (proceed == MagickFalse)
3024           status=MagickFalse;
3025       }
3026   }
3027   composite_view=DestroyCacheView(composite_view);
3028   image_view=DestroyCacheView(image_view);
3029   if (destination_image != (Image * ) NULL)
3030     destination_image=DestroyImage(destination_image);
3031   return(status);
3032 }
3033 \f
3034 /*
3035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3036 %                                                                             %
3037 %                                                                             %
3038 %                                                                             %
3039 %     T e x t u r e I m a g e                                                 %
3040 %                                                                             %
3041 %                                                                             %
3042 %                                                                             %
3043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3044 %
3045 %  TextureImage() repeatedly tiles the texture image across and down the image
3046 %  canvas.
3047 %
3048 %  The format of the TextureImage method is:
3049 %
3050 %      MagickBooleanType TextureImage(Image *image,const Image *texture,
3051 %        ExceptionInfo *exception)
3052 %
3053 %  A description of each parameter follows:
3054 %
3055 %    o image: the image.
3056 %
3057 %    o texture: This image is the texture to layer on the background.
3058 %
3059 */
3060 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
3061   ExceptionInfo *exception)
3062 {
3063 #define TextureImageTag  "Texture/Image"
3064
3065   CacheView
3066     *image_view,
3067     *texture_view;
3068
3069   MagickBooleanType
3070     status;
3071
3072   ssize_t
3073     y;
3074
3075   assert(image != (Image *) NULL);
3076   if (image->debug != MagickFalse)
3077     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3078   assert(image->signature == MagickSignature);
3079   if (texture == (const Image *) NULL)
3080     return(MagickFalse);
3081   (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
3082   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
3083     return(MagickFalse);
3084   status=MagickTrue;
3085   if ((image->compose != CopyCompositeOp) &&
3086       ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
3087        (texture->matte != MagickFalse)))
3088     {
3089       /*
3090         Tile texture onto the image background.
3091       */
3092 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3093       #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
3094 #endif
3095       for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
3096       {
3097         register ssize_t
3098           x;
3099
3100         if (status == MagickFalse)
3101           continue;
3102         for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
3103         {
3104           MagickBooleanType
3105             thread_status;
3106
3107           thread_status=CompositeImage(image,image->compose,texture,x+
3108             texture->tile_offset.x,y+texture->tile_offset.y,exception);
3109           if (thread_status == MagickFalse)
3110             {
3111               status=thread_status;
3112               break;
3113             }
3114         }
3115         if (image->progress_monitor != (MagickProgressMonitor) NULL)
3116           {
3117             MagickBooleanType
3118               proceed;
3119
3120 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3121   #pragma omp critical (MagickCore_TextureImage)
3122 #endif
3123             proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
3124               y,image->rows);
3125             if (proceed == MagickFalse)
3126               status=MagickFalse;
3127           }
3128       }
3129       (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
3130         image->rows,image->rows);
3131       return(status);
3132     }
3133   /*
3134     Tile texture onto the image background (optimized).
3135   */
3136   status=MagickTrue;
3137   image_view=AcquireCacheView(image);
3138   texture_view=AcquireCacheView(texture);
3139 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3140   #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
3141 #endif
3142   for (y=0; y < (ssize_t) image->rows; y++)
3143   {
3144     MagickBooleanType
3145       sync;
3146
3147     register const Quantum
3148       *p,
3149       *pixels;
3150
3151     register ssize_t
3152       x;
3153
3154     register Quantum
3155       *q;
3156
3157     size_t
3158       width;
3159
3160     if (status == MagickFalse)
3161       continue;
3162     pixels=GetCacheViewVirtualPixels(texture_view,texture->tile_offset.x,(y+
3163       texture->tile_offset.y) % texture->rows,texture->columns,1,exception);
3164     q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3165       exception);
3166     if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3167       {
3168         status=MagickFalse;
3169         continue;
3170       }
3171     for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
3172     {
3173       register ssize_t
3174         i;
3175
3176       p=pixels;
3177       width=texture->columns;
3178       if ((x+(ssize_t) width) > (ssize_t) image->columns)
3179         width=image->columns-x;
3180       for (i=0; i < (ssize_t) width; i++)
3181       {
3182         SetPixelRed(image,GetPixelRed(texture,p),q);
3183         SetPixelGreen(image,GetPixelGreen(texture,p),q);
3184         SetPixelBlue(image,GetPixelBlue(texture,p),q);
3185         SetPixelAlpha(image,GetPixelAlpha(texture,p),q);
3186         if ((image->colorspace == CMYKColorspace)  &&
3187             (texture->colorspace == CMYKColorspace))
3188           SetPixelBlack(image,GetPixelBlack(texture,p),q);
3189         p+=GetPixelChannels(texture);
3190         q+=GetPixelChannels(image);
3191       }
3192     }
3193     sync=SyncCacheViewAuthenticPixels(image_view,exception);
3194     if (sync == MagickFalse)
3195       status=MagickFalse;
3196     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3197       {
3198         MagickBooleanType
3199           proceed;
3200
3201 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3202         #pragma omp critical (MagickCore_TextureImage)
3203 #endif
3204         proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3205           image->rows);
3206         if (proceed == MagickFalse)
3207           status=MagickFalse;
3208       }
3209   }
3210   texture_view=DestroyCacheView(texture_view);
3211   image_view=DestroyCacheView(image_view);
3212   return(status);
3213 }