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