MagickCore  6.9.13-3
Convert, Edit, Or Compose Bitmap Images
 All Data Structures
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-(double) y_offset) >= (double) 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) &&
2324  ((y-(double) y_offset) < (double) source_image->rows))
2325  {
2326  p=GetCacheViewVirtualPixels(source_view,0,
2327  CastDoubleToLong(y-(double) y_offset),source_image->columns,1,
2328  exception);
2329  if (p == (const PixelPacket *) NULL)
2330  {
2331  status=MagickFalse;
2332  continue;
2333  }
2334  pixels=p;
2335  if (x_offset < 0)
2336  p-=x_offset;
2337  }
2338  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2339  if (q == (PixelPacket *) NULL)
2340  {
2341  status=MagickFalse;
2342  continue;
2343  }
2344  indexes=GetCacheViewAuthenticIndexQueue(image_view);
2345  source_indexes=GetCacheViewVirtualIndexQueue(source_view);
2346  GetMagickPixelPacket(source_image,&source);
2347  GetMagickPixelPacket(image,&canvas);
2348  hue=0.0;
2349  chroma=0.0;
2350  luma=0.0;
2351  for (x=0; x < (ssize_t) image->columns; x++)
2352  {
2353  if (clip_to_self != MagickFalse)
2354  {
2355  if (x < x_offset)
2356  {
2357  q++;
2358  continue;
2359  }
2360  if ((x-(double) x_offset) >= (double) source_image->columns)
2361  break;
2362  }
2363  canvas.red=(MagickRealType) GetPixelRed(q);
2364  canvas.green=(MagickRealType) GetPixelGreen(q);
2365  canvas.blue=(MagickRealType) GetPixelBlue(q);
2366  if (image->matte != MagickFalse)
2367  canvas.opacity=(MagickRealType) GetPixelOpacity(q);
2368  if (image->colorspace == CMYKColorspace)
2369  canvas.index=(MagickRealType) GetPixelIndex(indexes+x);
2370  if (image->colorspace == CMYKColorspace)
2371  {
2372  canvas.red=(MagickRealType) QuantumRange-canvas.red;
2373  canvas.green=(MagickRealType) QuantumRange-canvas.green;
2374  canvas.blue=(MagickRealType) QuantumRange-canvas.blue;
2375  canvas.index=(MagickRealType) QuantumRange-canvas.index;
2376  }
2377  /*
2378  Handle canvas modifications outside overlaid region.
2379  */
2380  composite=canvas;
2381  if ((pixels == (PixelPacket *) NULL) || (x < x_offset) ||
2382  ((x-(double) x_offset) >= (double) source_image->columns))
2383  {
2384  switch (compose)
2385  {
2386  case DissolveCompositeOp:
2387  case BlendCompositeOp:
2388  {
2389  composite.opacity=(MagickRealType) ((MagickRealType) QuantumRange-
2390  canvas_dissolve*((MagickRealType) QuantumRange-
2391  composite.opacity));
2392  break;
2393  }
2394  case ClearCompositeOp:
2395  case SrcCompositeOp:
2396  {
2397  CompositeClear(&canvas,&composite);
2398  break;
2399  }
2400  case InCompositeOp:
2401  case SrcInCompositeOp:
2402  case OutCompositeOp:
2403  case SrcOutCompositeOp:
2404  case DstInCompositeOp:
2405  case DstAtopCompositeOp:
2406  case CopyOpacityCompositeOp:
2407  case ChangeMaskCompositeOp:
2408  {
2409  composite.opacity=(MagickRealType) TransparentOpacity;
2410  break;
2411  }
2412  default:
2413  {
2414  (void) GetOneVirtualMagickPixel(source_image,
2415  CastDoubleToLong(x-(double) x_offset),
2416  CastDoubleToLong(y-(double) y_offset),&composite,exception);
2417  break;
2418  }
2419  }
2420  if (image->colorspace == CMYKColorspace)
2421  {
2422  composite.red=(MagickRealType) QuantumRange-composite.red;
2423  composite.green=(MagickRealType) QuantumRange-composite.green;
2424  composite.blue=(MagickRealType) QuantumRange-composite.blue;
2425  composite.index=(MagickRealType) QuantumRange-composite.index;
2426  }
2427  SetPixelRed(q,clamp != MagickFalse ?
2428  ClampPixel(composite.red) : ClampToQuantum(composite.red));
2429  SetPixelGreen(q,clamp != MagickFalse ?
2430  ClampPixel(composite.green) : ClampToQuantum(composite.green));
2431  SetPixelBlue(q,clamp != MagickFalse ?
2432  ClampPixel(composite.blue) : ClampToQuantum(composite.blue));
2433  if (image->matte != MagickFalse)
2434  SetPixelOpacity(q,clamp != MagickFalse ?
2435  ClampPixel(composite.opacity) :
2436  ClampToQuantum(composite.opacity));
2437  if (image->colorspace == CMYKColorspace)
2438  SetPixelIndex(indexes+x,clamp != MagickFalse ?
2439  ClampPixel(composite.index) : ClampToQuantum(composite.index));
2440  q++;
2441  continue;
2442  }
2443  /*
2444  Handle normal overlay of source onto canvas.
2445  */
2446  source.red=(MagickRealType) GetPixelRed(p);
2447  source.green=(MagickRealType) GetPixelGreen(p);
2448  source.blue=(MagickRealType) GetPixelBlue(p);
2449  if (source_image->matte != MagickFalse)
2450  source.opacity=(MagickRealType) GetPixelOpacity(p);
2451  if (source_image->colorspace == CMYKColorspace)
2452  source.index=(MagickRealType) GetPixelIndex(source_indexes+
2453  CastDoubleToLong(x-(double) x_offset));
2454  if (source_image->colorspace == CMYKColorspace)
2455  {
2456  source.red=(MagickRealType) QuantumRange-source.red;
2457  source.green=(MagickRealType) QuantumRange-source.green;
2458  source.blue=(MagickRealType) QuantumRange-source.blue;
2459  source.index=(MagickRealType) QuantumRange-source.index;
2460  }
2461  switch (compose)
2462  {
2463  /* Duff-Porter Compositions */
2464  case ClearCompositeOp:
2465  {
2466  CompositeClear(&canvas,&composite);
2467  break;
2468  }
2469  case SrcCompositeOp:
2470  case CopyCompositeOp:
2471  case ReplaceCompositeOp:
2472  {
2473  composite=source;
2474  break;
2475  }
2476  case NoCompositeOp:
2477  case DstCompositeOp:
2478  break;
2479  case OverCompositeOp:
2480  case SrcOverCompositeOp:
2481  {
2482  MagickPixelCompositeOver(&source,source.opacity,&canvas,
2483  canvas.opacity,&composite);
2484  break;
2485  }
2486  case DstOverCompositeOp:
2487  {
2488  MagickPixelCompositeOver(&canvas,canvas.opacity,&source,
2489  source.opacity,&composite);
2490  break;
2491  }
2492  case SrcInCompositeOp:
2493  case InCompositeOp:
2494  {
2495  CompositeIn(&source,&canvas,&composite);
2496  break;
2497  }
2498  case DstInCompositeOp:
2499  {
2500  CompositeIn(&canvas,&source,&composite);
2501  break;
2502  }
2503  case OutCompositeOp:
2504  case SrcOutCompositeOp:
2505  {
2506  CompositeOut(&source,&canvas,&composite);
2507  break;
2508  }
2509  case DstOutCompositeOp:
2510  {
2511  CompositeOut(&canvas,&source,&composite);
2512  break;
2513  }
2514  case AtopCompositeOp:
2515  case SrcAtopCompositeOp:
2516  {
2517  CompositeAtop(&source,&canvas,&composite);
2518  break;
2519  }
2520  case DstAtopCompositeOp:
2521  {
2522  CompositeAtop(&canvas,&source,&composite);
2523  break;
2524  }
2525  case XorCompositeOp:
2526  {
2527  CompositeXor(&source,&canvas,&composite);
2528  break;
2529  }
2530  /* Mathematical Compositions */
2531  case PlusCompositeOp:
2532  {
2533  CompositePlus(&source,&canvas,channel,&composite);
2534  break;
2535  }
2536  case MinusDstCompositeOp:
2537  {
2538  CompositeMinus(&source,&canvas,channel,&composite);
2539  break;
2540  }
2541  case MinusSrcCompositeOp:
2542  {
2543  CompositeMinus(&canvas,&source,channel,&composite);
2544  break;
2545  }
2546  case ModulusAddCompositeOp:
2547  {
2548  CompositeModulusAdd(&source,&canvas,channel,&composite);
2549  break;
2550  }
2551  case ModulusSubtractCompositeOp:
2552  {
2553  CompositeModulusSubtract(&source,&canvas,channel,&composite);
2554  break;
2555  }
2556  case DifferenceCompositeOp:
2557  {
2558  CompositeDifference(&source,&canvas,channel,&composite);
2559  break;
2560  }
2561  case ExclusionCompositeOp:
2562  {
2563  CompositeExclusion(&source,&canvas,channel,&composite);
2564  break;
2565  }
2566  case MultiplyCompositeOp:
2567  {
2568  CompositeMultiply(&source,&canvas,channel,&composite);
2569  break;
2570  }
2571  case ScreenCompositeOp:
2572  {
2573  CompositeScreen(&source,&canvas,channel,&composite);
2574  break;
2575  }
2576  case DivideDstCompositeOp:
2577  {
2578  CompositeDivide(&source,&canvas,channel,&composite);
2579  break;
2580  }
2581  case DivideSrcCompositeOp:
2582  {
2583  CompositeDivide(&canvas,&source,channel,&composite);
2584  break;
2585  }
2586  case DarkenCompositeOp:
2587  {
2588  CompositeDarken(&source,&canvas,channel,&composite);
2589  break;
2590  }
2591  case LightenCompositeOp:
2592  {
2593  CompositeLighten(&source,&canvas,channel,&composite);
2594  break;
2595  }
2596  case DarkenIntensityCompositeOp:
2597  {
2598  CompositeDarkenIntensity(&source,&canvas,channel,&composite);
2599  break;
2600  }
2601  case LightenIntensityCompositeOp:
2602  {
2603  CompositeLightenIntensity(&source,&canvas,channel,&composite);
2604  break;
2605  }
2606  case MathematicsCompositeOp:
2607  {
2608  CompositeMathematics(&source,&canvas,channel,&geometry_info,
2609  &composite);
2610  break;
2611  }
2612  /* Lighting Compositions */
2613  case ColorDodgeCompositeOp:
2614  {
2615  CompositeColorDodge(&source,&canvas,&composite);
2616  break;
2617  }
2618  case ColorBurnCompositeOp:
2619  {
2620  CompositeColorBurn(&source,&canvas,&composite);
2621  break;
2622  }
2623  case LinearDodgeCompositeOp:
2624  {
2625  CompositeLinearDodge(&source,&canvas,&composite);
2626  break;
2627  }
2628  case LinearBurnCompositeOp:
2629  {
2630  CompositeLinearBurn(&source,&canvas,&composite);
2631  break;
2632  }
2633  case HardLightCompositeOp:
2634  {
2635  CompositeHardLight(&source,&canvas,&composite);
2636  break;
2637  }
2638  case HardMixCompositeOp:
2639  {
2640  CompositeHardMix(&source,&canvas,&composite);
2641  break;
2642  }
2643  case OverlayCompositeOp:
2644  {
2645  /* Overlay = Reversed HardLight. */
2646  CompositeHardLight(&canvas,&source,&composite);
2647  break;
2648  }
2649  case SoftLightCompositeOp:
2650  {
2651  CompositeSoftLight(&source,&canvas,&composite);
2652  break;
2653  }
2654  case LinearLightCompositeOp:
2655  {
2656  CompositeLinearLight(&source,&canvas,&composite);
2657  break;
2658  }
2659  case PegtopLightCompositeOp:
2660  {
2661  CompositePegtopLight(&source,&canvas,&composite);
2662  break;
2663  }
2664  case VividLightCompositeOp:
2665  {
2666  CompositeVividLight(&source,&canvas,&composite);
2667  break;
2668  }
2669  case PinLightCompositeOp:
2670  {
2671  CompositePinLight(&source,&canvas,&composite);
2672  break;
2673  }
2674  /* Other Composition */
2675  case ChangeMaskCompositeOp:
2676  {
2677  if ((composite.opacity > ((MagickRealType) QuantumRange/2.0)) ||
2678  (IsMagickColorSimilar(&source,&canvas) != MagickFalse))
2679  composite.opacity=(MagickRealType) TransparentOpacity;
2680  else
2681  composite.opacity=(MagickRealType) OpaqueOpacity;
2682  break;
2683  }
2684  case BumpmapCompositeOp:
2685  {
2686  if (source.opacity == (MagickRealType) TransparentOpacity)
2687  break;
2688  CompositeBumpmap(&source,&canvas,&composite);
2689  break;
2690  }
2691  case DissolveCompositeOp:
2692  {
2693  MagickPixelCompositeOver(&source,(MagickRealType) QuantumRange-
2694  source_dissolve*((MagickRealType) QuantumRange-source.opacity),
2695  &canvas,(MagickRealType) QuantumRange-canvas_dissolve*
2696  ((MagickRealType) QuantumRange-canvas.opacity),&composite);
2697  break;
2698  }
2699  case BlendCompositeOp:
2700  {
2701  MagickPixelCompositeBlend(&source,source_dissolve,&canvas,
2702  canvas_dissolve,&composite);
2703  break;
2704  }
2705  case StereoCompositeOp:
2706  {
2707  composite.red=(MagickRealType) GetPixelRed(p);
2708  composite.opacity=(composite.opacity+canvas.opacity/2);
2709  break;
2710  }
2711  case ThresholdCompositeOp:
2712  {
2713  CompositeThreshold(&source,&canvas,threshold,amount,&composite);
2714  break;
2715  }
2716  case ModulateCompositeOp:
2717  {
2718  ssize_t
2719  offset;
2720 
2721  if (source.opacity == (MagickRealType) TransparentOpacity)
2722  break;
2723  offset=(ssize_t) ((MagickRealType) MagickPixelIntensityToQuantum(
2724  &source)-(MagickRealType) midpoint);
2725  if (offset == 0)
2726  break;
2727  CompositeHCL(canvas.red,canvas.green,canvas.blue,&hue,
2728  &chroma,&luma);
2729  luma+=(0.01*percent_luma*offset)/midpoint;
2730  chroma*=0.01*percent_chroma;
2731  HCLComposite(hue,chroma,luma,&composite.red,&composite.green,
2732  &composite.blue);
2733  break;
2734  }
2735  case HueCompositeOp:
2736  {
2737  if (source.opacity == (MagickRealType) TransparentOpacity)
2738  break;
2739  if (canvas.opacity == (MagickRealType) TransparentOpacity)
2740  {
2741  composite=source;
2742  break;
2743  }
2744  CompositeHCL(canvas.red,canvas.green,canvas.blue,&hue,
2745  &chroma,&luma);
2746  CompositeHCL(source.red,source.green,source.blue,&hue,&sans,&sans);
2747  HCLComposite(hue,chroma,luma,&composite.red,
2748  &composite.green,&composite.blue);
2749  if (source.opacity < canvas.opacity)
2750  composite.opacity=source.opacity;
2751  break;
2752  }
2753  case SaturateCompositeOp:
2754  {
2755  if (source.opacity == (MagickRealType) TransparentOpacity)
2756  break;
2757  if (canvas.opacity == (MagickRealType) TransparentOpacity)
2758  {
2759  composite=source;
2760  break;
2761  }
2762  CompositeHCL(canvas.red,canvas.green,canvas.blue,&hue,
2763  &chroma,&luma);
2764  CompositeHCL(source.red,source.green,source.blue,&sans,&chroma,
2765  &sans);
2766  HCLComposite(hue,chroma,luma,&composite.red,
2767  &composite.green,&composite.blue);
2768  if (source.opacity < canvas.opacity)
2769  composite.opacity=source.opacity;
2770  break;
2771  }
2772  case LuminizeCompositeOp:
2773  {
2774  if (source.opacity == (MagickRealType) TransparentOpacity)
2775  break;
2776  if (canvas.opacity == (MagickRealType) TransparentOpacity)
2777  {
2778  composite=source;
2779  break;
2780  }
2781  CompositeHCL(canvas.red,canvas.green,canvas.blue,&hue,
2782  &chroma,&luma);
2783  CompositeHCL(source.red,source.green,source.blue,&sans,&sans,
2784  &luma);
2785  HCLComposite(hue,chroma,luma,&composite.red,
2786  &composite.green,&composite.blue);
2787  if (source.opacity < canvas.opacity)
2788  composite.opacity=source.opacity;
2789  break;
2790  }
2791  case ColorizeCompositeOp:
2792  {
2793  if (source.opacity == (MagickRealType) TransparentOpacity)
2794  break;
2795  if (canvas.opacity == (MagickRealType) TransparentOpacity)
2796  {
2797  composite=source;
2798  break;
2799  }
2800  CompositeHCL(canvas.red,canvas.green,canvas.blue,&sans,
2801  &sans,&luma);
2802  CompositeHCL(source.red,source.green,source.blue,&hue,&chroma,&sans);
2803  HCLComposite(hue,chroma,luma,&composite.red,
2804  &composite.green,&composite.blue);
2805  if (source.opacity < canvas.opacity)
2806  composite.opacity=source.opacity;
2807  break;
2808  }
2809  case CopyRedCompositeOp:
2810  case CopyCyanCompositeOp:
2811  {
2812  composite.red=source.red;
2813  break;
2814  }
2815  case CopyGreenCompositeOp:
2816  case CopyMagentaCompositeOp:
2817  {
2818  composite.green=source.green;
2819  break;
2820  }
2821  case CopyBlueCompositeOp:
2822  case CopyYellowCompositeOp:
2823  {
2824  composite.blue=source.blue;
2825  break;
2826  }
2827  case CopyOpacityCompositeOp:
2828  {
2829  if (source.matte == MagickFalse)
2830  composite.opacity=(MagickRealType) (QuantumRange-
2831  MagickPixelIntensityToQuantum(&source));
2832  else
2833  composite.opacity=source.opacity;
2834  break;
2835  }
2836  case CopyBlackCompositeOp:
2837  {
2838  if (source.colorspace != CMYKColorspace)
2839  ConvertRGBToCMYK(&source);
2840  composite.index=source.index;
2841  break;
2842  }
2843  /* compose methods that are already handled */
2844  case BlurCompositeOp:
2845  case DisplaceCompositeOp:
2846  case DistortCompositeOp:
2847  {
2848  composite=source;
2849  break;
2850  }
2851  default:
2852  break;
2853  }
2854  if (image->colorspace == CMYKColorspace)
2855  {
2856  composite.red=(MagickRealType) QuantumRange-composite.red;
2857  composite.green=(MagickRealType) QuantumRange-composite.green;
2858  composite.blue=(MagickRealType) QuantumRange-composite.blue;
2859  composite.index=(MagickRealType) QuantumRange-composite.index;
2860  }
2861  SetPixelRed(q,clamp != MagickFalse ?
2862  ClampPixel(composite.red) : ClampToQuantum(composite.red));
2863  SetPixelGreen(q,clamp != MagickFalse ?
2864  ClampPixel(composite.green) : ClampToQuantum(composite.green));
2865  SetPixelBlue(q,clamp != MagickFalse ?
2866  ClampPixel(composite.blue) : ClampToQuantum(composite.blue));
2867  SetPixelOpacity(q,clamp != MagickFalse ?
2868  ClampPixel(composite.opacity) : ClampToQuantum(composite.opacity));
2869  if (image->colorspace == CMYKColorspace)
2870  SetPixelIndex(indexes+x,clamp != MagickFalse ?
2871  ClampPixel(composite.index) : ClampToQuantum(composite.index));
2872  p++;
2873  if (p >= (pixels+source_image->columns))
2874  p=pixels;
2875  q++;
2876  }
2877  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2878  status=MagickFalse;
2879  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2880  {
2881  MagickBooleanType
2882  proceed;
2883 
2884 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2885  #pragma omp atomic
2886 #endif
2887  progress++;
2888  proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
2889  if (proceed == MagickFalse)
2890  status=MagickFalse;
2891  }
2892  }
2893  source_view=DestroyCacheView(source_view);
2894  image_view=DestroyCacheView(image_view);
2895  if (canvas_image != (Image * ) NULL)
2896  canvas_image=DestroyImage(canvas_image);
2897  else
2898  source_image=DestroyImage(source_image);
2899  return(status);
2900 }
2901 
2902 /*
2903 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2904 % %
2905 % %
2906 % %
2907 % T e x t u r e I m a g e %
2908 % %
2909 % %
2910 % %
2911 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2912 %
2913 % TextureImage() repeatedly tiles the texture image across and down the image
2914 % canvas.
2915 %
2916 % The format of the TextureImage method is:
2917 %
2918 % MagickBooleanType TextureImage(Image *image,const Image *texture)
2919 %
2920 % A description of each parameter follows:
2921 %
2922 % o image: the image.
2923 %
2924 % o texture: This image is the texture to layer on the background.
2925 %
2926 */
2927 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
2928 {
2929 #define TextureImageTag "Texture/Image"
2930 
2931  CacheView
2932  *image_view,
2933  *texture_view;
2934 
2936  *exception;
2937 
2938  Image
2939  *texture_image;
2940 
2941  MagickBooleanType
2942  status;
2943 
2944  ssize_t
2945  y;
2946 
2947  assert(image != (Image *) NULL);
2948  assert(image->signature == MagickCoreSignature);
2949  if (IsEventLogging() != MagickFalse)
2950  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2951  if (texture == (const Image *) NULL)
2952  return(MagickFalse);
2953  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2954  return(MagickFalse);
2955  exception=(&image->exception);
2956  texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2957  if (texture_image == (const Image *) NULL)
2958  return(MagickFalse);
2959  (void) TransformImageColorspace(texture_image,image->colorspace);
2960  (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod);
2961  status=MagickTrue;
2962  if ((image->compose != CopyCompositeOp) &&
2963  ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2964  (texture_image->matte != MagickFalse)))
2965  {
2966  /*
2967  Tile texture onto the image background.
2968  */
2969  for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2970  {
2971  ssize_t
2972  x;
2973 
2974  if (status == MagickFalse)
2975  continue;
2976  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2977  {
2978  MagickBooleanType
2979  thread_status;
2980 
2981  thread_status=CompositeImage(image,image->compose,texture_image,x+
2982  texture_image->tile_offset.x,y+texture_image->tile_offset.y);
2983  if (thread_status == MagickFalse)
2984  {
2985  status=thread_status;
2986  break;
2987  }
2988  }
2989  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2990  {
2991  MagickBooleanType
2992  proceed;
2993 
2994  proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2995  y,image->rows);
2996  if (proceed == MagickFalse)
2997  status=MagickFalse;
2998  }
2999  }
3000  (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
3001  image->rows,image->rows);
3002  texture_image=DestroyImage(texture_image);
3003  return(status);
3004  }
3005  /*
3006  Tile texture onto the image background (optimized).
3007  */
3008  status=MagickTrue;
3009  texture_view=AcquireVirtualCacheView(texture_image,exception);
3010  image_view=AcquireAuthenticCacheView(image,exception);
3011 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3012  #pragma omp parallel for schedule(static) shared(status) \
3013  magick_number_threads(image,texture_image,image->rows,1)
3014 #endif
3015  for (y=0; y < (ssize_t) image->rows; y++)
3016  {
3017  MagickBooleanType
3018  sync;
3019 
3020  const IndexPacket
3021  *texture_indexes;
3022 
3023  const PixelPacket
3024  *p;
3025 
3026  IndexPacket
3027  *indexes;
3028 
3029  ssize_t
3030  x;
3031 
3032  PixelPacket
3033  *q;
3034 
3035  size_t
3036  width;
3037 
3038  if (status == MagickFalse)
3039  continue;
3040  p=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,(y+
3041  texture_image->tile_offset.y) % texture_image->rows,
3042  texture_image->columns,1,exception);
3043  q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3044  exception);
3045  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3046  {
3047  status=MagickFalse;
3048  continue;
3049  }
3050  texture_indexes=GetCacheViewVirtualIndexQueue(texture_view);
3051  indexes=GetCacheViewAuthenticIndexQueue(image_view);
3052  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
3053  {
3054  width=texture_image->columns;
3055  if ((x+(ssize_t) width) > (ssize_t) image->columns)
3056  width=image->columns-x;
3057  (void) memcpy(q,p,width*sizeof(*p));
3058  if ((image->colorspace == CMYKColorspace) &&
3059  (texture_image->colorspace == CMYKColorspace))
3060  {
3061  (void) memcpy(indexes,texture_indexes,width*
3062  sizeof(*indexes));
3063  indexes+=width;
3064  }
3065  q+=width;
3066  }
3067  sync=SyncCacheViewAuthenticPixels(image_view,exception);
3068  if (sync == MagickFalse)
3069  status=MagickFalse;
3070  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3071  {
3072  MagickBooleanType
3073  proceed;
3074 
3075  proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3076  image->rows);
3077  if (proceed == MagickFalse)
3078  status=MagickFalse;
3079  }
3080  }
3081  texture_view=DestroyCacheView(texture_view);
3082  image_view=DestroyCacheView(image_view);
3083  texture_image=DestroyImage(texture_image);
3084  return(status);
3085 }
Definition: image.h:152