MagickCore  6.9.12-90
Convert, Edit, Or Compose Bitmap Images
 All Data Structures
composite.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
7 % C O O MM MM P P O O SS I T E %
8 % C O O M M M PPPP O O SSS I T EEE %
9 % C O O M M P O O SS I T E %
10 % CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
11 % %
12 % %
13 % MagickCore Image Composite Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 
40 /*
41  Include declarations.
42 */
43 #include "magick/studio.h"
44 #include "magick/accelerate-private.h"
45 #include "magick/artifact.h"
46 #include "magick/cache-view.h"
47 #include "magick/channel.h"
48 #include "magick/client.h"
49 #include "magick/color.h"
50 #include "magick/color-private.h"
51 #include "magick/colorspace.h"
52 #include "magick/colorspace-private.h"
53 #include "magick/composite.h"
54 #include "magick/composite-private.h"
55 #include "magick/constitute.h"
56 #include "magick/draw.h"
57 #include "magick/fx.h"
58 #include "magick/gem.h"
59 #include "magick/geometry.h"
60 #include "magick/image.h"
61 #include "magick/image-private.h"
62 #include "magick/list.h"
63 #include "magick/log.h"
64 #include "magick/monitor.h"
65 #include "magick/monitor-private.h"
66 #include "magick/memory_.h"
67 #include "magick/option.h"
68 #include "magick/pixel-private.h"
69 #include "magick/property.h"
70 #include "magick/quantum.h"
71 #include "magick/resample.h"
72 #include "magick/resource_.h"
73 #include "magick/string_.h"
74 #include "magick/thread-private.h"
75 #include "magick/threshold.h"
76 #include "magick/token.h"
77 #include "magick/utility.h"
78 #include "magick/version.h"
79 
80 /*
81 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 % %
83 % %
84 % %
85 % C o m p o s i t e I m a g e C h a n n e l %
86 % %
87 % %
88 % %
89 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 %
91 % CompositeImageChannel() returns the second image composited onto the first
92 % at the specified offset, using the specified composite method.
93 %
94 % The format of the CompositeImageChannel method is:
95 %
96 % MagickBooleanType CompositeImage(Image *image,
97 % const CompositeOperator compose,Image *source_image,
98 % const ssize_t x_offset,const ssize_t y_offset)
99 % MagickBooleanType CompositeImageChannel(Image *image,
100 % const ChannelType channel,const CompositeOperator compose,
101 % Image *source_image,const ssize_t x_offset,const ssize_t y_offset)
102 %
103 % A description of each parameter follows:
104 %
105 % o image: the canvas image, modified by he composition
106 %
107 % o channel: the channel.
108 %
109 % o compose: This operator affects how the composite is applied to
110 % the image. The operators and how they are utilized are listed here
111 % http://www.w3.org/TR/SVG12/#compositing.
112 %
113 % o source_image: the composite (source) image.
114 %
115 % o x_offset: the column offset of the composited image.
116 %
117 % o y_offset: the row offset of the composited image.
118 %
119 % Extra Controls from Image meta-data in 'source_image' (artifacts)
120 %
121 % o "compose:args"
122 % A string containing extra numerical arguments for specific compose
123 % methods, generally expressed as a 'geometry' or a comma separated list
124 % of numbers.
125 %
126 % Compose methods needing such arguments include "BlendCompositeOp" and
127 % "DisplaceCompositeOp".
128 %
129 % o "compose:outside-overlay"
130 % Modify how the composition is to effect areas not directly covered
131 % by the 'source_image' at the offset given. Normally this is
132 % dependant on the 'compose' method, especially Duff-Porter methods.
133 %
134 % If set to "false" then disable all normal handling of pixels not
135 % covered by the source_image. Typically used for repeated tiling
136 % of the source_image by the calling API.
137 %
138 % Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
139 %
140 */
141 
142 /*
143 ** Programmers notes on SVG specification.
144 **
145 ** A Composition is defined by...
146 ** Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
147 ** Blending areas : X = 1 for area of overlap ie: f(Sc,Dc)
148 ** Y = 1 for source preserved
149 ** Z = 1 for canvas preserved
150 **
151 ** Conversion to transparency (then optimized)
152 ** Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
153 ** Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
154 **
155 ** Where...
156 ** Sca = Sc*Sa normalized Source color divided by Source alpha
157 ** Dca = Dc*Da normalized Dest color divided by Dest alpha
158 ** Dc' = Dca'/Da' the desired color value for this channel.
159 **
160 ** Da' in in the follow formula as 'gamma' The resulting alpla value.
161 **
162 **
163 ** Most functions use a blending mode of over (X=1,Y=1,Z=1)
164 ** this results in the following optimizations...
165 ** gamma = Sa+Da-Sa*Da;
166 ** gamma = 1 - QuantumScale*alpha * QuantumScale*beta;
167 ** opacity = QuantumScale*alpha*beta; // over blend, optimized 1-Gamma
168 **
169 ** The above SVG definitions also define that Mathematical Composition
170 ** methods should use a 'Over' blending mode for Alpha Channel.
171 ** It however was not applied for composition modes of 'Plus', 'Minus',
172 ** the modulus versions of 'Add' and 'Subtract'.
173 **
174 **
175 ** Mathematical operator changes to be applied from IM v6.7...
176 **
177 ** 1/ Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
178 ** 'ModulusAdd' and 'ModulusSubtract' for clarity.
179 **
180 ** 2/ All mathematical compositions work as per the SVG specification
181 ** with regard to blending. This now includes 'ModulusAdd' and
182 ** 'ModulusSubtract'.
183 **
184 ** 3/ When the special channel flag 'sync' (syncronize channel updates)
185 ** is turned off (enabled by default) then mathematical compositions are
186 ** only performed on the channels specified, and are applied
187 ** independantally of each other. In other words the mathematics is
188 ** performed as 'pure' mathematical operations, rather than as image
189 ** operations.
190 */
191 
192 static inline MagickRealType Atop(const MagickRealType p,
193  const MagickRealType Sa,const MagickRealType q,
194  const MagickRealType magick_unused(Da))
195 {
196  magick_unreferenced(Da);
197 
198  return(p*Sa+q*(1.0-Sa)); /* Da optimized out, Da/gamma => 1.0 */
199 }
200 
201 static inline void CompositeAtop(const MagickPixelPacket *p,
202  const MagickPixelPacket *q,MagickPixelPacket *composite)
203 {
204  MagickRealType
205  Sa;
206 
207  Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
208  composite->opacity=q->opacity; /* optimized Da = 1.0-Gamma */
209  composite->red=Atop(p->red,Sa,q->red,1.0);
210  composite->green=Atop(p->green,Sa,q->green,1.0);
211  composite->blue=Atop(p->blue,Sa,q->blue,1.0);
212  if (q->colorspace == CMYKColorspace)
213  composite->index=Atop(p->index,Sa,q->index,1.0);
214 }
215 
216 /*
217  What is this Composition method for? Can't find any specification!
218  WARNING this is not doing correct 'over' blend handling (Anthony Thyssen).
219 */
220 static inline void CompositeBumpmap(const MagickPixelPacket *p,
221  const MagickPixelPacket *q,MagickPixelPacket *composite)
222 {
223  MagickRealType
224  intensity;
225 
226  intensity=MagickPixelIntensity(p);
227  composite->red=QuantumScale*intensity*q->red;
228  composite->green=QuantumScale*intensity*q->green;
229  composite->blue=QuantumScale*intensity*q->blue;
230  composite->opacity=(MagickRealType) QuantumScale*intensity*p->opacity;
231  if (q->colorspace == CMYKColorspace)
232  composite->index=QuantumScale*intensity*q->index;
233 }
234 
235 static inline void CompositeClear(const MagickPixelPacket *q,
236  MagickPixelPacket *composite)
237 {
238  composite->opacity=(MagickRealType) TransparentOpacity;
239  composite->red=0.0;
240  composite->green=0.0;
241  composite->blue=0.0;
242  if (q->colorspace == CMYKColorspace)
243  composite->index=0.0;
244 }
245 
246 static MagickRealType ColorBurn(const MagickRealType Sca,
247  const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
248 {
249  double
250  SaSca;
251 
252  if ((fabs((double) Sca) < MagickEpsilon) &&
253  (fabs((double) (Dca-Da)) < MagickEpsilon))
254  return(Sa*Da+Dca*(1.0-Sa));
255  if (Sca < MagickEpsilon)
256  return(Dca*(1.0-Sa));
257  SaSca=Sa*PerceptibleReciprocal(Sca);
258  return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*SaSca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
259 }
260 
261 static inline void CompositeColorBurn(const MagickPixelPacket *p,
262  const MagickPixelPacket *q,MagickPixelPacket *composite)
263 {
264  MagickRealType
265  Da,
266  gamma,
267  Sa;
268 
269  Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
270  Da=1.0-QuantumScale*q->opacity;
271  gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
272  composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
273  gamma=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  magick_fallthrough;
1703  }
1704  case CopyCompositeOp:
1705  {
1706  if ((x_offset < 0) || (y_offset < 0))
1707  break;
1708  if ((x_offset+(ssize_t) source_image->columns) >= (ssize_t) image->columns)
1709  break;
1710  if ((y_offset+(ssize_t) source_image->rows) >= (ssize_t) image->rows)
1711  break;
1712  status=MagickTrue;
1713  source_view=AcquireVirtualCacheView(source_image,exception);
1714  image_view=AcquireAuthenticCacheView(image,exception);
1715 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1716  #pragma omp parallel for schedule(static) shared(status) \
1717  magick_number_threads(source_image,image,source_image->rows,1)
1718 #endif
1719  for (y=0; y < (ssize_t) source_image->rows; y++)
1720  {
1721  MagickBooleanType
1722  sync;
1723 
1724  const IndexPacket
1725  *source_indexes;
1726 
1727  const PixelPacket
1728  *p;
1729 
1730  IndexPacket
1731  *indexes;
1732 
1733  PixelPacket
1734  *q;
1735 
1736  if (status == MagickFalse)
1737  continue;
1738  p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,
1739  1,exception);
1740  q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1741  source_image->columns,1,exception);
1742  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1743  {
1744  status=MagickFalse;
1745  continue;
1746  }
1747  source_indexes=GetCacheViewVirtualIndexQueue(source_view);
1748  indexes=GetCacheViewAuthenticIndexQueue(image_view);
1749  (void) memcpy(q,p,source_image->columns*sizeof(*p));
1750  if ((indexes != (IndexPacket *) NULL) &&
1751  (source_indexes != (const IndexPacket *) NULL))
1752  (void) memcpy(indexes,source_indexes,
1753  source_image->columns*sizeof(*indexes));
1754  sync=SyncCacheViewAuthenticPixels(image_view,exception);
1755  if (sync == MagickFalse)
1756  status=MagickFalse;
1757  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1758  {
1759  MagickBooleanType
1760  proceed;
1761 
1762  proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
1763  y,image->rows);
1764  if (proceed == MagickFalse)
1765  status=MagickFalse;
1766  }
1767  }
1768  source_view=DestroyCacheView(source_view);
1769  image_view=DestroyCacheView(image_view);
1770  source_image=DestroyImage(source_image);
1771  return(status);
1772  }
1773  case CopyOpacityCompositeOp:
1774  case ChangeMaskCompositeOp:
1775  {
1776  /*
1777  Modify canvas outside the overlaid region and require an alpha
1778  channel to exist, to add transparency.
1779  */
1780  if (image->matte == MagickFalse)
1781  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1782  clip_to_self=MagickFalse;
1783  break;
1784  }
1785  case BlurCompositeOp:
1786  {
1787  CacheView
1788  *canvas_view,
1789  *source_view;
1790 
1792  pixel;
1793 
1794  MagickRealType
1795  angle_range,
1796  angle_start,
1797  height,
1798  width;
1799 
1801  *resample_filter;
1802 
1803  SegmentInfo
1804  blur;
1805 
1806  /*
1807  Blur Image by resampling.
1808 
1809  Blur Image dictated by an overlay gradient map: X = red_channel;
1810  Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
1811  */
1812  canvas_image=CloneImage(image,0,0,MagickTrue,exception);
1813  if (canvas_image == (Image *) NULL)
1814  {
1815  source_image=DestroyImage(source_image);
1816  return(MagickFalse);
1817  }
1818  /*
1819  Gather the maximum blur sigma values from user.
1820  */
1821  SetGeometryInfo(&geometry_info);
1822  flags=NoValue;
1823  value=GetImageArtifact(image,"compose:args");
1824  if (value != (char *) NULL)
1825  flags=ParseGeometry(value,&geometry_info);
1826  if ((flags & WidthValue) == 0)
1827  {
1828  (void) ThrowMagickException(exception,GetMagickModule(),
1829  OptionWarning,"InvalidGeometry","'%s' '%s'","compose:args",value);
1830  source_image=DestroyImage(source_image);
1831  canvas_image=DestroyImage(canvas_image);
1832  return(MagickFalse);
1833  }
1834  /*
1835  Users input sigma now needs to be converted to the EWA ellipse size.
1836  The filter defaults to a sigma of 0.5 so to make this match the
1837  users input the ellipse size needs to be doubled.
1838  */
1839  width=height=geometry_info.rho*2.0;
1840  if ((flags & HeightValue) != 0 )
1841  height=geometry_info.sigma*2.0;
1842 
1843  /* default the unrotated ellipse width and height axis vectors */
1844  blur.x1=width;
1845  blur.x2=0.0;
1846  blur.y1=0.0;
1847  blur.y2=height;
1848  /* rotate vectors if a rotation angle is given */
1849  if ((flags & XValue) != 0 )
1850  {
1851  MagickRealType
1852  angle;
1853 
1854  angle=DegreesToRadians(geometry_info.xi);
1855  blur.x1=width*cos(angle);
1856  blur.x2=width*sin(angle);
1857  blur.y1=(-height*sin(angle));
1858  blur.y2=height*cos(angle);
1859  }
1860  /* Otherwise lets set a angle range and calculate in the loop */
1861  angle_start=0.0;
1862  angle_range=0.0;
1863  if ((flags & YValue) != 0 )
1864  {
1865  angle_start=DegreesToRadians(geometry_info.xi);
1866  angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1867  }
1868  /*
1869  Set up a gaussian cylindrical filter for EWA Blurring.
1870 
1871  As the minimum ellipse radius of support*1.0 the EWA algorithm
1872  can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
1873  This means that even 'No Blur' will be still a little blurry!
1874 
1875  The solution (as well as the problem of preventing any user
1876  expert filter settings, is to set our own user settings, then
1877  restore them afterwards.
1878  */
1879  resample_filter=AcquireResampleFilter(image,exception);
1880  SetResampleFilter(resample_filter,GaussianFilter,1.0);
1881 
1882  /* do the variable blurring of each pixel in image */
1883  pixel=zero;
1884  source_view=AcquireVirtualCacheView(source_image,exception);
1885  canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1886  for (y=0; y < (ssize_t) source_image->rows; y++)
1887  {
1888  MagickBooleanType
1889  sync;
1890 
1891  const PixelPacket
1892  *magick_restrict p;
1893 
1894  PixelPacket
1895  *magick_restrict r;
1896 
1897  IndexPacket
1898  *magick_restrict canvas_indexes;
1899 
1900  ssize_t
1901  x;
1902 
1903  if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1904  continue;
1905  p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,
1906  1,exception);
1907  r=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,
1908  1,exception);
1909  if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
1910  break;
1911  canvas_indexes=GetCacheViewAuthenticIndexQueue(canvas_view);
1912  for (x=0; x < (ssize_t) source_image->columns; x++)
1913  {
1914  if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1915  {
1916  p++;
1917  continue;
1918  }
1919  if (fabs((double) angle_range) > MagickEpsilon)
1920  {
1921  MagickRealType
1922  angle;
1923 
1924  angle=angle_start+angle_range*QuantumScale*GetPixelBlue(p);
1925  blur.x1=width*cos(angle);
1926  blur.x2=width*sin(angle);
1927  blur.y1=(-height*sin(angle));
1928  blur.y2=height*cos(angle);
1929  }
1930 #if 0
1931  if ( x == 10 && y == 60 ) {
1932  fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",
1933  blur.x1, blur.x2, blur.y1, blur.y2);
1934  fprintf(stderr, "scaled by=%lf,%lf\n",
1935  QuantumScale*GetPixelRed(p), QuantumScale*GetPixelGreen(p));
1936  }
1937 #endif
1938  ScaleResampleFilter(resample_filter,
1939  blur.x1*QuantumScale*GetPixelRed(p),
1940  blur.y1*QuantumScale*GetPixelGreen(p),
1941  blur.x2*QuantumScale*GetPixelRed(p),
1942  blur.y2*QuantumScale*GetPixelGreen(p));
1943  (void) ResamplePixelColor(resample_filter,(double) x_offset+x,(double)
1944  y_offset+y,&pixel);
1945  SetPixelPacket(canvas_image,&pixel,r,canvas_indexes+x);
1946  p++;
1947  r++;
1948  }
1949  sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1950  if (sync == MagickFalse)
1951  break;
1952  }
1953  resample_filter=DestroyResampleFilter(resample_filter);
1954  source_view=DestroyCacheView(source_view);
1955  canvas_view=DestroyCacheView(canvas_view);
1956  source_image=DestroyImage(source_image);
1957  source_image=canvas_image;
1958  break;
1959  }
1960  case DisplaceCompositeOp:
1961  case DistortCompositeOp:
1962  {
1963  CacheView
1964  *canvas_view,
1965  *source_view,
1966  *image_view;
1967 
1969  pixel;
1970 
1971  MagickRealType
1972  horizontal_scale,
1973  vertical_scale;
1974 
1975  PointInfo
1976  center,
1977  offset;
1978 
1979  IndexPacket
1980  *magick_restrict canvas_indexes;
1981 
1982  PixelPacket
1983  *magick_restrict r;
1984 
1985  /*
1986  Displace/Distort based on overlay gradient map:
1987  X = red_channel; Y = green_channel;
1988  compose:args = x_scale[,y_scale[,center.x,center.y]]
1989  */
1990  canvas_image=CloneImage(image,0,0,MagickTrue,exception);
1991  if (canvas_image == (Image *) NULL)
1992  {
1993  source_image=DestroyImage(source_image);
1994  return(MagickFalse);
1995  }
1996  SetGeometryInfo(&geometry_info);
1997  flags=NoValue;
1998  value=GetImageArtifact(image,"compose:args");
1999  if (value != (char *) NULL)
2000  flags=ParseGeometry(value,&geometry_info);
2001  if ((flags & (WidthValue | HeightValue)) == 0 )
2002  {
2003  if ((flags & AspectValue) == 0)
2004  {
2005  horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
2006  vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
2007  }
2008  else
2009  {
2010  horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
2011  vertical_scale=(MagickRealType) (image->rows-1)/2.0;
2012  }
2013  }
2014  else
2015  {
2016  horizontal_scale=geometry_info.rho;
2017  vertical_scale=geometry_info.sigma;
2018  if ((flags & PercentValue) != 0)
2019  {
2020  if ((flags & AspectValue) == 0)
2021  {
2022  horizontal_scale*=(source_image->columns-1)/200.0;
2023  vertical_scale*=(source_image->rows-1)/200.0;
2024  }
2025  else
2026  {
2027  horizontal_scale*=(image->columns-1)/200.0;
2028  vertical_scale*=(image->rows-1)/200.0;
2029  }
2030  }
2031  if ((flags & HeightValue) == 0)
2032  vertical_scale=horizontal_scale;
2033  }
2034  /*
2035  Determine fixed center point for absolute distortion map
2036  Absolute distort ==
2037  Displace offset relative to a fixed absolute point
2038  Select that point according to +X+Y user inputs.
2039  default = center of overlay image
2040  arg flag '!' = locations/percentage relative to background image
2041  */
2042  center.x=(MagickRealType) x_offset;
2043  center.y=(MagickRealType) y_offset;
2044  if (compose == DistortCompositeOp)
2045  {
2046  if ((flags & XValue) == 0)
2047  if ((flags & AspectValue) != 0)
2048  center.x=((MagickRealType) image->columns-1)/2.0;
2049  else
2050  center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
2051  2.0);
2052  else
2053  if ((flags & AspectValue) == 0)
2054  center.x=(MagickRealType) (x_offset+geometry_info.xi);
2055  else
2056  center.x=geometry_info.xi;
2057  if ((flags & YValue) == 0)
2058  if ((flags & AspectValue) != 0)
2059  center.y=((MagickRealType) image->rows-1)/2.0;
2060  else
2061  center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
2062  else
2063  if ((flags & AspectValue) != 0)
2064  center.y=geometry_info.psi;
2065  else
2066  center.y=(MagickRealType) (y_offset+geometry_info.psi);
2067  }
2068  /*
2069  Shift the pixel offset point as defined by the provided,
2070  displacement/distortion map. -- Like a lens...
2071  */
2072  pixel=zero;
2073  image_view=AcquireVirtualCacheView(image,exception);
2074  source_view=AcquireVirtualCacheView(source_image,exception);
2075  canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
2076  for (y=0; y < (ssize_t) source_image->rows; y++)
2077  {
2078  MagickBooleanType
2079  sync;
2080 
2081  const PixelPacket
2082  *magick_restrict p;
2083 
2084  ssize_t
2085  x;
2086 
2087  if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
2088  continue;
2089  p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,
2090  1,exception);
2091  r=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,
2092  1,exception);
2093  if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
2094  break;
2095  canvas_indexes=GetCacheViewAuthenticIndexQueue(canvas_view);
2096  for (x=0; x < (ssize_t) source_image->columns; x++)
2097  {
2098  if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
2099  {
2100  p++;
2101  continue;
2102  }
2103  /*
2104  Displace the offset.
2105  */
2106  offset.x=(double) ((horizontal_scale*(GetPixelRed(p)-
2107  (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2108  QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
2109  x : 0));
2110  offset.y=(double) ((vertical_scale*(GetPixelGreen(p)-
2111  (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2112  QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
2113  y : 0));
2114  status=InterpolateMagickPixelPacket(image,image_view,
2115  UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2116  &pixel,exception);
2117  if (status == MagickFalse)
2118  break;
2119  /*
2120  Mask with the 'invalid pixel mask' in alpha channel.
2121  */
2122  pixel.opacity=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
2123  pixel.opacity)*(1.0-QuantumScale*GetPixelOpacity(p)));
2124  SetPixelPacket(canvas_image,&pixel,r,canvas_indexes+x);
2125  p++;
2126  r++;
2127  }
2128  if (x < (ssize_t) source_image->columns)
2129  break;
2130  sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
2131  if (sync == MagickFalse)
2132  break;
2133  }
2134  canvas_view=DestroyCacheView(canvas_view);
2135  source_view=DestroyCacheView(source_view);
2136  image_view=DestroyCacheView(image_view);
2137  source_image=DestroyImage(source_image);
2138  source_image=canvas_image;
2139  break;
2140  }
2141  case DissolveCompositeOp:
2142  {
2143  /*
2144  Geometry arguments to dissolve factors.
2145  */
2146  value=GetImageArtifact(image,"compose:args");
2147  if (value != (char *) NULL)
2148  {
2149  flags=ParseGeometry(value,&geometry_info);
2150  source_dissolve=geometry_info.rho/100.0;
2151  canvas_dissolve=1.0;
2152  if ((source_dissolve-MagickEpsilon) < 0.0)
2153  source_dissolve=0.0;
2154  if ((source_dissolve+MagickEpsilon) > 1.0)
2155  {
2156  canvas_dissolve=2.0-source_dissolve;
2157  source_dissolve=1.0;
2158  }
2159  if ((flags & SigmaValue) != 0)
2160  canvas_dissolve=geometry_info.sigma/100.0;
2161  if ((canvas_dissolve-MagickEpsilon) < 0.0)
2162  canvas_dissolve=0.0;
2163  clip_to_self=MagickFalse;
2164  if ((canvas_dissolve+MagickEpsilon) > 1.0 )
2165  {
2166  canvas_dissolve=1.0;
2167  clip_to_self=MagickTrue;
2168  }
2169  }
2170  break;
2171  }
2172  case BlendCompositeOp:
2173  {
2174  value=GetImageArtifact(image,"compose:args");
2175  if (value != (char *) NULL)
2176  {
2177  flags=ParseGeometry(value,&geometry_info);
2178  source_dissolve=geometry_info.rho/100.0;
2179  canvas_dissolve=1.0-source_dissolve;
2180  if ((flags & SigmaValue) != 0)
2181  canvas_dissolve=geometry_info.sigma/100.0;
2182  clip_to_self=MagickFalse;
2183  if ((canvas_dissolve+MagickEpsilon) > 1.0)
2184  clip_to_self=MagickTrue;
2185  }
2186  break;
2187  }
2188  case MathematicsCompositeOp:
2189  {
2190  /*
2191  Just collect the values from "compose:args", setting.
2192  Unused values are set to zero automagically.
2193 
2194  Arguments are normally a comma separated list, so this probably should
2195  be changed to some 'general comma list' parser, (with a minimum
2196  number of values)
2197  */
2198  SetGeometryInfo(&geometry_info);
2199  value=GetImageArtifact(image,"compose:args");
2200  if (value != (char *) NULL)
2201  {
2202  flags=ParseGeometry(value,&geometry_info);
2203  if (flags == NoValue)
2204  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2205  "InvalidGeometry","`%s'",value);
2206  }
2207  break;
2208  }
2209  case ModulateCompositeOp:
2210  {
2211  /*
2212  Determine the luma and chroma scale.
2213  */
2214  value=GetImageArtifact(image,"compose:args");
2215  if (value != (char *) NULL)
2216  {
2217  flags=ParseGeometry(value,&geometry_info);
2218  percent_luma=geometry_info.rho;
2219  if ((flags & SigmaValue) != 0)
2220  percent_chroma=geometry_info.sigma;
2221  }
2222  break;
2223  }
2224  case ThresholdCompositeOp:
2225  {
2226  /*
2227  Determine the amount and threshold.
2228  This Composition method is deprecated
2229  */
2230  value=GetImageArtifact(image,"compose:args");
2231  if (value != (char *) NULL)
2232  {
2233  flags=ParseGeometry(value,&geometry_info);
2234  amount=geometry_info.rho;
2235  threshold=geometry_info.sigma;
2236  if ((flags & SigmaValue) == 0)
2237  threshold=0.05f;
2238  }
2239  threshold*=QuantumRange;
2240  break;
2241  }
2242  default:
2243  break;
2244  }
2245  value=GetImageArtifact(image,"compose:outside-overlay");
2246  if (value != (const char *) NULL)
2247  clip_to_self=IsMagickTrue(value) == MagickFalse ? MagickTrue : MagickFalse;
2248  value=GetImageArtifact(image,"compose:clip-to-self");
2249  if (value != (const char *) NULL)
2250  clip_to_self=IsMagickTrue(value) != MagickFalse ? MagickTrue : MagickFalse;
2251  clamp=MagickTrue;
2252  value=GetImageArtifact(image,"compose:clamp");
2253  if (value != (const char *) NULL)
2254  clamp=IsMagickTrue(value);
2255  /*
2256  Composite image.
2257  */
2258 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2259  status=AccelerateCompositeImage(image,channel,compose,source_image,
2260  x_offset,y_offset,canvas_dissolve,source_dissolve,exception);
2261  if (status != MagickFalse)
2262  return(status);
2263 #endif
2264  status=MagickTrue;
2265  progress=0;
2266  midpoint=((MagickRealType) QuantumRange+1.0)/2;
2267  GetMagickPixelPacket(source_image,&zero);
2268  source_view=AcquireVirtualCacheView(source_image,exception);
2269  image_view=AcquireAuthenticCacheView(image,exception);
2270 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2271  #pragma omp parallel for schedule(static) shared(progress,status) \
2272  magick_number_threads(source_image,image,image->rows,1)
2273 #endif
2274  for (y=0; y < (ssize_t) image->rows; y++)
2275  {
2276  const PixelPacket
2277  *pixels;
2278 
2279  double
2280  luma,
2281  hue,
2282  chroma,
2283  sans;
2284 
2286  composite,
2287  canvas,
2288  source;
2289 
2290  const IndexPacket
2291  *magick_restrict source_indexes;
2292 
2293  const PixelPacket
2294  *magick_restrict p;
2295 
2296  IndexPacket
2297  *magick_restrict indexes;
2298 
2299  ssize_t
2300  x;
2301 
2302  PixelPacket
2303  *magick_restrict q;
2304 
2305  if (status == MagickFalse)
2306  continue;
2307  if (clip_to_self != MagickFalse)
2308  {
2309  if (y < y_offset)
2310  continue;
2311  if ((y-y_offset) >= (ssize_t) source_image->rows)
2312  continue;
2313  }
2314  /*
2315  If pixels is NULL, y is outside overlay region.
2316  */
2317  pixels=(PixelPacket *) NULL;
2318  p=(PixelPacket *) NULL;
2319  if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
2320  {
2321  p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
2322  source_image->columns,1,exception);
2323  if (p == (const PixelPacket *) NULL)
2324  {
2325  status=MagickFalse;
2326  continue;
2327  }
2328  pixels=p;
2329  if (x_offset < 0)
2330  p-=x_offset;
2331  }
2332  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2333  if (q == (PixelPacket *) NULL)
2334  {
2335  status=MagickFalse;
2336  continue;
2337  }
2338  indexes=GetCacheViewAuthenticIndexQueue(image_view);
2339  source_indexes=GetCacheViewVirtualIndexQueue(source_view);
2340  GetMagickPixelPacket(source_image,&source);
2341  GetMagickPixelPacket(image,&canvas);
2342  hue=0.0;
2343  chroma=0.0;
2344  luma=0.0;
2345  for (x=0; x < (ssize_t) image->columns; x++)
2346  {
2347  if (clip_to_self != MagickFalse)
2348  {
2349  if (x < x_offset)
2350  {
2351  q++;
2352  continue;
2353  }
2354  if ((x-x_offset) >= (ssize_t) source_image->columns)
2355  break;
2356  }
2357  canvas.red=(MagickRealType) GetPixelRed(q);
2358  canvas.green=(MagickRealType) GetPixelGreen(q);
2359  canvas.blue=(MagickRealType) GetPixelBlue(q);
2360  if (image->matte != MagickFalse)
2361  canvas.opacity=(MagickRealType) GetPixelOpacity(q);
2362  if (image->colorspace == CMYKColorspace)
2363  canvas.index=(MagickRealType) GetPixelIndex(indexes+x);
2364  if (image->colorspace == CMYKColorspace)
2365  {
2366  canvas.red=(MagickRealType) QuantumRange-canvas.red;
2367  canvas.green=(MagickRealType) QuantumRange-canvas.green;
2368  canvas.blue=(MagickRealType) QuantumRange-canvas.blue;
2369  canvas.index=(MagickRealType) QuantumRange-canvas.index;
2370  }
2371  /*
2372  Handle canvas modifications outside overlaid region.
2373  */
2374  composite=canvas;
2375  if ((pixels == (PixelPacket *) NULL) || (x < x_offset) ||
2376  ((x-x_offset) >= (ssize_t) source_image->columns))
2377  {
2378  switch (compose)
2379  {
2380  case DissolveCompositeOp:
2381  case BlendCompositeOp:
2382  {
2383  composite.opacity=(MagickRealType) (QuantumRange-canvas_dissolve*
2384  (QuantumRange-composite.opacity));
2385  break;
2386  }
2387  case ClearCompositeOp:
2388  case SrcCompositeOp:
2389  {
2390  CompositeClear(&canvas,&composite);
2391  break;
2392  }
2393  case InCompositeOp:
2394  case SrcInCompositeOp:
2395  case OutCompositeOp:
2396  case SrcOutCompositeOp:
2397  case DstInCompositeOp:
2398  case DstAtopCompositeOp:
2399  case CopyOpacityCompositeOp:
2400  case ChangeMaskCompositeOp:
2401  {
2402  composite.opacity=(MagickRealType) TransparentOpacity;
2403  break;
2404  }
2405  default:
2406  {
2407  (void) GetOneVirtualMagickPixel(source_image,x-x_offset,
2408  y-y_offset,&composite,exception);
2409  break;
2410  }
2411  }
2412  if (image->colorspace == CMYKColorspace)
2413  {
2414  composite.red=(MagickRealType) QuantumRange-composite.red;
2415  composite.green=(MagickRealType) QuantumRange-composite.green;
2416  composite.blue=(MagickRealType) QuantumRange-composite.blue;
2417  composite.index=(MagickRealType) QuantumRange-composite.index;
2418  }
2419  SetPixelRed(q,clamp != MagickFalse ?
2420  ClampPixel(composite.red) : ClampToQuantum(composite.red));
2421  SetPixelGreen(q,clamp != MagickFalse ?
2422  ClampPixel(composite.green) : ClampToQuantum(composite.green));
2423  SetPixelBlue(q,clamp != MagickFalse ?
2424  ClampPixel(composite.blue) : ClampToQuantum(composite.blue));
2425  if (image->matte != MagickFalse)
2426  SetPixelOpacity(q,clamp != MagickFalse ?
2427  ClampPixel(composite.opacity) :
2428  ClampToQuantum(composite.opacity));
2429  if (image->colorspace == CMYKColorspace)
2430  SetPixelIndex(indexes+x,clamp != MagickFalse ?
2431  ClampPixel(composite.index) : ClampToQuantum(composite.index));
2432  q++;
2433  continue;
2434  }
2435  /*
2436  Handle normal overlay of source onto canvas.
2437  */
2438  source.red=(MagickRealType) GetPixelRed(p);
2439  source.green=(MagickRealType) GetPixelGreen(p);
2440  source.blue=(MagickRealType) GetPixelBlue(p);
2441  if (source_image->matte != MagickFalse)
2442  source.opacity=(MagickRealType) GetPixelOpacity(p);
2443  if (source_image->colorspace == CMYKColorspace)
2444  source.index=(MagickRealType) GetPixelIndex(source_indexes+
2445  x-x_offset);
2446  if (source_image->colorspace == CMYKColorspace)
2447  {
2448  source.red=(MagickRealType) QuantumRange-source.red;
2449  source.green=(MagickRealType) QuantumRange-source.green;
2450  source.blue=(MagickRealType) QuantumRange-source.blue;
2451  source.index=(MagickRealType) QuantumRange-source.index;
2452  }
2453  switch (compose)
2454  {
2455  /* Duff-Porter Compositions */
2456  case ClearCompositeOp:
2457  {
2458  CompositeClear(&canvas,&composite);
2459  break;
2460  }
2461  case SrcCompositeOp:
2462  case CopyCompositeOp:
2463  case ReplaceCompositeOp:
2464  {
2465  composite=source;
2466  break;
2467  }
2468  case NoCompositeOp:
2469  case DstCompositeOp:
2470  break;
2471  case OverCompositeOp:
2472  case SrcOverCompositeOp:
2473  {
2474  MagickPixelCompositeOver(&source,source.opacity,&canvas,
2475  canvas.opacity,&composite);
2476  break;
2477  }
2478  case DstOverCompositeOp:
2479  {
2480  MagickPixelCompositeOver(&canvas,canvas.opacity,&source,
2481  source.opacity,&composite);
2482  break;
2483  }
2484  case SrcInCompositeOp:
2485  case InCompositeOp:
2486  {
2487  CompositeIn(&source,&canvas,&composite);
2488  break;
2489  }
2490  case DstInCompositeOp:
2491  {
2492  CompositeIn(&canvas,&source,&composite);
2493  break;
2494  }
2495  case OutCompositeOp:
2496  case SrcOutCompositeOp:
2497  {
2498  CompositeOut(&source,&canvas,&composite);
2499  break;
2500  }
2501  case DstOutCompositeOp:
2502  {
2503  CompositeOut(&canvas,&source,&composite);
2504  break;
2505  }
2506  case AtopCompositeOp:
2507  case SrcAtopCompositeOp:
2508  {
2509  CompositeAtop(&source,&canvas,&composite);
2510  break;
2511  }
2512  case DstAtopCompositeOp:
2513  {
2514  CompositeAtop(&canvas,&source,&composite);
2515  break;
2516  }
2517  case XorCompositeOp:
2518  {
2519  CompositeXor(&source,&canvas,&composite);
2520  break;
2521  }
2522  /* Mathematical Compositions */
2523  case PlusCompositeOp:
2524  {
2525  CompositePlus(&source,&canvas,channel,&composite);
2526  break;
2527  }
2528  case MinusDstCompositeOp:
2529  {
2530  CompositeMinus(&source,&canvas,channel,&composite);
2531  break;
2532  }
2533  case MinusSrcCompositeOp:
2534  {
2535  CompositeMinus(&canvas,&source,channel,&composite);
2536  break;
2537  }
2538  case ModulusAddCompositeOp:
2539  {
2540  CompositeModulusAdd(&source,&canvas,channel,&composite);
2541  break;
2542  }
2543  case ModulusSubtractCompositeOp:
2544  {
2545  CompositeModulusSubtract(&source,&canvas,channel,&composite);
2546  break;
2547  }
2548  case DifferenceCompositeOp:
2549  {
2550  CompositeDifference(&source,&canvas,channel,&composite);
2551  break;
2552  }
2553  case ExclusionCompositeOp:
2554  {
2555  CompositeExclusion(&source,&canvas,channel,&composite);
2556  break;
2557  }
2558  case MultiplyCompositeOp:
2559  {
2560  CompositeMultiply(&source,&canvas,channel,&composite);
2561  break;
2562  }
2563  case ScreenCompositeOp:
2564  {
2565  CompositeScreen(&source,&canvas,channel,&composite);
2566  break;
2567  }
2568  case DivideDstCompositeOp:
2569  {
2570  CompositeDivide(&source,&canvas,channel,&composite);
2571  break;
2572  }
2573  case DivideSrcCompositeOp:
2574  {
2575  CompositeDivide(&canvas,&source,channel,&composite);
2576  break;
2577  }
2578  case DarkenCompositeOp:
2579  {
2580  CompositeDarken(&source,&canvas,channel,&composite);
2581  break;
2582  }
2583  case LightenCompositeOp:
2584  {
2585  CompositeLighten(&source,&canvas,channel,&composite);
2586  break;
2587  }
2588  case DarkenIntensityCompositeOp:
2589  {
2590  CompositeDarkenIntensity(&source,&canvas,channel,&composite);
2591  break;
2592  }
2593  case LightenIntensityCompositeOp:
2594  {
2595  CompositeLightenIntensity(&source,&canvas,channel,&composite);
2596  break;
2597  }
2598  case MathematicsCompositeOp:
2599  {
2600  CompositeMathematics(&source,&canvas,channel,&geometry_info,
2601  &composite);
2602  break;
2603  }
2604  /* Lighting Compositions */
2605  case ColorDodgeCompositeOp:
2606  {
2607  CompositeColorDodge(&source,&canvas,&composite);
2608  break;
2609  }
2610  case ColorBurnCompositeOp:
2611  {
2612  CompositeColorBurn(&source,&canvas,&composite);
2613  break;
2614  }
2615  case LinearDodgeCompositeOp:
2616  {
2617  CompositeLinearDodge(&source,&canvas,&composite);
2618  break;
2619  }
2620  case LinearBurnCompositeOp:
2621  {
2622  CompositeLinearBurn(&source,&canvas,&composite);
2623  break;
2624  }
2625  case HardLightCompositeOp:
2626  {
2627  CompositeHardLight(&source,&canvas,&composite);
2628  break;
2629  }
2630  case HardMixCompositeOp:
2631  {
2632  CompositeHardMix(&source,&canvas,&composite);
2633  break;
2634  }
2635  case OverlayCompositeOp:
2636  {
2637  /* Overlay = Reversed HardLight. */
2638  CompositeHardLight(&canvas,&source,&composite);
2639  break;
2640  }
2641  case SoftLightCompositeOp:
2642  {
2643  CompositeSoftLight(&source,&canvas,&composite);
2644  break;
2645  }
2646  case LinearLightCompositeOp:
2647  {
2648  CompositeLinearLight(&source,&canvas,&composite);
2649  break;
2650  }
2651  case PegtopLightCompositeOp:
2652  {
2653  CompositePegtopLight(&source,&canvas,&composite);
2654  break;
2655  }
2656  case VividLightCompositeOp:
2657  {
2658  CompositeVividLight(&source,&canvas,&composite);
2659  break;
2660  }
2661  case PinLightCompositeOp:
2662  {
2663  CompositePinLight(&source,&canvas,&composite);
2664  break;
2665  }
2666  /* Other Composition */
2667  case ChangeMaskCompositeOp:
2668  {
2669  if ((composite.opacity > ((MagickRealType) QuantumRange/2.0)) ||
2670  (IsMagickColorSimilar(&source,&canvas) != MagickFalse))
2671  composite.opacity=(MagickRealType) TransparentOpacity;
2672  else
2673  composite.opacity=(MagickRealType) OpaqueOpacity;
2674  break;
2675  }
2676  case BumpmapCompositeOp:
2677  {
2678  if (source.opacity == TransparentOpacity)
2679  break;
2680  CompositeBumpmap(&source,&canvas,&composite);
2681  break;
2682  }
2683  case DissolveCompositeOp:
2684  {
2685  MagickPixelCompositeOver(&source,(MagickRealType) (QuantumRange-
2686  source_dissolve*(QuantumRange-source.opacity)),&canvas,
2687  (MagickRealType) (QuantumRange-canvas_dissolve*(QuantumRange-
2688  canvas.opacity)),&composite);
2689  break;
2690  }
2691  case BlendCompositeOp:
2692  {
2693  MagickPixelCompositeBlend(&source,source_dissolve,&canvas,
2694  canvas_dissolve,&composite);
2695  break;
2696  }
2697  case StereoCompositeOp:
2698  {
2699  composite.red=(MagickRealType) GetPixelRed(p);
2700  composite.opacity=(composite.opacity+canvas.opacity/2);
2701  break;
2702  }
2703  case ThresholdCompositeOp:
2704  {
2705  CompositeThreshold(&source,&canvas,threshold,amount,&composite);
2706  break;
2707  }
2708  case ModulateCompositeOp:
2709  {
2710  ssize_t
2711  offset;
2712 
2713  if (source.opacity == TransparentOpacity)
2714  break;
2715  offset=(ssize_t) (MagickPixelIntensityToQuantum(&source)-midpoint);
2716  if (offset == 0)
2717  break;
2718  CompositeHCL(canvas.red,canvas.green,canvas.blue,&hue,
2719  &chroma,&luma);
2720  luma+=(0.01*percent_luma*offset)/midpoint;
2721  chroma*=0.01*percent_chroma;
2722  HCLComposite(hue,chroma,luma,&composite.red,&composite.green,
2723  &composite.blue);
2724  break;
2725  }
2726  case HueCompositeOp:
2727  {
2728  if (source.opacity == TransparentOpacity)
2729  break;
2730  if (canvas.opacity == TransparentOpacity)
2731  {
2732  composite=source;
2733  break;
2734  }
2735  CompositeHCL(canvas.red,canvas.green,canvas.blue,&hue,
2736  &chroma,&luma);
2737  CompositeHCL(source.red,source.green,source.blue,&hue,&sans,&sans);
2738  HCLComposite(hue,chroma,luma,&composite.red,
2739  &composite.green,&composite.blue);
2740  if (source.opacity < canvas.opacity)
2741  composite.opacity=source.opacity;
2742  break;
2743  }
2744  case SaturateCompositeOp:
2745  {
2746  if (source.opacity == TransparentOpacity)
2747  break;
2748  if (canvas.opacity == TransparentOpacity)
2749  {
2750  composite=source;
2751  break;
2752  }
2753  CompositeHCL(canvas.red,canvas.green,canvas.blue,&hue,
2754  &chroma,&luma);
2755  CompositeHCL(source.red,source.green,source.blue,&sans,&chroma,
2756  &sans);
2757  HCLComposite(hue,chroma,luma,&composite.red,
2758  &composite.green,&composite.blue);
2759  if (source.opacity < canvas.opacity)
2760  composite.opacity=source.opacity;
2761  break;
2762  }
2763  case LuminizeCompositeOp:
2764  {
2765  if (source.opacity == TransparentOpacity)
2766  break;
2767  if (canvas.opacity == TransparentOpacity)
2768  {
2769  composite=source;
2770  break;
2771  }
2772  CompositeHCL(canvas.red,canvas.green,canvas.blue,&hue,
2773  &chroma,&luma);
2774  CompositeHCL(source.red,source.green,source.blue,&sans,&sans,
2775  &luma);
2776  HCLComposite(hue,chroma,luma,&composite.red,
2777  &composite.green,&composite.blue);
2778  if (source.opacity < canvas.opacity)
2779  composite.opacity=source.opacity;
2780  break;
2781  }
2782  case ColorizeCompositeOp:
2783  {
2784  if (source.opacity == TransparentOpacity)
2785  break;
2786  if (canvas.opacity == TransparentOpacity)
2787  {
2788  composite=source;
2789  break;
2790  }
2791  CompositeHCL(canvas.red,canvas.green,canvas.blue,&sans,
2792  &sans,&luma);
2793  CompositeHCL(source.red,source.green,source.blue,&hue,&chroma,&sans);
2794  HCLComposite(hue,chroma,luma,&composite.red,
2795  &composite.green,&composite.blue);
2796  if (source.opacity < canvas.opacity)
2797  composite.opacity=source.opacity;
2798  break;
2799  }
2800  case CopyRedCompositeOp:
2801  case CopyCyanCompositeOp:
2802  {
2803  composite.red=source.red;
2804  break;
2805  }
2806  case CopyGreenCompositeOp:
2807  case CopyMagentaCompositeOp:
2808  {
2809  composite.green=source.green;
2810  break;
2811  }
2812  case CopyBlueCompositeOp:
2813  case CopyYellowCompositeOp:
2814  {
2815  composite.blue=source.blue;
2816  break;
2817  }
2818  case CopyOpacityCompositeOp:
2819  {
2820  if (source.matte == MagickFalse)
2821  composite.opacity=(MagickRealType) (QuantumRange-
2822  MagickPixelIntensityToQuantum(&source));
2823  else
2824  composite.opacity=source.opacity;
2825  break;
2826  }
2827  case CopyBlackCompositeOp:
2828  {
2829  if (source.colorspace != CMYKColorspace)
2830  ConvertRGBToCMYK(&source);
2831  composite.index=source.index;
2832  break;
2833  }
2834  /* compose methods that are already handled */
2835  case BlurCompositeOp:
2836  case DisplaceCompositeOp:
2837  case DistortCompositeOp:
2838  {
2839  composite=source;
2840  break;
2841  }
2842  default:
2843  break;
2844  }
2845  if (image->colorspace == CMYKColorspace)
2846  {
2847  composite.red=(MagickRealType) QuantumRange-composite.red;
2848  composite.green=(MagickRealType) QuantumRange-composite.green;
2849  composite.blue=(MagickRealType) QuantumRange-composite.blue;
2850  composite.index=(MagickRealType) QuantumRange-composite.index;
2851  }
2852  SetPixelRed(q,clamp != MagickFalse ?
2853  ClampPixel(composite.red) : ClampToQuantum(composite.red));
2854  SetPixelGreen(q,clamp != MagickFalse ?
2855  ClampPixel(composite.green) : ClampToQuantum(composite.green));
2856  SetPixelBlue(q,clamp != MagickFalse ?
2857  ClampPixel(composite.blue) : ClampToQuantum(composite.blue));
2858  SetPixelOpacity(q,clamp != MagickFalse ?
2859  ClampPixel(composite.opacity) : ClampToQuantum(composite.opacity));
2860  if (image->colorspace == CMYKColorspace)
2861  SetPixelIndex(indexes+x,clamp != MagickFalse ?
2862  ClampPixel(composite.index) : ClampToQuantum(composite.index));
2863  p++;
2864  if (p >= (pixels+source_image->columns))
2865  p=pixels;
2866  q++;
2867  }
2868  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2869  status=MagickFalse;
2870  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2871  {
2872  MagickBooleanType
2873  proceed;
2874 
2875 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2876  #pragma omp atomic
2877 #endif
2878  progress++;
2879  proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
2880  if (proceed == MagickFalse)
2881  status=MagickFalse;
2882  }
2883  }
2884  source_view=DestroyCacheView(source_view);
2885  image_view=DestroyCacheView(image_view);
2886  if (canvas_image != (Image * ) NULL)
2887  canvas_image=DestroyImage(canvas_image);
2888  else
2889  source_image=DestroyImage(source_image);
2890  return(status);
2891 }
2892 
2893 /*
2894 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2895 % %
2896 % %
2897 % %
2898 % T e x t u r e I m a g e %
2899 % %
2900 % %
2901 % %
2902 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2903 %
2904 % TextureImage() repeatedly tiles the texture image across and down the image
2905 % canvas.
2906 %
2907 % The format of the TextureImage method is:
2908 %
2909 % MagickBooleanType TextureImage(Image *image,const Image *texture)
2910 %
2911 % A description of each parameter follows:
2912 %
2913 % o image: the image.
2914 %
2915 % o texture: This image is the texture to layer on the background.
2916 %
2917 */
2918 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
2919 {
2920 #define TextureImageTag "Texture/Image"
2921 
2922  CacheView
2923  *image_view,
2924  *texture_view;
2925 
2927  *exception;
2928 
2929  Image
2930  *texture_image;
2931 
2932  MagickBooleanType
2933  status;
2934 
2935  ssize_t
2936  y;
2937 
2938  assert(image != (Image *) NULL);
2939  assert(image->signature == MagickCoreSignature);
2940  if (IsEventLogging() != MagickFalse)
2941  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2942  if (texture == (const Image *) NULL)
2943  return(MagickFalse);
2944  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2945  return(MagickFalse);
2946  exception=(&image->exception);
2947  texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2948  if (texture_image == (const Image *) NULL)
2949  return(MagickFalse);
2950  (void) TransformImageColorspace(texture_image,image->colorspace);
2951  (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod);
2952  status=MagickTrue;
2953  if ((image->compose != CopyCompositeOp) &&
2954  ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2955  (texture_image->matte != MagickFalse)))
2956  {
2957  /*
2958  Tile texture onto the image background.
2959  */
2960  for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2961  {
2962  ssize_t
2963  x;
2964 
2965  if (status == MagickFalse)
2966  continue;
2967  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2968  {
2969  MagickBooleanType
2970  thread_status;
2971 
2972  thread_status=CompositeImage(image,image->compose,texture_image,x+
2973  texture_image->tile_offset.x,y+texture_image->tile_offset.y);
2974  if (thread_status == MagickFalse)
2975  {
2976  status=thread_status;
2977  break;
2978  }
2979  }
2980  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2981  {
2982  MagickBooleanType
2983  proceed;
2984 
2985  proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2986  y,image->rows);
2987  if (proceed == MagickFalse)
2988  status=MagickFalse;
2989  }
2990  }
2991  (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2992  image->rows,image->rows);
2993  texture_image=DestroyImage(texture_image);
2994  return(status);
2995  }
2996  /*
2997  Tile texture onto the image background (optimized).
2998  */
2999  status=MagickTrue;
3000  texture_view=AcquireVirtualCacheView(texture_image,exception);
3001  image_view=AcquireAuthenticCacheView(image,exception);
3002 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3003  #pragma omp parallel for schedule(static) shared(status) \
3004  magick_number_threads(image,texture_image,image->rows,1)
3005 #endif
3006  for (y=0; y < (ssize_t) image->rows; y++)
3007  {
3008  MagickBooleanType
3009  sync;
3010 
3011  const IndexPacket
3012  *texture_indexes;
3013 
3014  const PixelPacket
3015  *p;
3016 
3017  IndexPacket
3018  *indexes;
3019 
3020  ssize_t
3021  x;
3022 
3023  PixelPacket
3024  *q;
3025 
3026  size_t
3027  width;
3028 
3029  if (status == MagickFalse)
3030  continue;
3031  p=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,(y+
3032  texture_image->tile_offset.y) % texture_image->rows,
3033  texture_image->columns,1,exception);
3034  q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3035  exception);
3036  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3037  {
3038  status=MagickFalse;
3039  continue;
3040  }
3041  texture_indexes=GetCacheViewVirtualIndexQueue(texture_view);
3042  indexes=GetCacheViewAuthenticIndexQueue(image_view);
3043  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
3044  {
3045  width=texture_image->columns;
3046  if ((x+(ssize_t) width) > (ssize_t) image->columns)
3047  width=image->columns-x;
3048  (void) memcpy(q,p,width*sizeof(*p));
3049  if ((image->colorspace == CMYKColorspace) &&
3050  (texture_image->colorspace == CMYKColorspace))
3051  {
3052  (void) memcpy(indexes,texture_indexes,width*
3053  sizeof(*indexes));
3054  indexes+=width;
3055  }
3056  q+=width;
3057  }
3058  sync=SyncCacheViewAuthenticPixels(image_view,exception);
3059  if (sync == MagickFalse)
3060  status=MagickFalse;
3061  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3062  {
3063  MagickBooleanType
3064  proceed;
3065 
3066  proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3067  image->rows);
3068  if (proceed == MagickFalse)
3069  status=MagickFalse;
3070  }
3071  }
3072  texture_view=DestroyCacheView(texture_view);
3073  image_view=DestroyCacheView(image_view);
3074  texture_image=DestroyImage(texture_image);
3075  return(status);
3076 }
Definition: image.h:152