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