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