2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 %
13 % MagickCore Image Composite Methods %
20 % Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "magick/studio.h"
44 #include "magick/artifact.h"
45 #include "magick/cache-view.h"
46 #include "magick/client.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/colorspace.h"
50 #include "magick/colorspace-private.h"
51 #include "magick/composite.h"
52 #include "magick/composite-private.h"
53 #include "magick/constitute.h"
54 #include "magick/draw.h"
55 #include "magick/fx.h"
56 #include "magick/gem.h"
57 #include "magick/geometry.h"
58 #include "magick/image.h"
59 #include "magick/image-private.h"
60 #include "magick/list.h"
61 #include "magick/log.h"
62 #include "magick/monitor.h"
63 #include "magick/monitor-private.h"
64 #include "magick/memory_.h"
65 #include "magick/option.h"
66 #include "magick/pixel-private.h"
67 #include "magick/property.h"
68 #include "magick/quantum.h"
69 #include "magick/resample.h"
70 #include "magick/resource_.h"
71 #include "magick/string_.h"
72 #include "magick/utility.h"
73 #include "magick/version.h"
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80 % C o m p o s i t e I m a g e C h a n n e l %
84 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 % CompositeImageChannel() returns the second image composited onto the first
87 % at the specified offset, using the specified composite method.
89 % The format of the CompositeImageChannel method is:
91 % MagickBooleanType CompositeImage(Image *image,
92 % const CompositeOperator compose,Image *composite_image,
93 % const long x_offset,const long y_offset)
94 % MagickBooleanType CompositeImageChannel(Image *image,
95 % const ChannelType channel,const CompositeOperator compose,
96 % Image *composite_image,const long x_offset,const long y_offset)
98 % A description of each parameter follows:
100 % o image: the destination image, modified by he composition
102 % o channel: the channel.
104 % o compose: This operator affects how the composite is applied to
105 % the image. The operators and how they are utilized are listed here
106 % http://www.w3.org/TR/SVG12/#compositing.
108 % o composite_image: the composite (source) image.
110 % o x_offset: the column offset of the composited image.
112 % o y_offset: the row offset of the composited image.
114 % Extra Controls from Image meta-data in 'composite_image' (artifacts)
117 % A string containing extra numerical arguments for specific compose
118 % methods, generally expressed as a 'geometry' or a comma separated list
121 % Compose methods needing such arguments include "BlendCompositeOp" and
122 % "DisplaceCompositeOp".
124 % o "compose:outside-overlay"
125 % Modify how the composition is to effect areas not directly covered
126 % by the 'composite_image' at the offset given. Normally this is
127 % dependant on the 'compose' method, especially Duff-Porter methods.
129 % If set to "false" then disable all normal handling of pixels not
130 % covered by the composite_image. Typically used for repeated tiling
131 % of the composite_image by the calling API.
133 % Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
137 static inline double MagickMin(const double x,const double y)
143 static inline double MagickMax(const double x,const double y)
151 ** Programmers notes on SVG specification.
153 ** A Composition is defined by...
154 ** Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
155 ** Blending areas : X = 1 for area of overlap ie: f(Sc,Dc)
156 ** Y = 1 for source preserved
157 ** Z = 1 for destination preserved
159 ** Conversion to transparency (then optimized)
160 ** Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
161 ** Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
164 ** Sca = Sc*Sa normalized Source color divided by Source alpha
165 ** Dca = Dc*Da normalized Dest color divided by Dest alpha
166 ** Dc' = Dca'/Da' the desired color value for this channel.
168 ** Da' in in the follow formula as 'gamma' The resulting alpla value.
171 ** Most functions use a blending mode of over (X=1,Y=1,Z=1)
172 ** this results in the following optimizations...
173 ** gamma = Sa+Da-Sa*Da;
174 ** gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
175 ** opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
178 static inline MagickRealType Add(const MagickRealType p,const MagickRealType q)
184 if (pixel > QuantumRange)
185 pixel-=(QuantumRange+1.0);
189 static inline void CompositeAdd(const MagickPixelPacket *p,
190 const MagickRealType alpha,const MagickPixelPacket *q,
191 const MagickRealType beta,MagickPixelPacket *composite)
193 composite->red=Add(p->red,q->red);
194 composite->green=Add(p->green,q->green);
195 composite->blue=Add(p->blue,q->blue);
196 composite->opacity=Add(alpha,beta);
197 if (q->colorspace == CMYKColorspace)
198 composite->index=Add(p->index,q->index);
201 static inline MagickRealType Atop(const MagickRealType p,
202 const MagickRealType Sa,const MagickRealType q,
203 const MagickRealType magick_unused(Da))
205 return(p*Sa+q*(1.0-Sa)); /* Da optimized out, Da/gamma => 1.0 */
208 static inline void CompositeAtop(const MagickPixelPacket *p,
209 const MagickRealType alpha,const MagickPixelPacket *q,
210 const MagickRealType beta,MagickPixelPacket *composite)
215 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
216 composite->opacity=beta; /* optimized 1.0-Gamma */
217 composite->red=Atop(p->red,Sa,q->red,1.0);
218 composite->green=Atop(p->green,Sa,q->green,1.0);
219 composite->blue=Atop(p->blue,Sa,q->blue,1.0);
220 if (q->colorspace == CMYKColorspace)
221 composite->index=Atop(p->index,Sa,q->index,1.0);
225 What is this Composition method for, can't find any specification!
226 WARNING this is not doing correct 'over' blend handling (Anthony Thyssen).
228 static inline void CompositeBumpmap(const MagickPixelPacket *p,
229 const MagickRealType magick_unused(alpha),const MagickPixelPacket *q,
230 const MagickRealType magick_unused(beta),MagickPixelPacket *composite)
235 intensity=MagickPixelIntensity(p);
236 composite->red=QuantumScale*intensity*q->red;
237 composite->green=QuantumScale*intensity*q->green;
238 composite->blue=QuantumScale*intensity*q->blue;
239 composite->opacity=(MagickRealType) QuantumScale*intensity*p->opacity;
240 if (q->colorspace == CMYKColorspace)
241 composite->index=QuantumScale*intensity*q->index;
244 static inline void CompositeClear(const MagickPixelPacket *q,
245 MagickPixelPacket *composite)
247 composite->opacity=(MagickRealType) TransparentOpacity;
249 composite->green=0.0;
251 if (q->colorspace == CMYKColorspace)
252 composite->index=0.0;
255 static MagickRealType ColorBurn(const MagickRealType Sca,
256 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
260 Oct 2004 SVG specification.
262 if (Sca*Da + Dca*Sa <= Sa*Da)
263 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
264 return(Sa*(Sca*Da+Dca*Sa-Sa*Da)/Sca + Sca*(1.0-Da) + Dca*(1.0-Sa));
267 March 2009 SVG specification.
269 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
270 return(Sa*Da+Dca*(1.0-Sa));
271 if (Sca < MagickEpsilon)
272 return(Dca*(1.0-Sa));
273 return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
277 static inline void CompositeColorBurn(const MagickPixelPacket *p,
278 const MagickRealType alpha,const MagickPixelPacket *q,
279 const MagickRealType beta,MagickPixelPacket *composite)
286 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
287 Da=1.0-QuantumScale*beta;
288 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
289 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
290 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
291 composite->red=gamma*ColorBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
293 composite->green=gamma*ColorBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
295 composite->blue=gamma*ColorBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
297 if (q->colorspace == CMYKColorspace)
298 composite->index=gamma*ColorBurn(QuantumScale*p->index*Sa,Sa,QuantumScale*
303 static MagickRealType ColorDodge(const MagickRealType Sca,
304 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
308 Oct 2004 SVG specification.
310 if ((Sca*Da+Dca*Sa) >= Sa*Da)
311 return( Sa*Da + Sca*(1.0-Da) + Dca*(1.0-Sa) );
312 return( Dca*Sa*Sa/(Sa-Sca) + Sca*(1.0-Da) + Dca*(1.0-Sa) );
316 New specification, March 2009 SVG specification. This specification was
317 also wrong of non-overlap cases.
319 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
320 return(Sca*(1.0-Da));
321 if (fabs(Sca-Sa) < MagickEpsilon)
322 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
323 return(Sa*MagickMin(Da,Dca*Sa/(Sa-Sca)));
326 Working from first principles using the original formula:
330 This works correctly! Looks like the 2004 model was right but just
331 required a extra condition for correct handling.
333 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
334 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
335 if (fabs(Sca-Sa) < MagickEpsilon)
336 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
337 return(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
340 static inline void CompositeColorDodge(const MagickPixelPacket *p,
341 const MagickRealType alpha,const MagickPixelPacket *q,
342 const MagickRealType beta,MagickPixelPacket *composite)
349 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
350 Da=1.0-QuantumScale*beta;
351 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
352 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
353 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
354 composite->red=gamma*ColorDodge(QuantumScale*p->red*Sa,Sa,QuantumScale*
356 composite->green=gamma*ColorDodge(QuantumScale*p->green*Sa,Sa,QuantumScale*
358 composite->blue=gamma*ColorDodge(QuantumScale*p->blue*Sa,Sa,QuantumScale*
360 if (q->colorspace == CMYKColorspace)
361 composite->index=gamma*ColorDodge(QuantumScale*p->index*Sa,Sa,QuantumScale*
365 static inline MagickRealType Darken(const MagickRealType p,
366 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
369 return(MagickOver_(p,alpha,q,beta)); /* src-over */
370 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
373 static inline void CompositeDarken(const MagickPixelPacket *p,
374 const MagickRealType alpha,const MagickPixelPacket *q,
375 const MagickRealType beta,MagickPixelPacket *composite)
380 gamma=1.0-QuantumScale*QuantumScale*alpha*beta;
381 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
382 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
383 composite->red=gamma*Darken(p->red,alpha,q->red,beta);
384 composite->green=gamma*Darken(p->green,alpha,q->green,beta);
385 composite->blue=gamma*Darken(p->blue,alpha,q->blue,beta);
386 if (q->colorspace == CMYKColorspace)
387 composite->index=gamma*Darken(p->index,alpha,q->index,beta);
390 static inline MagickRealType Difference(const MagickRealType p,
391 const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
394 Optimized by Multipling by QuantumRange (taken from gamma).
396 return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q));
399 static inline void CompositeDifference(const MagickPixelPacket *p,
400 const MagickRealType alpha,const MagickPixelPacket *q,
401 const MagickRealType beta,MagickPixelPacket *composite)
408 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
409 Da=1.0-QuantumScale*beta;
410 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
411 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
413 Values not normalized as an optimization.
415 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
416 composite->red=gamma*Difference(p->red,Sa,q->red,Da);
417 composite->green=gamma*Difference(p->green,Sa,q->green,Da);
418 composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da);
419 if (q->colorspace == CMYKColorspace)
420 composite->index=gamma*Difference(p->index,Sa,q->index,Da);
423 static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
424 const MagickRealType Dca,const MagickRealType Da)
431 But with appropriate handling for special case of Dc == 0 specifically
432 f(Black,Black) = Black and f(non-Black,Black) = White. It is however
433 also important to correctly do 'over' alpha blending which is why it
434 becomes so complex looking.
436 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
437 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
438 if (fabs(Dca) < MagickEpsilon)
439 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
440 return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
443 static inline void CompositeDivide(const MagickPixelPacket *p,
444 const MagickRealType alpha,const MagickPixelPacket *q,
445 const MagickRealType beta,MagickPixelPacket *composite)
452 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
453 Da=1.0-QuantumScale*beta;
454 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
455 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
456 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
457 composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale*
459 composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale*
461 composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale*
463 if (q->colorspace == CMYKColorspace)
464 composite->index=gamma*Divide(QuantumScale*p->index*Sa,Sa,QuantumScale*
468 static MagickRealType Exclusion(const MagickRealType Sca,
469 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
471 return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
474 static inline void CompositeExclusion(const MagickPixelPacket *p,
475 const MagickRealType alpha,const MagickPixelPacket *q,
476 const MagickRealType beta,MagickPixelPacket *composite)
483 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
484 Da=1.0-QuantumScale*beta;
485 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
486 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
487 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
488 composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale*
490 composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale*
492 composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale*
494 if (q->colorspace == CMYKColorspace)
495 composite->index=gamma*Exclusion(QuantumScale*p->index*Sa,Sa,QuantumScale*
499 static MagickRealType HardLight(const MagickRealType Sca,
500 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
503 return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
504 return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
507 static inline void CompositeHardLight(const MagickPixelPacket *p,
508 const MagickRealType alpha,const MagickPixelPacket *q,
509 const MagickRealType beta,MagickPixelPacket *composite)
516 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
517 Da=1.0-QuantumScale*beta;
518 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
519 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
520 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
521 composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
523 composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
525 composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
527 if (q->colorspace == CMYKColorspace)
528 composite->index=gamma*HardLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
533 static void CompositeHSB(const MagickRealType red,const MagickRealType green,
534 const MagickRealType blue,double *hue,double *saturation,double *brightness)
542 Convert RGB to HSB colorspace.
544 assert(hue != (double *) NULL);
545 assert(saturation != (double *) NULL);
546 assert(brightness != (double *) NULL);
547 max=(red > green ? red : green);
550 min=(red < green ? red : green);
555 *brightness=(double) (QuantumScale*max);
558 *saturation=(double) (1.0-min/max);
563 *hue=(double) ((green-blue)/delta);
566 *hue=(double) (2.0+(blue-red)/delta);
569 *hue=(double) (4.0+(red-green)/delta);
575 static inline MagickRealType In(const MagickRealType p,
576 const MagickRealType alpha,const MagickRealType magick_unused(q),
577 const MagickRealType beta)
579 return((1.0-QuantumScale*alpha)*p*(1.0-QuantumScale*beta));
582 static inline void CompositeIn(const MagickPixelPacket *p,
583 const MagickRealType alpha,const MagickPixelPacket *q,
584 const MagickRealType beta,MagickPixelPacket *composite)
589 gamma=(1.0-QuantumScale*alpha)*(1.0-QuantumScale*beta);
590 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
591 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
592 composite->red=gamma*In(p->red,alpha,q->red,beta);
593 composite->green=gamma*In(p->green,alpha,q->green,beta);
594 composite->blue=gamma*In(p->blue,alpha,q->blue,beta);
595 if (q->colorspace == CMYKColorspace)
596 composite->index=gamma*In(p->index,alpha,q->index,beta);
599 static inline MagickRealType Lighten(const MagickRealType p,
600 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
603 return(MagickOver_(p,alpha,q,beta)); /* src-over */
604 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
607 static inline void CompositeLighten(const MagickPixelPacket *p,
608 const MagickRealType alpha,const MagickPixelPacket *q,
609 const MagickRealType beta,MagickPixelPacket *composite)
614 composite->opacity=QuantumScale*alpha*beta; /* optimized 1-gamma */
615 gamma=1.0-QuantumScale*composite->opacity;
616 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
617 composite->red=gamma*Lighten(p->red,alpha,q->red,beta);
618 composite->green=gamma*Lighten(p->green,alpha,q->green,beta);
619 composite->blue=gamma*Lighten(p->blue,alpha,q->blue,beta);
620 if (q->colorspace == CMYKColorspace)
621 composite->index=gamma*Lighten(p->index,alpha,q->index,beta);
624 static inline void CompositeLinearDodge(const MagickPixelPacket *p,
625 const MagickRealType alpha,const MagickPixelPacket *q,
626 const MagickRealType beta,MagickPixelPacket *composite)
633 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
634 Da=1.0-QuantumScale*beta;
635 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
636 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
638 Operation performed directly - not need for sub-routine.
640 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
641 composite->red=gamma*(p->red*Sa+q->red*Da);
642 composite->green=gamma*(p->green*Sa+q->green*Da);
643 composite->blue=gamma*(p->blue*Sa+q->blue*Da);
644 if (q->colorspace == CMYKColorspace)
645 composite->index=gamma*(p->index*Sa+q->index*Da);
649 static inline MagickRealType LinearBurn(const MagickRealType Sca,
650 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
653 LinearLight: as defined by Abode Photoshop, according to
654 http://www.simplefilter.de/en/basics/mixmods.html is:
656 f(Sc,Dc) = Dc + Sc - 1
658 return(Sca+Dca-Sa*Da);
661 static inline void CompositeLinearBurn(const MagickPixelPacket *p,
662 const MagickRealType alpha,const MagickPixelPacket *q,
663 const MagickRealType beta,MagickPixelPacket *composite)
670 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
671 Da=1.0-QuantumScale*beta;
672 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
673 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
674 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
675 composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
677 composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
679 composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
681 if (q->colorspace == CMYKColorspace)
682 composite->index=gamma*LinearBurn(QuantumScale*p->index*Sa,Sa,QuantumScale*
687 static inline MagickRealType LinearLight(const MagickRealType Sca,
688 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
692 Previous formula, only valid for fully-opaque images.
694 return(Dca+2*Sca-1.0);
697 LinearLight: as defined by Abode Photoshop, according to
698 http://www.simplefilter.de/en/basics/mixmods.html is:
700 f(Sc,Dc) = Dc + 2*Sc - 1
702 return((Sca-Sa)*Da+Sca+Dca);
706 static inline void CompositeLinearLight(const MagickPixelPacket *p,
707 const MagickRealType alpha,const MagickPixelPacket *q,
708 const MagickRealType beta,MagickPixelPacket *composite)
715 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
716 Da=1.0-QuantumScale*beta;
717 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
718 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
719 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
720 composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
722 composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
724 composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
726 if (q->colorspace == CMYKColorspace)
727 composite->index=gamma*LinearLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
731 static inline MagickRealType Mathematics(const MagickRealType Sca,
732 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
733 const GeometryInfo *geometry_info)
736 'Mathematics' a free form user control mathematical composition is defined
739 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
741 Where the arguments A,B,C,D are (currently) passed to composite as
742 a command separated 'geometry' string in "compose:args" image artifact.
744 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
746 Applying the SVG transparency formula (see above), we get...
748 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
750 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
753 return(geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
754 geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
758 static inline void CompositeMathematics(const MagickPixelPacket *p,
759 const MagickPixelPacket *q,const GeometryInfo *args,
760 MagickPixelPacket *composite)
767 Sa=1.0-QuantumScale*p->opacity;
768 Da=1.0-QuantumScale*q->opacity;
769 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
770 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
771 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
772 composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
774 composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,QuantumScale*
775 q->green*Da,Da,args);
776 composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
778 if (q->colorspace == CMYKColorspace)
779 composite->index=gamma*Mathematics(QuantumScale*p->index*Sa,Sa,QuantumScale*
780 q->index*Da,Da,args);
783 static inline MagickRealType Minus(const MagickRealType Sca,
784 const MagickRealType Dca)
789 static inline void CompositeMinus(const MagickPixelPacket *p,
790 const MagickRealType alpha,const MagickPixelPacket *q,
791 const MagickRealType beta,MagickPixelPacket *composite)
798 Sa=1.0-QuantumScale*alpha;
799 Da=1.0-QuantumScale*beta;
800 gamma=RoundToUnity(Sa-Da); /* is this correct? - I do not think so! */
801 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
802 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
803 composite->red=gamma*Minus(p->red*Sa,q->red*Da);
804 composite->green=gamma*Minus(p->green*Sa,q->green*Da);
805 composite->blue=gamma*Minus(p->blue*Sa,q->blue*Da);
806 if (q->colorspace == CMYKColorspace)
807 composite->index=gamma*Minus(p->index*Sa,q->index*Da);
810 static inline MagickRealType Multiply(const MagickRealType Sca,
811 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
813 return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
816 static inline void CompositeMultiply(const MagickPixelPacket *p,
817 const MagickRealType alpha,const MagickPixelPacket *q,
818 const MagickRealType beta,MagickPixelPacket *composite)
825 Sa=1.0-QuantumScale*alpha;
826 Da=1.0-QuantumScale*beta;
827 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
828 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
829 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
830 composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
832 composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
834 composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
836 if (q->colorspace == CMYKColorspace)
837 composite->index=gamma*Multiply(QuantumScale*p->index*Sa,Sa,QuantumScale*
841 static inline MagickRealType Out(const MagickRealType p,
842 const MagickRealType alpha,const MagickRealType magick_unused(q),
843 const MagickRealType beta)
845 return((1.0-QuantumScale*alpha)*p*QuantumScale*beta);
848 static inline void CompositeOut(const MagickPixelPacket *p,
849 const MagickRealType alpha,const MagickPixelPacket *q,
850 const MagickRealType beta,MagickPixelPacket *composite)
855 gamma=(1.0-QuantumScale*alpha)*QuantumScale*beta;
856 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
857 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
858 composite->red=gamma*Out(p->red,alpha,q->red,beta);
859 composite->green=gamma*Out(p->green,alpha,q->green,beta);
860 composite->blue=gamma*Out(p->blue,alpha,q->blue,beta);
861 if (q->colorspace == CMYKColorspace)
862 composite->index=gamma*Out(p->index,alpha,q->index,beta);
865 static inline void CompositeOver(const MagickPixelPacket *p,
866 const MagickRealType alpha,const MagickPixelPacket *q,
867 const MagickRealType beta,MagickPixelPacket *composite)
869 MagickPixelCompositeOver(p,alpha,q,beta,composite);
872 static MagickRealType PegtopLight(const MagickRealType Sca,
873 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
876 PegTOP Soft-Light alternative: A continuous version of the Softlight
877 function, producing very similar results however it does not take into
878 account alpha channel.
880 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
882 See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
884 if (fabs(Da) < MagickEpsilon)
886 return(Dca*Dca*(Sa-2*Sca)/Da+Sca*(2*Dca+1-Da)+Dca*(1-Sa));
889 static inline void CompositePegtopLight(const MagickPixelPacket *p,
890 const MagickRealType alpha,const MagickPixelPacket *q,
891 const MagickRealType beta,MagickPixelPacket *composite)
898 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
899 Da=1.0-QuantumScale*beta;
900 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
901 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
902 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
903 composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
905 composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
907 composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
909 if (q->colorspace == CMYKColorspace)
910 composite->index=gamma*PegtopLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
914 static MagickRealType PinLight(const MagickRealType Sca,
915 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
918 PinLight: A Photoshop 7 composition method
919 http://www.simplefilter.de/en/basics/mixmods.html
921 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
923 if (Dca*Sa < Da*(2*Sca-Sa))
924 return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
925 if ((Dca*Sa) > (2*Sca*Da))
926 return(Sca*Da+Sca+Dca*(1.0-Sa));
927 return(Sca*(1.0-Da)+Dca);
930 static inline void CompositePinLight(const MagickPixelPacket *p,
931 const MagickRealType alpha,const MagickPixelPacket *q,
932 const MagickRealType beta,MagickPixelPacket *composite)
939 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
940 Da=1.0-QuantumScale*beta;
941 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
942 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
943 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
944 composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
946 composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
948 composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
950 if (q->colorspace == CMYKColorspace)
951 composite->index=gamma*PinLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
955 static inline void CompositePlus(const MagickPixelPacket *p,
956 const MagickRealType alpha,const MagickPixelPacket *q,
957 const MagickRealType beta,MagickPixelPacket *composite)
959 MagickPixelCompositePlus(p,alpha,q,beta,composite);
962 static inline MagickRealType Screen(const MagickRealType Sca,
963 const MagickRealType Dca)
965 return(Sca+Dca-Sca*Dca);
968 static inline void CompositeScreen(const MagickPixelPacket *p,
969 const MagickRealType alpha,const MagickPixelPacket *q,
970 const MagickRealType beta,MagickPixelPacket *composite)
977 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
978 Da=1.0-QuantumScale*beta;
979 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
980 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
981 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
982 composite->red=gamma*Screen(QuantumScale*p->red*Sa,QuantumScale*
984 composite->green=gamma*Screen(QuantumScale*p->green*Sa,QuantumScale*
986 composite->blue=gamma*Screen(QuantumScale*p->blue*Sa,QuantumScale*
988 if (q->colorspace == CMYKColorspace)
989 composite->index=gamma*Screen(QuantumScale*p->index*Sa,QuantumScale*
993 static MagickRealType SoftLight(const MagickRealType Sca,
994 const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
998 Oct 2004 SVG specification -- spec discovered to be incorrect
999 See http://lists.w3.org/Archives/Public/www-svg/2009Feb/0014.html.
1002 return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1004 return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa)*(3.0-8.0*Dca/Da))+
1005 Sca*(1.0-Da)+Dca*(1.0-Sa));
1006 return((Dca*Sa+(pow(Dca/Da,0.5)*Da-Dca)*(2.0*Sca-Sa))+Sca*(1.0-Da)+
1014 New specification: March 2009 SVG specification.
1018 return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1019 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1021 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
1022 alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1025 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1030 static inline void CompositeSoftLight(const MagickPixelPacket *p,
1031 const MagickRealType alpha,const MagickPixelPacket *q,
1032 const MagickRealType beta,MagickPixelPacket *composite)
1039 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
1040 Da=1.0-QuantumScale*beta;
1041 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1042 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1043 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1044 composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1046 composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1048 composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1050 if (q->colorspace == CMYKColorspace)
1051 composite->index=gamma*SoftLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1055 static inline MagickRealType Subtract(const MagickRealType p,
1056 const MagickRealType magick_unused(alpha),const MagickRealType q,
1057 const MagickRealType magick_unused(beta))
1064 pixel+=(QuantumRange+1.0);
1068 static inline void CompositeSubtract(const MagickPixelPacket *p,
1069 const MagickRealType alpha,const MagickPixelPacket *q,
1070 const MagickRealType beta,MagickPixelPacket *composite)
1072 composite->red=Subtract(p->red,alpha,q->red,beta);
1073 composite->green=Subtract(p->green,alpha,q->green,beta);
1074 composite->blue=Subtract(p->blue,alpha,q->blue,beta);
1075 if (q->colorspace == CMYKColorspace)
1076 composite->index=Subtract(p->index,alpha,q->index,beta);
1079 static inline MagickRealType Threshold(const MagickRealType p,
1080 const MagickRealType magick_unused(alpha),const MagickRealType q,
1081 const MagickRealType magick_unused(beta),const MagickRealType threshold,
1082 const MagickRealType amount)
1088 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
1090 return(q+delta*amount);
1093 static inline void CompositeThreshold(const MagickPixelPacket *p,
1094 const MagickRealType alpha,const MagickPixelPacket *q,
1095 const MagickRealType beta,const MagickRealType threshold,
1096 const MagickRealType amount,MagickPixelPacket *composite)
1098 composite->red=Threshold(p->red,alpha,q->red,beta,threshold,amount);
1099 composite->green=Threshold(p->green,alpha,q->green,beta,threshold,amount);
1100 composite->blue=Threshold(p->blue,alpha,q->blue,beta,threshold,amount);
1101 composite->opacity=(MagickRealType) QuantumRange-
1102 Threshold(p->opacity,alpha,q->opacity,beta,threshold,amount);
1103 if (q->colorspace == CMYKColorspace)
1104 composite->index=Threshold(p->index,alpha,q->index,beta,threshold,amount);
1107 static MagickRealType VividLight(const MagickRealType Sca,
1108 const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
1111 VividLight: A Photoshop 7 composition method. See
1112 http://www.simplefilter.de/en/basics/mixmods.html.
1114 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
1116 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
1117 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1119 return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1120 return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1123 static inline void CompositeVividLight(const MagickPixelPacket *p,
1124 const MagickRealType alpha,const MagickPixelPacket *q,
1125 const MagickRealType beta,MagickPixelPacket *composite)
1132 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
1133 Da=1.0-QuantumScale*beta;
1134 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1135 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1136 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1137 composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1139 composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1141 composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1143 if (q->colorspace == CMYKColorspace)
1144 composite->index=gamma*VividLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1148 static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
1149 const MagickRealType Dca,const MagickRealType Da)
1151 return(Sca*(1-Da)+Dca*(1-Sa));
1154 static inline void CompositeXor(const MagickPixelPacket *p,
1155 const MagickRealType alpha,const MagickPixelPacket *q,
1156 const MagickRealType beta,MagickPixelPacket *composite)
1163 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
1164 Da=1.0-QuantumScale*beta;
1165 gamma=Sa+Da-2*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
1166 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1168 Optimized by multipling QuantumRange taken from gamma.
1170 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1171 composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
1172 composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
1173 composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
1174 if (q->colorspace == CMYKColorspace)
1175 composite->index=gamma*Xor(p->index*Sa,Sa,q->index*Da,Da);
1178 static void HSBComposite(const double hue,const double saturation,
1179 const double brightness,MagickRealType *red,MagickRealType *green,
1180 MagickRealType *blue)
1190 Convert HSB to RGB colorspace.
1192 assert(red != (MagickRealType *) NULL);
1193 assert(green != (MagickRealType *) NULL);
1194 assert(blue != (MagickRealType *) NULL);
1195 if (saturation == 0.0)
1197 *red=(MagickRealType) QuantumRange*brightness;
1202 h=6.0*(hue-floor(hue));
1203 f=h-floor((double) h);
1204 p=brightness*(1.0-saturation);
1205 q=brightness*(1.0-saturation*f);
1206 t=brightness*(1.0-saturation*(1.0-f));
1212 *red=(MagickRealType) QuantumRange*brightness;
1213 *green=(MagickRealType) QuantumRange*t;
1214 *blue=(MagickRealType) QuantumRange*p;
1219 *red=(MagickRealType) QuantumRange*q;
1220 *green=(MagickRealType) QuantumRange*brightness;
1221 *blue=(MagickRealType) QuantumRange*p;
1226 *red=(MagickRealType) QuantumRange*p;
1227 *green=(MagickRealType) QuantumRange*brightness;
1228 *blue=(MagickRealType) QuantumRange*t;
1233 *red=(MagickRealType) QuantumRange*p;
1234 *green=(MagickRealType) QuantumRange*q;
1235 *blue=(MagickRealType) QuantumRange*brightness;
1240 *red=(MagickRealType) QuantumRange*t;
1241 *green=(MagickRealType) QuantumRange*p;
1242 *blue=(MagickRealType) QuantumRange*brightness;
1247 *red=(MagickRealType) QuantumRange*brightness;
1248 *green=(MagickRealType) QuantumRange*p;
1249 *blue=(MagickRealType) QuantumRange*q;
1255 MagickExport MagickBooleanType CompositeImage(Image *image,
1256 const CompositeOperator compose,const Image *composite_image,
1257 const long x_offset,const long y_offset)
1262 status=CompositeImageChannel(image,DefaultChannels,compose,composite_image,
1267 MagickExport MagickBooleanType CompositeImageChannel(Image *image,
1268 const ChannelType magick_unused(channel),const CompositeOperator compose,
1269 const Image *composite_image,const long x_offset,const long y_offset)
1271 #define CompositeImageTag "Composite/Image"
1297 modify_outside_overlay,
1305 destination_dissolve,
1316 Prepare composite image.
1318 assert(image != (Image *) NULL);
1319 assert(image->signature == MagickSignature);
1320 if (image->debug != MagickFalse)
1321 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1322 assert(composite_image != (Image *) NULL);
1323 assert(composite_image->signature == MagickSignature);
1324 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1325 return(MagickFalse);
1326 GetMagickPixelPacket(image,&zero);
1327 destination_image=(Image *) NULL;
1329 destination_dissolve=1.0;
1330 modify_outside_overlay=MagickFalse;
1331 percent_brightness=100.0;
1332 percent_saturation=100.0;
1333 source_dissolve=1.0;
1337 case ClearCompositeOp:
1338 case SrcCompositeOp:
1340 case SrcInCompositeOp:
1341 case OutCompositeOp:
1342 case SrcOutCompositeOp:
1343 case DstInCompositeOp:
1344 case DstAtopCompositeOp:
1347 Modify destination outside the overlaid region.
1349 modify_outside_overlay=MagickTrue;
1352 case OverCompositeOp:
1354 if (image->matte != MagickFalse)
1356 if (composite_image->matte != MagickFalse)
1359 case CopyCompositeOp:
1361 if ((x_offset+(long) composite_image->columns) < 0)
1363 if ((x_offset+(long) composite_image->columns) >= (long) image->columns)
1365 if ((y_offset+(long) composite_image->rows) < 0)
1367 if ((y_offset+(long) composite_image->rows) >= (long) image->rows)
1370 exception=(&image->exception);
1371 image_view=AcquireCacheView(image);
1372 composite_view=AcquireCacheView(composite_image);
1373 #if defined(_OPENMP) && (_OPENMP >= 200203)
1374 #pragma omp parallel for schedule(static,1) shared(status)
1376 for (y=0; y < (long) composite_image->rows; y++)
1381 register const IndexPacket
1384 register const PixelPacket
1387 register IndexPacket
1390 register PixelPacket
1393 if (status == MagickFalse)
1395 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1397 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1398 composite_image->columns,1,exception);
1399 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1404 composite_indexes=GetCacheViewVirtualIndexQueue(composite_view);
1405 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1406 (void) CopyMagickMemory(q,p,composite_image->columns*sizeof(*p));
1407 if ((indexes != (IndexPacket *) NULL) &&
1408 (composite_indexes != (const IndexPacket *) NULL))
1409 (void) CopyMagickMemory(indexes,composite_indexes,
1410 composite_image->columns*sizeof(*indexes));
1411 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1412 if (sync == MagickFalse)
1414 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1419 #if defined(_OPENMP) && (_OPENMP >= 200203)
1420 #pragma omp critical (MagickCore_TextureImage)
1422 proceed=SetImageProgress(image,CompositeImageTag,y,image->rows);
1423 if (proceed == MagickFalse)
1427 composite_view=DestroyCacheView(composite_view);
1428 image_view=DestroyCacheView(image_view);
1431 case CopyOpacityCompositeOp:
1432 case ChangeMaskCompositeOp:
1435 Modify destination outside the overlaid region and require an alpha
1436 channel to exist, to add transparency.
1438 if (image->matte == MagickFalse)
1439 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1440 modify_outside_overlay=MagickTrue;
1443 case BlurCompositeOp:
1462 Blur Image dictated by an overlay gradient map:
1463 X = red_channel; Y = green_channel;
1464 compose:args = x_scale[,y_scale[,angle]]
1466 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1468 if (destination_image == (Image *) NULL)
1469 return(MagickFalse);
1471 Determine the horizontal and vertical maximim blur.
1473 SetGeometryInfo(&geometry_info);
1475 value=GetImageArtifact(composite_image,"compose:args");
1476 if (value != (char *) NULL)
1477 flags=ParseGeometry(value,&geometry_info);
1478 if ((flags & WidthValue) == 0 )
1480 destination_image=DestroyImage(destination_image);
1481 return(MagickFalse);
1483 blur_xu=geometry_info.rho;
1484 blur_yv=geometry_info.sigma;
1485 blur_xv=blur_yu = 0.0;
1486 if ((flags & HeightValue) == 0)
1488 if ((flags & XValue) != 0)
1497 angle=DegreesToRadians(geometry_info.xi);
1498 blur_xu=x*cos(angle);
1499 blur_xv=x*sin(angle);
1500 blur_yu=(-y*sin(angle));
1501 blur_yu=y*cos(angle);
1504 Blur Image by resampling;
1507 exception=(&image->exception);
1508 resample_filter=AcquireResampleFilter(image,&image->exception);
1509 SetResampleFilter(resample_filter,GaussianFilter,1.0);
1510 dest_view=AcquireCacheView(destination_image);
1511 composite_view=AcquireCacheView(composite_image);
1512 for (y=0; y < (long) composite_image->rows; y++)
1517 register const PixelPacket
1520 register PixelPacket
1523 register IndexPacket
1524 *__restrict destination_indexes;
1529 if (((y+y_offset) < 0) || ((y+y_offset) >= (long) image->rows))
1531 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1533 r=QueueCacheViewAuthenticPixels(dest_view,0,y,
1534 destination_image->columns,1,&image->exception);
1535 if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
1537 destination_indexes=GetCacheViewAuthenticIndexQueue(dest_view);
1538 for (x=0; x < (long) composite_image->columns; x++)
1540 if (((x_offset+x) < 0) || ((x_offset+x) >= (long) image->columns))
1545 ScaleResampleFilter(resample_filter,blur_xu*QuantumScale*p->red,
1546 blur_yu*QuantumScale*p->green,blur_xv*QuantumScale*p->red,
1547 blur_yv*QuantumScale*p->green);
1548 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1549 (double) y_offset+y,&pixel);
1550 SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
1554 sync=SyncCacheViewAuthenticPixels(dest_view,exception);
1555 if (sync == MagickFalse)
1558 resample_filter=DestroyResampleFilter(resample_filter);
1559 composite_view=DestroyCacheView(composite_view);
1560 dest_view=DestroyCacheView(dest_view);
1561 composite_image=destination_image;
1564 case DisplaceCompositeOp:
1565 case DistortCompositeOp:
1578 register IndexPacket
1579 *__restrict destination_indexes;
1581 register PixelPacket
1592 Displace/Distort based on overlay gradient map:
1593 X = red_channel; Y = green_channel;
1594 compose:args = x_scale[,y_scale[,x_center,y_center]]
1596 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1598 if (destination_image == (Image *) NULL)
1599 return(MagickFalse);
1600 SetGeometryInfo(&geometry_info);
1602 value=GetImageArtifact(composite_image,"compose:args");
1603 if (value != (char *) NULL)
1604 flags=ParseGeometry(value,&geometry_info);
1605 if ((flags & (WidthValue|HeightValue)) == 0 )
1606 if ((flags & AspectValue) == 0)
1608 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1610 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1614 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1615 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1619 horizontal_scale=geometry_info.rho;
1620 vertical_scale=geometry_info.sigma;
1621 if ((flags & PercentValue) != 0)
1623 if ((flags & AspectValue) == 0)
1625 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1626 vertical_scale*=(composite_image->rows-1.0)/200.0;
1630 horizontal_scale*=(image->columns-1.0)/200.0;
1631 vertical_scale*=(image->rows-1.0)/200.0;
1634 if ((flags & HeightValue) == 0)
1635 vertical_scale=horizontal_scale;
1638 Determine fixed center point for absolute distortion map
1640 Displace lookup relative to a fixed absolute point
1641 Select that point according to +X+Y user inputs.
1642 default = center of overlay image
1643 flag '!' = locations/percentage relative to background image
1645 x_center=(MagickRealType) x_offset;
1646 y_center=(MagickRealType) y_offset;
1647 if (compose == DistortCompositeOp)
1649 if ((flags & XValue) == 0)
1650 if ((flags & AspectValue) == 0)
1651 x_center=(MagickRealType) x_offset+ (composite_image->columns-1)/
1654 x_center=((MagickRealType) image->columns-1)/2.0;
1656 if ((flags & AspectValue) == 0)
1657 x_center=(MagickRealType) x_offset+geometry_info.xi;
1659 x_center=geometry_info.xi;
1660 if ((flags & YValue) == 0)
1661 if ((flags & AspectValue) == 0)
1662 y_center=(MagickRealType) y_offset+
1663 (composite_image->rows-1)/2.0;
1665 y_center=((MagickRealType) image->rows-1)/2.0;
1667 if ((flags & AspectValue) == 0)
1668 y_center=(MagickRealType) y_offset+geometry_info.psi;
1670 y_center=geometry_info.psi;
1673 Shift the pixel lookup point as defined by the provided,
1674 displacement/distortion map. -- Like a lens...
1677 exception=(&image->exception);
1678 resample_filter=AcquireResampleFilter(image,&image->exception);
1679 dest_view=AcquireCacheView(destination_image);
1680 composite_view=AcquireCacheView(composite_image);
1681 for (y=0; y < (long) composite_image->rows; y++)
1686 register const PixelPacket
1692 if (((y+y_offset) < 0) || ((y+y_offset) >= (long) image->rows))
1694 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1696 r=QueueCacheViewAuthenticPixels(dest_view,0,y,
1697 destination_image->columns,1,&image->exception);
1698 if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
1700 destination_indexes=GetCacheViewAuthenticIndexQueue(dest_view);
1701 for (x=0; x < (long) composite_image->columns; x++)
1703 if (((x_offset+x) < 0) || ((x_offset+x) >= (long) image->columns))
1709 Displace the lookup.
1711 x_lookup=(horizontal_scale*(p->red-(((MagickRealType) QuantumRange+
1712 1.0)/2.0)))/(((MagickRealType) QuantumRange+1.0)/2.0)+
1713 x_center+((compose == DisplaceCompositeOp) ? x : 0);
1714 y_lookup=(vertical_scale*(p->green-(((MagickRealType) QuantumRange+
1715 1.0)/2.0)))/(((MagickRealType) QuantumRange+1.0)/2.0)+
1716 y_center+((compose == DisplaceCompositeOp) ? y : 0);
1717 (void) ResamplePixelColor(resample_filter,(double) x_lookup,
1718 (double) y_lookup,&pixel);
1720 Mask with 'invalid pixel mask' in alpha channel.
1722 pixel.opacity = (MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1723 pixel.opacity)*(1.0-QuantumScale*p->opacity));
1724 SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
1728 sync=SyncCacheViewAuthenticPixels(dest_view,exception);
1729 if (sync == MagickFalse)
1732 resample_filter=DestroyResampleFilter(resample_filter);
1733 composite_view=DestroyCacheView(composite_view);
1734 dest_view=DestroyCacheView(dest_view);
1735 composite_image=destination_image;
1738 case DissolveCompositeOp:
1741 Geometry arguments to dissolve factors.
1743 value=GetImageArtifact(composite_image,"compose:args");
1744 if (value != (char *) NULL)
1746 flags=ParseGeometry(value,&geometry_info);
1747 source_dissolve=geometry_info.rho/100.0;
1748 destination_dissolve=1.0;
1749 if ((source_dissolve-MagickEpsilon) < 0.0)
1750 source_dissolve=0.0;
1751 if ((source_dissolve+MagickEpsilon) > 1.0)
1753 destination_dissolve=2.0-source_dissolve;
1754 source_dissolve=1.0;
1756 if ((flags & SigmaValue) != 0)
1757 destination_dissolve=geometry_info.sigma/100.0;
1758 if ((destination_dissolve-MagickEpsilon) < 0.0)
1759 destination_dissolve=0.0;
1760 modify_outside_overlay=MagickTrue;
1761 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1763 destination_dissolve=1.0;
1764 modify_outside_overlay=MagickFalse;
1769 case BlendCompositeOp:
1771 value=GetImageArtifact(composite_image,"compose:args");
1772 if (value != (char *) NULL)
1774 flags=ParseGeometry(value,&geometry_info);
1775 source_dissolve=geometry_info.rho/100.0;
1776 destination_dissolve=1.0-source_dissolve;
1777 if ((flags & SigmaValue) != 0)
1778 destination_dissolve=geometry_info.sigma/100.0;
1779 modify_outside_overlay=MagickTrue;
1780 if ((destination_dissolve+MagickEpsilon) > 1.0)
1781 modify_outside_overlay=MagickFalse;
1785 case MathematicsCompositeOp:
1788 Just collect the values from "compose:args", setting.
1789 Unused values are set to zero automagically.
1791 Arguments are normally a comma separated list, so this probably should
1792 be changed to some 'general comma list' parser, (with a minimum
1795 SetGeometryInfo(&geometry_info);
1796 value=GetImageArtifact(composite_image,"compose:args");
1797 if (value != (char *) NULL)
1798 (void) ParseGeometry(value,&geometry_info);
1801 case ModulateCompositeOp:
1804 Determine the brightness and saturation scale.
1806 value=GetImageArtifact(composite_image,"compose:args");
1807 if (value != (char *) NULL)
1809 flags=ParseGeometry(value,&geometry_info);
1810 percent_brightness=geometry_info.rho;
1811 if ((flags & SigmaValue) != 0)
1812 percent_saturation=geometry_info.sigma;
1816 case ThresholdCompositeOp:
1819 Determine the amount and threshold.
1821 value=GetImageArtifact(composite_image,"compose:args");
1822 if (value != (char *) NULL)
1824 flags=ParseGeometry(value,&geometry_info);
1825 amount=geometry_info.rho;
1826 threshold=geometry_info.sigma;
1827 if ((flags & SigmaValue) == 0)
1830 threshold*=QuantumRange;
1836 value=GetImageArtifact(composite_image,"compose:outside-overlay");
1837 if (value != (const char *) NULL)
1838 modify_outside_overlay=IsMagickTrue(value);
1844 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1845 GetMagickPixelPacket(composite_image,&zero);
1846 exception=(&image->exception);
1847 image_view=AcquireCacheView(image);
1848 composite_view=AcquireCacheView(composite_image);
1849 #if defined(_OPENMP) && (_OPENMP >= 200203)
1850 #pragma omp parallel for schedule(static,1) shared(progress,status)
1852 for (y=0; y < (long) image->rows; y++)
1867 register const IndexPacket
1868 *__restrict composite_indexes;
1870 register const PixelPacket
1873 register IndexPacket
1874 *__restrict indexes;
1879 register PixelPacket
1882 if (status == MagickFalse)
1884 if (modify_outside_overlay == MagickFalse)
1888 if ((y-y_offset) >= (long) composite_image->rows)
1892 If pixels is NULL, y is outside overlay region.
1894 pixels=(PixelPacket *) NULL;
1895 p=(PixelPacket *) NULL;
1896 if ((y >= y_offset) && ((y-y_offset) < (long) composite_image->rows))
1898 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1899 composite_image->columns,1,exception);
1900 if (p == (const PixelPacket *) NULL)
1909 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1911 if (q == (PixelPacket *) NULL)
1916 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1917 composite_indexes=GetCacheViewVirtualIndexQueue(composite_view);
1923 for (x=0; x < (long) image->columns; x++)
1925 if (modify_outside_overlay == MagickFalse)
1932 if ((x-x_offset) >= (long) composite_image->columns)
1935 destination.red=(MagickRealType) q->red;
1936 destination.green=(MagickRealType) q->green;
1937 destination.blue=(MagickRealType) q->blue;
1938 if (image->matte != MagickFalse)
1939 destination.opacity=(MagickRealType) q->opacity;
1940 if (image->colorspace == CMYKColorspace)
1942 destination.red=(MagickRealType) QuantumRange-destination.red;
1943 destination.green=(MagickRealType) QuantumRange-destination.green;
1944 destination.blue=(MagickRealType) QuantumRange-destination.blue;
1945 destination.index=(MagickRealType) (QuantumRange-indexes[x]);
1948 Handle destination modifications outside overlaid region.
1950 composite=destination;
1951 if ((pixels == (PixelPacket *) NULL) || (x < x_offset) ||
1952 ((x-x_offset) >= (long) composite_image->columns))
1956 case DissolveCompositeOp:
1957 case BlendCompositeOp:
1959 composite.opacity=(MagickRealType) (QuantumRange-
1960 destination_dissolve*(QuantumRange-composite.opacity));
1963 case ClearCompositeOp:
1964 case SrcCompositeOp:
1966 CompositeClear(&destination,&composite);
1970 case SrcInCompositeOp:
1971 case OutCompositeOp:
1972 case SrcOutCompositeOp:
1973 case DstInCompositeOp:
1974 case DstAtopCompositeOp:
1975 case CopyOpacityCompositeOp:
1976 case ChangeMaskCompositeOp:
1978 composite.opacity=(MagickRealType) TransparentOpacity;
1983 (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,
1984 y-y_offset,&composite,exception);
1988 if (image->colorspace == CMYKColorspace)
1990 composite.red=(MagickRealType) QuantumRange-composite.red;
1991 composite.green=(MagickRealType) QuantumRange-composite.green;
1992 composite.blue=(MagickRealType) QuantumRange-composite.blue;
1993 composite.index=(MagickRealType) QuantumRange-composite.index;
1995 q->red=RoundToQuantum(composite.red);
1996 q->green=RoundToQuantum(composite.green);
1997 q->blue=RoundToQuantum(composite.blue);
1998 if (image->matte != MagickFalse)
1999 q->opacity=RoundToQuantum(composite.opacity);
2000 if (image->colorspace == CMYKColorspace)
2001 indexes[x]=RoundToQuantum(composite.index);
2006 Handle normal overlay of source onto destination.
2008 source.red=(MagickRealType) p->red;
2009 source.green=(MagickRealType) p->green;
2010 source.blue=(MagickRealType) p->blue;
2011 if (composite_image->matte != MagickFalse)
2012 source.opacity=(MagickRealType) p->opacity;
2013 if (composite_image->colorspace == CMYKColorspace)
2015 source.red=(MagickRealType) QuantumRange-source.red;
2016 source.green=(MagickRealType) QuantumRange-source.green;
2017 source.blue=(MagickRealType) QuantumRange-source.blue;
2018 source.index=(MagickRealType) QuantumRange-(MagickRealType)
2019 composite_indexes[x-x_offset];
2023 case AddCompositeOp:
2025 CompositeAdd(&source,source.opacity,&destination,destination.opacity,
2029 case ClearCompositeOp:
2031 CompositeClear(&destination,&composite);
2034 case SrcCompositeOp:
2035 case CopyCompositeOp:
2036 case ReplaceCompositeOp:
2041 case ChangeMaskCompositeOp:
2043 if ((composite.opacity > ((MagickRealType) QuantumRange/2.0)) ||
2044 (IsMagickColorSimilar(&source,&destination) != MagickFalse))
2045 composite.opacity=(MagickRealType) TransparentOpacity;
2047 composite.opacity=(MagickRealType) OpaqueOpacity;
2050 case DivideCompositeOp:
2052 CompositeDivide(&source,source.opacity,&destination,
2053 destination.opacity,&composite);
2056 case DstCompositeOp:
2058 case OverCompositeOp:
2059 case SrcOverCompositeOp:
2061 CompositeOver(&source,source.opacity,&destination,destination.opacity,
2065 case DstOverCompositeOp:
2067 CompositeOver(&destination,destination.opacity,&source,source.opacity,
2071 case SrcInCompositeOp:
2074 CompositeIn(&source,source.opacity,&destination,destination.opacity,
2078 case DstInCompositeOp:
2080 CompositeIn(&destination,destination.opacity,&source,source.opacity,
2084 case OutCompositeOp:
2085 case SrcOutCompositeOp:
2087 CompositeOut(&source,source.opacity,&destination,destination.opacity,
2091 case DstOutCompositeOp:
2093 CompositeOut(&destination,destination.opacity,&source,source.opacity,
2097 case AtopCompositeOp:
2098 case SrcAtopCompositeOp:
2100 CompositeAtop(&source,source.opacity,&destination,destination.opacity,
2104 case DstAtopCompositeOp:
2106 CompositeAtop(&destination,destination.opacity,&source,source.opacity,
2110 case XorCompositeOp:
2112 CompositeXor(&source,source.opacity,&destination,destination.opacity,
2116 case PlusCompositeOp:
2118 CompositePlus(&source,source.opacity,&destination,destination.opacity,
2122 case MultiplyCompositeOp:
2124 CompositeMultiply(&source,source.opacity,&destination,
2125 destination.opacity,&composite);
2128 case ScreenCompositeOp:
2130 CompositeScreen(&source,source.opacity,&destination,
2131 destination.opacity,&composite);
2134 case DarkenCompositeOp:
2136 CompositeDarken(&source,source.opacity,&destination,
2137 destination.opacity,&composite);
2140 case LightenCompositeOp:
2142 CompositeLighten(&source,source.opacity,&destination,
2143 destination.opacity,&composite);
2146 case ColorDodgeCompositeOp:
2148 CompositeColorDodge(&source,source.opacity,&destination,
2149 destination.opacity,&composite);
2152 case ColorBurnCompositeOp:
2154 CompositeColorBurn(&source,source.opacity,&destination,
2155 destination.opacity,&composite);
2158 case LinearDodgeCompositeOp:
2160 CompositeLinearDodge(&source,source.opacity,&destination,
2161 destination.opacity,&composite);
2164 case LinearBurnCompositeOp:
2166 CompositeLinearBurn(&source,source.opacity,&destination,
2167 destination.opacity,&composite);
2170 case HardLightCompositeOp:
2172 CompositeHardLight(&source,source.opacity,&destination,
2173 destination.opacity,&composite);
2176 case OverlayCompositeOp:
2181 CompositeHardLight(&destination,destination.opacity,&source,
2182 source.opacity,&composite);
2185 case SoftLightCompositeOp:
2187 CompositeSoftLight(&source,source.opacity,&destination,
2188 destination.opacity,&composite);
2191 case LinearLightCompositeOp:
2193 CompositeLinearLight(&source,source.opacity,&destination,
2194 destination.opacity,&composite);
2197 case PegtopLightCompositeOp:
2199 CompositePegtopLight(&source,source.opacity,&destination,
2200 destination.opacity,&composite);
2203 case VividLightCompositeOp:
2205 CompositeVividLight(&source,source.opacity,&destination,
2206 destination.opacity,&composite);
2209 case PinLightCompositeOp:
2211 CompositePinLight(&source,source.opacity,&destination,
2212 destination.opacity,&composite);
2215 case DifferenceCompositeOp:
2217 CompositeDifference(&source,source.opacity,&destination,
2218 destination.opacity,&composite);
2221 case ExclusionCompositeOp:
2223 CompositeExclusion(&source,source.opacity,&destination,
2224 destination.opacity,&composite);
2227 case MinusCompositeOp:
2229 CompositeMinus(&source,source.opacity,&destination,
2230 destination.opacity,&composite);
2233 case BumpmapCompositeOp:
2235 if (source.opacity == TransparentOpacity)
2237 CompositeBumpmap(&source,source.opacity,&destination,
2238 destination.opacity,&composite);
2241 case DissolveCompositeOp:
2243 CompositeOver(&source,(MagickRealType) (QuantumRange-source_dissolve*
2244 (QuantumRange-source.opacity)),&destination,(MagickRealType)
2245 (QuantumRange-destination_dissolve*(QuantumRange-
2246 destination.opacity)),&composite);
2249 case BlendCompositeOp:
2251 MagickPixelCompositeBlend(&source,source_dissolve,&destination,
2252 destination_dissolve,&composite);
2255 case MathematicsCompositeOp:
2257 CompositeMathematics(&source,&destination,&geometry_info,&composite);
2260 case BlurCompositeOp:
2261 case DisplaceCompositeOp:
2262 case DistortCompositeOp:
2267 case ThresholdCompositeOp:
2269 CompositeThreshold(&source,source.opacity,&destination,
2270 destination.opacity,threshold,amount,&composite);
2273 case ModulateCompositeOp:
2278 if (source.opacity == TransparentOpacity)
2280 offset=(long) (MagickPixelIntensityToQuantum(&source)-midpoint);
2283 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2284 &saturation,&brightness);
2285 brightness+=(0.01*percent_brightness*offset)/midpoint;
2286 saturation*=0.01*percent_saturation;
2287 HSBComposite(hue,saturation,brightness,&composite.red,
2288 &composite.green,&composite.blue);
2291 case HueCompositeOp:
2293 if (source.opacity == TransparentOpacity)
2295 if (destination.opacity == TransparentOpacity)
2300 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2301 &saturation,&brightness);
2302 CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
2303 HSBComposite(hue,saturation,brightness,&composite.red,
2304 &composite.green,&composite.blue);
2305 if (source.opacity < destination.opacity)
2306 composite.opacity=source.opacity;
2309 case SaturateCompositeOp:
2311 if (source.opacity == TransparentOpacity)
2313 if (destination.opacity == TransparentOpacity)
2318 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2319 &saturation,&brightness);
2320 CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
2322 HSBComposite(hue,saturation,brightness,&composite.red,
2323 &composite.green,&composite.blue);
2324 if (source.opacity < destination.opacity)
2325 composite.opacity=source.opacity;
2328 case SubtractCompositeOp:
2330 CompositeSubtract(&source,source.opacity,&destination,
2331 destination.opacity,&composite);
2334 case LuminizeCompositeOp:
2336 if (source.opacity == TransparentOpacity)
2338 if (destination.opacity == TransparentOpacity)
2343 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2344 &saturation,&brightness);
2345 CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
2347 HSBComposite(hue,saturation,brightness,&composite.red,
2348 &composite.green,&composite.blue);
2349 if (source.opacity < destination.opacity)
2350 composite.opacity=source.opacity;
2353 case ColorizeCompositeOp:
2355 if (source.opacity == TransparentOpacity)
2357 if (destination.opacity == TransparentOpacity)
2362 CompositeHSB(destination.red,destination.green,destination.blue,&sans,
2364 CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
2366 HSBComposite(hue,saturation,brightness,&composite.red,
2367 &composite.green,&composite.blue);
2368 if (source.opacity < destination.opacity)
2369 composite.opacity=source.opacity;
2372 case CopyRedCompositeOp:
2373 case CopyCyanCompositeOp:
2375 composite.red=source.red;
2378 case CopyGreenCompositeOp:
2379 case CopyMagentaCompositeOp:
2381 composite.green=source.green;
2384 case CopyBlueCompositeOp:
2385 case CopyYellowCompositeOp:
2387 composite.blue=source.blue;
2390 case CopyOpacityCompositeOp:
2392 if (source.matte == MagickFalse)
2394 composite.opacity=(MagickRealType) (QuantumRange-
2395 MagickPixelIntensityToQuantum(&source));
2398 composite.opacity=source.opacity;
2401 case CopyBlackCompositeOp:
2403 if (source.colorspace != CMYKColorspace)
2404 ConvertRGBToCMYK(&source);
2405 composite.index=source.index;
2411 if (image->colorspace == CMYKColorspace)
2413 composite.red=(MagickRealType) QuantumRange-composite.red;
2414 composite.green=(MagickRealType) QuantumRange-composite.green;
2415 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2416 composite.index=(MagickRealType) QuantumRange-composite.index;
2418 q->red=RoundToQuantum(composite.red);
2419 q->green=RoundToQuantum(composite.green);
2420 q->blue=RoundToQuantum(composite.blue);
2421 q->opacity=RoundToQuantum(composite.opacity);
2422 if (image->colorspace == CMYKColorspace)
2423 indexes[x]=RoundToQuantum(composite.index);
2425 if (p >= (pixels+composite_image->columns))
2429 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2431 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2436 #if defined(_OPENMP) && (_OPENMP >= 200203)
2437 #pragma omp critical (MagickCore_CompositeImageChannel)
2439 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2441 if (proceed == MagickFalse)
2445 composite_view=DestroyCacheView(composite_view);
2446 image_view=DestroyCacheView(image_view);
2447 if (destination_image != (Image * ) NULL)
2448 destination_image=DestroyImage(destination_image);
2453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2457 % T e x t u r e I m a g e %
2461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2463 % TextureImage() repeatedly tiles the texture image across and down the image
2466 % The format of the TextureImage method is:
2468 % MagickBooleanType TextureImage(Image *image,const Image *texture)
2470 % A description of each parameter follows:
2472 % o image: the image.
2474 % o texture: This image is the texture to layer on the background.
2477 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
2479 #define TextureImageTag "Texture/Image"
2494 assert(image != (Image *) NULL);
2495 if (image->debug != MagickFalse)
2496 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2497 assert(image->signature == MagickSignature);
2498 if (texture == (const Image *) NULL)
2499 return(MagickFalse);
2500 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2501 return(MagickFalse);
2503 if ((image->compose != CopyCompositeOp) &&
2504 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2505 (texture->matte != MagickFalse)))
2508 Tile texture onto the image background.
2510 #if defined(_OPENMP) && (_OPENMP >= 200203)
2511 #pragma omp parallel for schedule(static,1) shared(status)
2513 for (y=0; y < (long) image->rows; y+=texture->rows)
2518 if (status == MagickFalse)
2520 for (x=0; x < (long) image->columns; x+=texture->columns)
2525 thread_status=CompositeImage(image,image->compose,texture,x+
2526 texture->tile_offset.x,y+texture->tile_offset.y);
2527 if (thread_status == MagickFalse)
2529 status=thread_status;
2533 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2538 #if defined(_OPENMP) && (_OPENMP >= 200203)
2539 #pragma omp critical (MagickCore_TextureImage)
2541 proceed=SetImageProgress(image,TextureImageTag,y,image->rows);
2542 if (proceed == MagickFalse)
2546 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2547 image->rows,image->rows);
2551 Tile texture onto the image background (optimized).
2554 exception=(&image->exception);
2555 image_view=AcquireCacheView(image);
2556 texture_view=AcquireCacheView(texture);
2557 #if defined(_OPENMP) && (_OPENMP >= 200203)
2558 #pragma omp parallel for schedule(static,1) shared(status)
2560 for (y=0; y < (long) image->rows; y++)
2565 register const IndexPacket
2568 register const PixelPacket
2571 register IndexPacket
2577 register PixelPacket
2583 if (status == MagickFalse)
2585 p=GetCacheViewVirtualPixels(texture_view,0,y % texture->rows,
2586 texture->columns,1,exception);
2587 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2589 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2594 texture_indexes=GetCacheViewVirtualIndexQueue(texture_view);
2595 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2596 for (x=0; x < (long) image->columns; x+=texture->columns)
2598 width=texture->columns;
2599 if ((x+(long) width) > (long) image->columns)
2600 width=image->columns-x;
2601 (void) CopyMagickMemory(q,p,width*sizeof(*p));
2602 if ((image->colorspace == CMYKColorspace) &&
2603 (texture->colorspace == CMYKColorspace))
2605 (void) CopyMagickMemory(indexes,texture_indexes,width*
2611 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2612 if (sync == MagickFalse)
2614 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2619 #if defined(_OPENMP) && (_OPENMP >= 200203)
2620 #pragma omp critical (MagickCore_TextureImage)
2622 proceed=SetImageProgress(image,TextureImageTag,y,image->rows);
2623 if (proceed == MagickFalse)
2627 texture_view=DestroyCacheView(texture_view);
2628 image_view=DestroyCacheView(image_view);