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