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