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-2011 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 "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/client.h"
48 #include "MagickCore/color.h"
49 #include "MagickCore/color-private.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/colorspace-private.h"
52 #include "MagickCore/composite.h"
53 #include "MagickCore/composite-private.h"
54 #include "MagickCore/constitute.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/fx.h"
57 #include "MagickCore/gem.h"
58 #include "MagickCore/geometry.h"
59 #include "MagickCore/image.h"
60 #include "MagickCore/image-private.h"
61 #include "MagickCore/list.h"
62 #include "MagickCore/log.h"
63 #include "MagickCore/monitor.h"
64 #include "MagickCore/monitor-private.h"
65 #include "MagickCore/memory_.h"
66 #include "MagickCore/option.h"
67 #include "MagickCore/pixel-accessor.h"
68 #include "MagickCore/property.h"
69 #include "MagickCore/quantum.h"
70 #include "MagickCore/resample.h"
71 #include "MagickCore/resource_.h"
72 #include "MagickCore/string_.h"
73 #include "MagickCore/thread-private.h"
74 #include "MagickCore/utility.h"
75 #include "MagickCore/version.h"
78 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 % C o m p o s i t e I m a g e %
86 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88 % CompositeImage() returns the second image composited onto the first
89 % at the specified offset, using the specified composite method.
91 % The format of the CompositeImage method is:
93 % MagickBooleanType CompositeImage(Image *image,
94 % const CompositeOperator compose,Image *composite_image,
95 % const ssize_t x_offset,const ssize_t y_offset)
97 % A description of each parameter follows:
99 % o image: the destination image, modified by he composition
101 % o compose: This operator affects how the composite is applied to
102 % the image. The operators and how they are utilized are listed here
103 % http://www.w3.org/TR/SVG12/#compositing.
105 % o composite_image: the composite (source) image.
107 % o x_offset: the column offset of the composited image.
109 % o y_offset: the row offset of the composited image.
111 % Extra Controls from Image meta-data in 'composite_image' (artifacts)
114 % A string containing extra numerical arguments for specific compose
115 % methods, generally expressed as a 'geometry' or a comma separated list
118 % Compose methods needing such arguments include "BlendCompositeOp" and
119 % "DisplaceCompositeOp".
121 % o "compose:outside-overlay"
122 % Modify how the composition is to effect areas not directly covered
123 % by the 'composite_image' at the offset given. Normally this is
124 % dependant on the 'compose' method, especially Duff-Porter methods.
126 % If set to "false" then disable all normal handling of pixels not
127 % covered by the composite_image. Typically used for repeated tiling
128 % of the composite_image by the calling API.
130 % Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
134 static inline double MagickMin(const double x,const double y)
140 static inline double MagickMax(const double x,const double y)
148 Programmers notes on SVG specification.
150 A Composition is defined by...
151 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
152 Blending areas : X = 1 for area of overlap ie: f(Sc,Dc)
153 Y = 1 for source preserved
154 Z = 1 for destination preserved
156 Conversion to transparency (then optimized)
157 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
158 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
161 Sca = Sc*Sa normalized Source color divided by Source alpha
162 Dca = Dc*Da normalized Dest color divided by Dest alpha
163 Dc' = Dca'/Da' the desired color value for this channel.
165 Da' in in the follow formula as 'gamma' The resulting alpla value.
167 Most functions use a blending mode of over (X=1,Y=1,Z=1)
168 this results in the following optimizations...
170 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
171 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
173 The above SVG definitions also definate that Mathematical Composition
174 methods should use a 'Over' blending mode for Alpha Channel.
175 It however was not applied for composition modes of 'Plus', 'Minus',
176 the modulus versions of 'Add' and 'Subtract'.
178 Mathematical operator changes to be applied from IM v6.7...
180 1/ Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
181 'ModulusAdd' and 'ModulusSubtract' for clarity.
183 2/ All mathematical compositions work as per the SVG specification
184 with regard to blending. This now includes 'ModulusAdd' and
187 3/ When the special channel flag 'sync' (syncronize channel updates)
188 is turned off (enabled by default) then mathematical compositions are
189 only performed on the channels specified, and are applied
190 independantally of each other. In other words the mathematics is
191 performed as 'pure' mathematical operations, rather than as image
195 static inline MagickRealType Atop(const MagickRealType p,
196 const MagickRealType Sa,const MagickRealType q,
197 const MagickRealType magick_unused(Da))
199 return(p*Sa+q*(1.0-Sa)); /* Da optimized out, Da/gamma => 1.0 */
202 static inline void CompositeAtop(const PixelInfo *p,const PixelInfo *q,
203 PixelInfo *composite)
208 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
209 composite->alpha=q->alpha; /* optimized Da = 1.0-Gamma */
210 composite->red=Atop(p->red,Sa,q->red,1.0);
211 composite->green=Atop(p->green,Sa,q->green,1.0);
212 composite->blue=Atop(p->blue,Sa,q->blue,1.0);
213 if (q->colorspace == CMYKColorspace)
214 composite->black=Atop(p->black,Sa,q->black,1.0);
218 What is this Composition method for? Can't find any specification!
219 WARNING this is not doing correct 'over' blend handling (Anthony Thyssen).
221 static inline void CompositeBumpmap(const PixelInfo *p,const PixelInfo *q,
222 PixelInfo *composite)
227 intensity=(MagickRealType) GetPixelInfoIntensity(p);
228 composite->red=QuantumScale*intensity*q->red;
229 composite->green=QuantumScale*intensity*q->green;
230 composite->blue=QuantumScale*intensity*q->blue;
231 composite->alpha=(MagickRealType) QuantumScale*intensity*p->alpha;
232 if (q->colorspace == CMYKColorspace)
233 composite->black=QuantumScale*intensity*q->black;
236 static inline void CompositeClear(const PixelInfo *q,PixelInfo *composite)
238 composite->alpha=(MagickRealType) TransparentAlpha;
240 composite->green=0.0;
242 if (q->colorspace == CMYKColorspace)
243 composite->black=0.0;
246 static MagickRealType ColorBurn(const MagickRealType Sca,
247 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
249 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
250 return(Sa*Da+Dca*(1.0-Sa));
251 if (Sca < MagickEpsilon)
252 return(Dca*(1.0-Sa));
253 return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
256 static inline void CompositeColorBurn(const PixelInfo *p,const PixelInfo *q,
257 PixelInfo *composite)
264 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
265 Da=QuantumScale*q->alpha;
266 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
267 composite->alpha=(MagickRealType) QuantumRange*gamma;
268 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
269 composite->red=gamma*ColorBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
271 composite->green=gamma*ColorBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
273 composite->blue=gamma*ColorBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
275 if (q->colorspace == CMYKColorspace)
276 composite->black=gamma*ColorBurn(QuantumScale*p->black*Sa,Sa,QuantumScale*
281 static MagickRealType ColorDodge(const MagickRealType Sca,
282 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
285 Working from first principles using the original formula:
289 This works correctly! Looks like the 2004 SVG model was right but just
290 required a extra condition for correct handling.
292 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
293 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
294 if (fabs(Sca-Sa) < MagickEpsilon)
295 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
296 return(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
299 static inline void CompositeColorDodge(const PixelInfo *p,const PixelInfo *q,
300 PixelInfo *composite)
307 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
308 Da=QuantumScale*q->alpha;
309 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
310 composite->alpha=(MagickRealType) QuantumRange*gamma;
311 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
312 composite->red=gamma*ColorDodge(QuantumScale*p->red*Sa,Sa,QuantumScale*
314 composite->green=gamma*ColorDodge(QuantumScale*p->green*Sa,Sa,QuantumScale*
316 composite->blue=gamma*ColorDodge(QuantumScale*p->blue*Sa,Sa,QuantumScale*
318 if (q->colorspace == CMYKColorspace)
319 composite->black=gamma*ColorDodge(QuantumScale*p->black*Sa,Sa,QuantumScale*
323 static inline MagickRealType Darken(const MagickRealType p,
324 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
327 return(MagickOver_(p,alpha,q,beta)); /* src-over */
328 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
331 static inline void CompositeDarken(const Image *image,const PixelInfo *p,
332 const PixelInfo *q,PixelInfo *composite)
338 Darken is equivalent to a 'Minimum' method OR a greyscale version of a
339 binary 'Or' OR the 'Intersection' of pixel sets.
341 if (image->sync == MagickFalse)
344 Handle channels as separate grayscale channels.
346 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
347 composite->red=MagickMin(p->red,q->red);
348 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
349 composite->green=MagickMin(p->green,q->green);
350 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
351 composite->blue=MagickMin(p->blue,q->blue);
352 if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 &&
353 (q->colorspace == CMYKColorspace))
354 composite->black=MagickMin(p->black,q->black);
355 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
356 composite->alpha=MagickMax(p->alpha,q->alpha);
359 composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */
360 gamma=1.0-QuantumScale*composite->alpha;
361 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
362 composite->red=gamma*Darken(p->red,p->alpha,q->red,q->alpha);
363 composite->green=gamma*Darken(p->green,p->alpha,q->green,q->alpha);
364 composite->blue=gamma*Darken(p->blue,p->alpha,q->blue,q->alpha);
365 if (q->colorspace == CMYKColorspace)
366 composite->black=gamma*Darken(p->black,p->alpha,q->black,q->alpha);
369 static inline void CompositeDarkenIntensity(const Image *image,
370 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
377 Select the pixel based on the intensity level.
378 If 'Sync' flag select whole pixel based on alpha weighted intensity.
379 Otherwise use intensity only, but restrict copy according to channel.
381 if (image->sync == MagickFalse)
386 from_p=GetPixelInfoIntensity(p) < GetPixelInfoIntensity(q) ? MagickTrue :
388 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
389 composite->red=from_p != MagickFalse ? p->red : q->red;
390 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
391 composite->green=from_p != MagickFalse ? p->green : q->green;
392 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
393 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
394 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
395 (q->colorspace == CMYKColorspace))
396 composite->black=from_p != MagickFalse ? p->black : q->black;
397 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
398 composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha;
401 Sa=QuantumScale*p->alpha;
402 Da=QuantumScale*q->alpha;
403 *composite=(Sa*GetPixelInfoIntensity(p) < Da*GetPixelInfoIntensity(q)) ?
407 static inline MagickRealType Difference(const MagickRealType p,
408 const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
411 Optimized by Multipling by QuantumRange (taken from gamma).
413 return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q));
416 static inline void CompositeDifference(const Image *image,const PixelInfo *p,
417 const PixelInfo *q,PixelInfo *composite)
424 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
425 Da=QuantumScale*q->alpha;
426 if (image->sync == MagickFalse)
429 Handle channels as separate grayscale channels.
431 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
432 composite->red=fabs((double) (p->red-q->red));
433 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
434 composite->green=fabs((double) (p->green-q->green));
435 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
436 composite->blue=fabs((double) (p->blue-q->blue));
437 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
438 (q->colorspace == CMYKColorspace))
439 composite->black=fabs((double) (p->black-q->black));
440 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
441 composite->alpha=fabs((double) (p->alpha-q->alpha));
444 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
445 composite->alpha=(MagickRealType) QuantumRange*gamma;
446 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
447 composite->red=gamma*Difference(p->red,Sa,q->red,Da);
448 composite->green=gamma*Difference(p->green,Sa,q->green,Da);
449 composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da);
450 if (q->colorspace == CMYKColorspace)
451 composite->black=gamma*Difference(p->black,Sa,q->black,Da);
454 static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
455 const MagickRealType Dca,const MagickRealType Da)
458 Divide Source by Destination
462 But with appropriate handling for special case of Dc == 0 specifically
463 so that f(Black,Black)=Black and f(non-Black,Black)=White.
464 It is however also important to correctly do 'over' alpha blending which
465 is why the formula becomes so complex.
467 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
468 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
469 if (fabs(Dca) < MagickEpsilon)
470 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
471 return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
474 static inline void CompositeDivide(const Image *image,const PixelInfo *p,
475 const PixelInfo *q,PixelInfo *composite)
482 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
483 Da=QuantumScale*q->alpha;
484 if (image->sync == MagickFalse)
487 Handle channels as separate grayscale channels.
489 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
490 composite->red=QuantumRange*Divide(QuantumScale*p->red,1.0,
491 QuantumScale*q->red,1.0);
492 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
493 composite->green=QuantumRange*Divide(QuantumScale*p->green,1.0,
494 QuantumScale*q->green,1.0);
495 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
496 composite->blue=QuantumRange*Divide(QuantumScale*p->blue,1.0,
497 QuantumScale*q->blue,1.0);
498 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
499 (q->colorspace == CMYKColorspace))
500 composite->black=QuantumRange*Divide(QuantumScale*p->black,1.0,
501 QuantumScale*q->black,1.0);
502 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
503 composite->alpha=QuantumRange*(1.0-Divide(Sa,1.0,Da,1.0));
506 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
507 composite->alpha=(MagickRealType) QuantumRange*gamma;
508 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
509 composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale*
511 composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale*
513 composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale*
515 if (q->colorspace == CMYKColorspace)
516 composite->black=gamma*Divide(QuantumScale*p->black*Sa,Sa,QuantumScale*
520 static MagickRealType Exclusion(const MagickRealType Sca,
521 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
523 return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
526 static inline void CompositeExclusion(const Image *image,const PixelInfo *p,
527 const PixelInfo *q,PixelInfo *composite)
534 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
535 Da=QuantumScale*q->alpha;
536 if (image->sync == MagickFalse)
539 Handle channels as separate grayscale channels.
541 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
542 composite->red=QuantumRange*Exclusion(QuantumScale*p->red,1.0,
543 QuantumScale*q->red,1.0);
544 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
545 composite->green=QuantumRange*Exclusion(QuantumScale*p->green,1.0,
546 QuantumScale*q->green,1.0);
547 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
548 composite->blue=QuantumRange*Exclusion(QuantumScale*p->blue,1.0,
549 QuantumScale*q->blue,1.0);
550 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
551 (q->colorspace == CMYKColorspace))
552 composite->black=QuantumRange*Exclusion(QuantumScale*p->black,1.0,
553 QuantumScale*q->black,1.0);
554 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
555 composite->alpha=QuantumRange*(1.0-Exclusion(Sa,1.0,Da,1.0));
558 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
559 composite->alpha=(MagickRealType) QuantumRange*gamma;
560 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
561 composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale*
563 composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale*
565 composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale*
567 if (q->colorspace == CMYKColorspace)
568 composite->black=gamma*Exclusion(QuantumScale*p->black*Sa,Sa,
569 QuantumScale*q->black*Da,Da);
572 static MagickRealType HardLight(const MagickRealType Sca,
573 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
576 return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
577 return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
580 static inline void CompositeHardLight(const PixelInfo *p,const PixelInfo *q,
581 PixelInfo *composite)
588 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
589 Da=QuantumScale*q->alpha;
590 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
591 composite->alpha=(MagickRealType) QuantumRange*gamma;
592 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
593 composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
595 composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
597 composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
599 if (q->colorspace == CMYKColorspace)
600 composite->black=gamma*HardLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
604 static void CompositeHSB(const MagickRealType red,const MagickRealType green,
605 const MagickRealType blue,double *hue,double *saturation,double *brightness)
613 Convert RGB to HSB colorspace.
615 assert(hue != (double *) NULL);
616 assert(saturation != (double *) NULL);
617 assert(brightness != (double *) NULL);
618 max=(red > green ? red : green);
621 min=(red < green ? red : green);
626 *brightness=(double) (QuantumScale*max);
629 *saturation=(double) (1.0-min/max);
634 *hue=(double) ((green-blue)/delta);
637 *hue=(double) (2.0+(blue-red)/delta);
640 *hue=(double) (4.0+(red-green)/delta);
646 static inline MagickRealType In(const MagickRealType p,const MagickRealType Sa,
647 const MagickRealType magick_unused(q),const MagickRealType Da)
652 static inline void CompositeIn(const PixelInfo *p,const PixelInfo *q,
653 PixelInfo *composite)
660 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
661 Da=QuantumScale*q->alpha;
663 composite->alpha=(MagickRealType) QuantumRange*gamma;
664 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
665 composite->red=gamma*In(p->red,Sa,q->red,Da);
666 composite->green=gamma*In(p->green,Sa,q->green,Da);
667 composite->blue=gamma*In(p->blue,Sa,q->blue,Da);
668 if (q->colorspace == CMYKColorspace)
669 composite->black=gamma*In(p->black,Sa,q->black,Da);
672 static inline MagickRealType Lighten(const MagickRealType p,
673 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
676 return(MagickOver_(p,alpha,q,beta)); /* src-over */
677 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
680 static inline void CompositeLighten(const Image *image,const PixelInfo *p,
681 const PixelInfo *q,PixelInfo *composite)
687 Lighten is also equvalent to a 'Maximum' method OR a greyscale version of a
688 binary 'And' OR the 'Union' of pixel sets.
690 if (image->sync == MagickFalse)
693 Handle channels as separate grayscale channels
695 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
696 composite->red=MagickMax(p->red,q->red);
697 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
698 composite->green=MagickMax(p->green,q->green);
699 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
700 composite->blue=MagickMax(p->blue,q->blue);
701 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
702 (q->colorspace == CMYKColorspace))
703 composite->black=MagickMax(p->black,q->black);
704 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
705 composite->alpha=MagickMin(p->alpha,q->alpha);
708 composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */
709 gamma=1.0-QuantumScale*composite->alpha;
710 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
711 composite->red=gamma*Lighten(p->red,p->alpha,q->red,q->alpha);
712 composite->green=gamma*Lighten(p->green,p->alpha,q->green,q->alpha);
713 composite->blue=gamma*Lighten(p->blue,p->alpha,q->blue,q->alpha);
714 if (q->colorspace == CMYKColorspace)
715 composite->black=gamma*Lighten(p->black,p->alpha,q->black,q->alpha);
718 static inline void CompositeLightenIntensity(const Image *image,
719 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
726 Select the pixel based on the intensity level.
727 If 'Sync' flag select whole pixel based on alpha weighted intensity.
728 Otherwise use Intenisty only, but restrict copy according to channel.
730 if (image->sync == MagickFalse)
735 from_p=GetPixelInfoIntensity(p) > GetPixelInfoIntensity(q) ? MagickTrue :
737 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
738 composite->red=from_p != MagickFalse ? p->red : q->red;
739 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
740 composite->green=from_p != MagickFalse ? p->green : q->green;
741 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
742 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
743 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
744 (q->colorspace == CMYKColorspace))
745 composite->black=from_p != MagickFalse ? p->black : q->black;
746 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
747 composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha;
750 Sa=QuantumScale*p->alpha;
751 Da=QuantumScale*q->alpha;
752 *composite=(Sa*GetPixelInfoIntensity(p) > Da*GetPixelInfoIntensity(q)) ?
756 static inline void CompositeLinearDodge(const PixelInfo *p,const PixelInfo *q,
757 PixelInfo *composite)
764 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
765 Da=QuantumScale*q->alpha;
766 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
767 composite->alpha=(MagickRealType) QuantumRange*gamma;
768 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
769 composite->red=gamma*(p->red*Sa+q->red*Da);
770 composite->green=gamma*(p->green*Sa+q->green*Da);
771 composite->blue=gamma*(p->blue*Sa+q->blue*Da);
772 if (q->colorspace == CMYKColorspace)
773 composite->black=gamma*(p->black*Sa+q->black*Da);
777 static inline MagickRealType LinearBurn(const MagickRealType Sca,
778 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
781 LinearBurn: as defined by Abode Photoshop, according to
782 http://www.simplefilter.de/en/basics/mixmods.html is:
784 f(Sc,Dc) = Sc + Dc - 1
786 return(Sca+Dca-Sa*Da);
789 static inline void CompositeLinearBurn(const PixelInfo *p,const PixelInfo *q,
790 PixelInfo *composite)
797 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
798 Da=QuantumScale*q->alpha;
799 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
800 composite->alpha=(MagickRealType) QuantumRange*gamma;
801 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
802 composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
804 composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
806 composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
808 if (q->colorspace == CMYKColorspace)
809 composite->black=gamma*LinearBurn(QuantumScale*p->black*Sa,Sa,QuantumScale*
813 static inline MagickRealType LinearLight(const MagickRealType Sca,
814 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
817 LinearLight: as defined by Abode Photoshop, according to
818 http://www.simplefilter.de/en/basics/mixmods.html is:
820 f(Sc,Dc) = Dc + 2*Sc - 1
822 return((Sca-Sa)*Da+Sca+Dca);
825 static inline void CompositeLinearLight(const PixelInfo *p,const PixelInfo *q,
826 PixelInfo *composite)
833 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
834 Da=QuantumScale*q->alpha;
835 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
836 composite->alpha=(MagickRealType) QuantumRange*gamma;
837 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
838 composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
840 composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
842 composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
844 if (q->colorspace == CMYKColorspace)
845 composite->black=gamma*LinearLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
849 static inline MagickRealType Mathematics(const MagickRealType Sca,
850 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
851 const GeometryInfo *geometry_info)
857 'Mathematics' a free form user control mathematical composition is defined
860 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
862 Where the arguments A,B,C,D are (currently) passed to composite as
863 a command separated 'geometry' string in "compose:args" image artifact.
865 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
867 Applying the SVG transparency formula (see above), we get...
869 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
871 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
874 gamma=geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
875 geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
880 static inline void CompositeMathematics(const Image *image,const PixelInfo *p,
881 const PixelInfo *q,const GeometryInfo *args,PixelInfo *composite)
888 Sa=QuantumScale*p->alpha; /* ??? - AT */
889 Da=QuantumScale*q->alpha;
890 if (image->sync == MagickFalse)
893 Handle channels as separate grayscale channels.
895 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
896 composite->red=QuantumRange*Mathematics(QuantumScale*p->red,1.0,
897 QuantumScale*q->red,1.0,args);
898 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
899 composite->green=QuantumRange*Mathematics(QuantumScale*p->green,1.0,
900 QuantumScale*q->green,1.0,args);
901 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
902 composite->blue=QuantumRange*Mathematics(QuantumScale*p->blue,1.0,
903 QuantumScale*q->blue,1.0,args);
904 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
905 (q->colorspace == CMYKColorspace))
906 composite->black=QuantumRange*Mathematics(QuantumScale*p->black,1.0,
907 QuantumScale*q->black,1.0,args);
908 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
909 composite->alpha=QuantumRange*(1.0-Mathematics(Sa,1.0,Da,1.0,args));
912 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
913 composite->alpha=(MagickRealType) QuantumRange*gamma;
914 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
915 composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
917 composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,
918 QuantumScale*q->green*Da,Da,args);
919 composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
921 if (q->colorspace == CMYKColorspace)
922 composite->black=gamma*Mathematics(QuantumScale*p->black*Sa,Sa,
923 QuantumScale*q->black*Da,Da,args);
926 static inline void CompositePlus(const Image *image,const PixelInfo *p,
927 const PixelInfo *q,PixelInfo *composite)
930 NOTE: "Plus" does not use 'over' alpha-blending but uses a special
931 'plus' form of alph-blending. It is the ONLY mathematical operator to
932 do this. this is what makes it different to the otherwise equivalent
933 "LinearDodge" composition method.
935 Note however that color channels are still effected by the alpha channel
936 as a result of the blending, making it just as useless for independant
937 channel maths, just like all other mathematical composition methods.
939 As such the removal of the 'sync' flag, is still a usful convention.
941 The CompositePixelInfoPlus() function is defined in
942 "composite-private.h" so it can also be used for Image Blending.
944 if (image->sync == MagickFalse)
947 Handle channels as separate grayscale channels.
949 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
950 composite->red=p->red+q->red;
951 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
952 composite->green=p->green+q->green;
953 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
954 composite->blue=p->blue+q->blue;
955 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
956 (q->colorspace == CMYKColorspace))
957 composite->black=p->black+q->black;
958 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
959 composite->alpha=p->alpha+q->alpha-QuantumRange;
962 CompositePixelInfoPlus(p,p->alpha,q,q->alpha,composite);
965 static inline MagickRealType Minus(const MagickRealType Sca,
966 const MagickRealType Sa,const MagickRealType Dca,
967 const MagickRealType magick_unused(Da))
970 Minus Source from Destination
974 return(Sca+Dca-2.0*Dca*Sa);
977 static inline void CompositeMinus(const Image *image,const PixelInfo *p,
978 const PixelInfo *q,PixelInfo *composite)
985 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
986 Da=QuantumScale*q->alpha;
987 if (image->sync == MagickFalse)
990 Handle channels as separate grayscale channels.
992 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
993 composite->red=p->red-q->red;
994 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
995 composite->green=p->green-q->green;
996 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
997 composite->blue=p->blue-q->blue;
998 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
999 (q->colorspace == CMYKColorspace))
1000 composite->black=p->black-q->black;
1001 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1002 composite->alpha=QuantumRange*(1.0-(Sa-Da));
1005 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1006 composite->alpha=(MagickRealType) QuantumRange*gamma;
1007 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1008 composite->red=gamma*Minus(p->red*Sa,Sa,q->red*Da,Da);
1009 composite->green=gamma*Minus(p->green*Sa,Sa,q->green*Da,Da);
1010 composite->blue=gamma*Minus(p->blue*Sa,Sa,q->blue*Da,Da);
1011 if (q->colorspace == CMYKColorspace)
1012 composite->black=gamma*Minus(p->black*Sa,Sa,q->black*Da,Da);
1015 static inline MagickRealType ModulusAdd(const MagickRealType p,
1016 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1022 if (pixel > QuantumRange)
1023 pixel-=(QuantumRange+1.0);
1024 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1027 static inline void CompositeModulusAdd(const Image *image,const PixelInfo *p,
1028 const PixelInfo *q,PixelInfo *composite)
1035 if (image->sync == MagickFalse)
1038 Handle channels as separate grayscale channels.
1040 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1041 composite->red=ModulusAdd(p->red,1.0,q->red,1.0);
1042 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1043 composite->green=ModulusAdd(p->green,1.0,q->green,1.0);
1044 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1045 composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0);
1046 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1047 (q->colorspace == CMYKColorspace))
1048 composite->black=ModulusAdd(p->black,1.0,q->black,1.0);
1049 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1050 composite->alpha=ModulusAdd(p->alpha,1.0,q->alpha,1.0);
1053 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1054 Da=QuantumScale*q->alpha;
1055 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1056 composite->alpha=(MagickRealType) QuantumRange*gamma;
1057 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1058 composite->red=ModulusAdd(p->red,Sa,q->red,Da);
1059 composite->green=ModulusAdd(p->green,Sa,q->green,Da);
1060 composite->blue=ModulusAdd(p->blue,Sa,q->blue,Da);
1061 if (q->colorspace == CMYKColorspace)
1062 composite->black=ModulusAdd(p->black,Sa,q->black,Da);
1065 static inline MagickRealType ModulusSubtract(const MagickRealType p,
1066 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1073 pixel+=(QuantumRange+1.0);
1074 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1077 static inline void CompositeModulusSubtract(const Image *image,
1078 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
1085 if (image->sync == MagickFalse)
1088 Handle channels as separate grayscale channels,
1090 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1091 composite->red=ModulusSubtract(p->red,1.0,q->red,1.0);
1092 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1093 composite->green=ModulusSubtract(p->green,1.0,q->green,1.0);
1094 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1095 composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0);
1096 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1097 (q->colorspace == CMYKColorspace))
1098 composite->black=ModulusSubtract(p->black,1.0,q->black,1.0);
1099 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1100 composite->alpha=ModulusSubtract(p->alpha,1.0,q->alpha,1.0);
1103 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1104 Da=QuantumScale*q->alpha;
1105 gamma = RoundToUnity(Sa+Da-Sa*Da);
1106 composite->alpha=(MagickRealType) QuantumRange*gamma;
1107 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1108 composite->red=ModulusSubtract(p->red,Sa,q->red,Da);
1109 composite->green=ModulusSubtract(p->green,Sa,q->green,Da);
1110 composite->blue=ModulusSubtract(p->blue,Sa,q->blue,Da);
1111 if (q->colorspace == CMYKColorspace)
1112 composite->black=ModulusSubtract(p->black,Sa,q->black,Da);
1115 static inline MagickRealType Multiply(const MagickRealType Sca,
1116 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1118 return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1121 static inline void CompositeMultiply(const Image *image,const PixelInfo *p,
1122 const PixelInfo *q,PixelInfo *composite)
1129 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1130 Da=QuantumScale*q->alpha;
1131 if (image->sync == MagickFalse)
1134 Handle channels as separate grayscale channels.
1136 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1137 composite->red=QuantumScale*p->red*q->red;
1138 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1139 composite->green=QuantumScale*p->green*q->green;
1140 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1141 composite->blue=QuantumScale*p->blue*q->blue;
1142 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1143 (q->colorspace == CMYKColorspace))
1144 composite->black=QuantumScale*p->black*q->black;
1145 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1146 composite->alpha=QuantumRange*(1.0-Sa*Da);
1149 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1150 composite->alpha=(MagickRealType) QuantumRange*gamma;
1151 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1152 composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
1154 composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
1156 composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1158 if (q->colorspace == CMYKColorspace)
1159 composite->black=gamma*Multiply(QuantumScale*p->black*Sa,Sa,
1160 QuantumScale*q->black*Da,Da);
1163 static inline MagickRealType Out(const MagickRealType p,const MagickRealType Sa,
1164 const MagickRealType magick_unused(q),const MagickRealType Da)
1166 return(Sa*p*(1.0-Da));
1169 static inline void CompositeOut(const PixelInfo *p,const PixelInfo *q,
1170 PixelInfo *composite)
1177 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1178 Da=QuantumScale*q->alpha;
1180 composite->alpha=(MagickRealType) QuantumRange*gamma;
1181 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1182 composite->red=gamma*Out(p->red,Sa,q->red,Da);
1183 composite->green=gamma*Out(p->green,Sa,q->green,Da);
1184 composite->blue=gamma*Out(p->blue,Sa,q->blue,Da);
1185 if (q->colorspace == CMYKColorspace)
1186 composite->black=gamma*Out(p->black,Sa,q->black,Da);
1189 static MagickRealType PegtopLight(const MagickRealType Sca,
1190 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1193 PegTop: A Soft-Light alternative: A continuous version of the Softlight
1194 function, producing very similar results.
1196 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
1198 See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
1200 if (fabs(Da) < MagickEpsilon)
1202 return(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+Dca*(1.0-Sa));
1205 static inline void CompositePegtopLight(const PixelInfo *p,const PixelInfo *q,
1206 PixelInfo *composite)
1213 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1214 Da=QuantumScale*q->alpha;
1215 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1216 composite->alpha=(MagickRealType) QuantumRange*gamma;
1217 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1218 composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1220 composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1222 composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1224 if (q->colorspace == CMYKColorspace)
1225 composite->black=gamma*PegtopLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1229 static MagickRealType PinLight(const MagickRealType Sca,const MagickRealType Sa,
1230 const MagickRealType Dca,const MagickRealType Da)
1233 PinLight: A Photoshop 7 composition method
1234 http://www.simplefilter.de/en/basics/mixmods.html
1236 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
1238 if (Dca*Sa < Da*(2.0*Sca-Sa))
1239 return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
1240 if ((Dca*Sa) > (2.0*Sca*Da))
1241 return(Sca*Da+Sca+Dca*(1.0-Sa));
1242 return(Sca*(1.0-Da)+Dca);
1245 static inline void CompositePinLight(const PixelInfo *p,const PixelInfo *q,
1246 PixelInfo *composite)
1253 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1254 Da=QuantumScale*q->alpha;
1255 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1256 composite->alpha=(MagickRealType) QuantumRange*gamma;
1257 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1258 composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1260 composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1262 composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1264 if (q->colorspace == CMYKColorspace)
1265 composite->black=gamma*PinLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1269 static inline MagickRealType Screen(const MagickRealType Sca,
1270 const MagickRealType Dca)
1273 Screen: A negated multiply
1274 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
1276 return(Sca+Dca-Sca*Dca);
1279 static inline void CompositeScreen(const Image *image,const PixelInfo *p,
1280 const PixelInfo *q,PixelInfo *composite)
1287 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1288 Da=QuantumScale*q->alpha;
1289 if (image->sync == MagickFalse)
1292 Handle channels as separate grayscale channels.
1294 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1295 composite->red=QuantumRange*Screen(QuantumScale*p->red,
1296 QuantumScale*q->red);
1297 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1298 composite->green=QuantumRange*Screen(QuantumScale*p->green,
1299 QuantumScale*q->green);
1300 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1301 composite->blue=QuantumRange*Screen(QuantumScale*p->blue,
1302 QuantumScale*q->blue);
1303 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1304 (q->colorspace == CMYKColorspace))
1305 composite->black=QuantumRange*Screen(QuantumScale*p->black,
1306 QuantumScale*q->black);
1307 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1308 composite->alpha=QuantumRange*(1.0-Screen(Sa,Da));
1311 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1312 composite->alpha=(MagickRealType) QuantumRange*gamma;
1313 Sa*=QuantumScale; Da*=QuantumScale; /* optimization */
1314 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1315 composite->red=gamma*Screen(p->red*Sa,q->red*Da);
1316 composite->green=gamma*Screen(p->green*Sa,q->green*Da);
1317 composite->blue=gamma*Screen(p->blue*Sa,q->blue*Da);
1318 if (q->colorspace == CMYKColorspace)
1319 composite->black=gamma*Screen(p->black*Sa,q->black*Da);
1322 static MagickRealType SoftLight(const MagickRealType Sca,
1323 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1330 New specification: March 2009 SVG specification.
1334 return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1335 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1337 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
1338 alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1341 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1345 static inline void CompositeSoftLight(const PixelInfo *p,const PixelInfo *q,
1346 PixelInfo *composite)
1353 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1354 Da=QuantumScale*q->alpha;
1355 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1356 composite->alpha=(MagickRealType) QuantumRange*gamma;
1357 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1358 composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1360 composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1362 composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1364 if (q->colorspace == CMYKColorspace)
1365 composite->black=gamma*SoftLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1369 static inline MagickRealType Threshold(const MagickRealType p,
1370 const MagickRealType q,const MagickRealType threshold,
1371 const MagickRealType amount)
1377 Multiply difference by amount, if differance larger than threshold???
1378 What use this is is completely unknown. The Opacity calculation appears to
1379 be inverted -- Anthony Thyssen
1384 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
1386 return(q+delta*amount);
1389 static inline void CompositeThreshold(const PixelInfo *p,const PixelInfo *q,
1390 const MagickRealType threshold,const MagickRealType amount,
1391 PixelInfo *composite)
1393 composite->red=Threshold(p->red,q->red,threshold,amount);
1394 composite->green=Threshold(p->green,q->green,threshold,amount);
1395 composite->blue=Threshold(p->blue,q->blue,threshold,amount);
1396 composite->alpha=Threshold(p->alpha,q->alpha,threshold,amount);
1397 if (q->colorspace == CMYKColorspace)
1398 composite->black=Threshold(p->black,q->black,threshold,amount);
1402 static MagickRealType VividLight(const MagickRealType Sca,
1403 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1406 VividLight: A Photoshop 7 composition method. See
1407 http://www.simplefilter.de/en/basics/mixmods.html.
1409 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
1411 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
1412 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1413 if ((2.0*Sca) <= Sa)
1414 return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1415 return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1418 static inline void CompositeVividLight(const PixelInfo *p,const PixelInfo *q,
1419 PixelInfo *composite)
1426 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1427 Da=QuantumScale*q->alpha;
1428 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1429 composite->alpha=(MagickRealType) QuantumRange*gamma;
1430 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1431 composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1433 composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1435 composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1437 if (q->colorspace == CMYKColorspace)
1438 composite->black=gamma*VividLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1442 static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
1443 const MagickRealType Dca,const MagickRealType Da)
1445 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
1448 static inline void CompositeXor(const PixelInfo *p,const PixelInfo *q,
1449 PixelInfo *composite)
1456 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1457 Da=QuantumScale*q->alpha;
1458 gamma=Sa+Da-2.0*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
1459 composite->alpha=(MagickRealType) QuantumRange*gamma;
1460 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1461 composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
1462 composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
1463 composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
1464 if (q->colorspace == CMYKColorspace)
1465 composite->black=gamma*Xor(p->black*Sa,Sa,q->black*Da,Da);
1468 static void HSBComposite(const double hue,const double saturation,
1469 const double brightness,MagickRealType *red,MagickRealType *green,
1470 MagickRealType *blue)
1480 Convert HSB to RGB colorspace.
1482 assert(red != (MagickRealType *) NULL);
1483 assert(green != (MagickRealType *) NULL);
1484 assert(blue != (MagickRealType *) NULL);
1485 if (saturation == 0.0)
1487 *red=(MagickRealType) QuantumRange*brightness;
1492 h=6.0*(hue-floor(hue));
1493 f=h-floor((double) h);
1494 p=brightness*(1.0-saturation);
1495 q=brightness*(1.0-saturation*f);
1496 t=brightness*(1.0-saturation*(1.0-f));
1502 *red=(MagickRealType) QuantumRange*brightness;
1503 *green=(MagickRealType) QuantumRange*t;
1504 *blue=(MagickRealType) QuantumRange*p;
1509 *red=(MagickRealType) QuantumRange*q;
1510 *green=(MagickRealType) QuantumRange*brightness;
1511 *blue=(MagickRealType) QuantumRange*p;
1516 *red=(MagickRealType) QuantumRange*p;
1517 *green=(MagickRealType) QuantumRange*brightness;
1518 *blue=(MagickRealType) QuantumRange*t;
1523 *red=(MagickRealType) QuantumRange*p;
1524 *green=(MagickRealType) QuantumRange*q;
1525 *blue=(MagickRealType) QuantumRange*brightness;
1530 *red=(MagickRealType) QuantumRange*t;
1531 *green=(MagickRealType) QuantumRange*p;
1532 *blue=(MagickRealType) QuantumRange*brightness;
1537 *red=(MagickRealType) QuantumRange*brightness;
1538 *green=(MagickRealType) QuantumRange*p;
1539 *blue=(MagickRealType) QuantumRange*q;
1545 MagickExport MagickBooleanType CompositeImage(Image *image,
1546 const CompositeOperator compose,const Image *composite_image,
1547 const ssize_t x_offset,const ssize_t y_offset)
1549 #define CompositeImageTag "Composite/Image"
1571 modify_outside_overlay,
1582 destination_dissolve,
1596 Prepare composite image.
1598 assert(image != (Image *) NULL);
1599 assert(image->signature == MagickSignature);
1600 if (image->debug != MagickFalse)
1601 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1602 assert(composite_image != (Image *) NULL);
1603 assert(composite_image->signature == MagickSignature);
1604 exception=(&image->exception);
1605 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1606 return(MagickFalse);
1607 GetPixelInfo(image,&zero);
1608 destination_image=(Image *) NULL;
1610 destination_dissolve=1.0;
1611 modify_outside_overlay=MagickFalse;
1612 percent_brightness=100.0;
1613 percent_saturation=100.0;
1614 source_dissolve=1.0;
1618 case ClearCompositeOp:
1619 case SrcCompositeOp:
1621 case SrcInCompositeOp:
1622 case OutCompositeOp:
1623 case SrcOutCompositeOp:
1624 case DstInCompositeOp:
1625 case DstAtopCompositeOp:
1628 Modify destination outside the overlaid region.
1630 modify_outside_overlay=MagickTrue;
1633 case CopyCompositeOp:
1635 if ((x_offset < 0) || (y_offset < 0))
1637 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
1639 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
1642 image_view=AcquireCacheView(image);
1643 composite_view=AcquireCacheView(composite_image);
1644 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1645 #pragma omp parallel for schedule(dynamic,4) shared(status)
1647 for (y=0; y < (ssize_t) composite_image->rows; y++)
1652 register const Quantum
1661 if (status == MagickFalse)
1663 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1665 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1666 composite_image->columns,1,exception);
1667 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1672 for (x=0; x < (ssize_t) composite_image->columns; x++)
1674 SetPixelRed(image,GetPixelRed(composite_image,p),q);
1675 SetPixelGreen(image,GetPixelGreen(composite_image,p),q);
1676 SetPixelBlue(image,GetPixelBlue(composite_image,p),q);
1677 SetPixelAlpha(image,GetPixelAlpha(composite_image,p),q);
1678 if (image->colorspace == CMYKColorspace)
1679 SetPixelBlack(image,GetPixelBlack(composite_image,p),q);
1680 p+=GetPixelChannels(composite_image);
1681 q+=GetPixelChannels(image);
1683 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1684 if (sync == MagickFalse)
1686 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1691 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1692 #pragma omp critical (MagickCore_CompositeImage)
1694 proceed=SetImageProgress(image,CompositeImageTag,
1695 (MagickOffsetType) y,image->rows);
1696 if (proceed == MagickFalse)
1700 composite_view=DestroyCacheView(composite_view);
1701 image_view=DestroyCacheView(image_view);
1704 case CopyOpacityCompositeOp:
1705 case ChangeMaskCompositeOp:
1708 Modify destination outside the overlaid region and require an alpha
1709 channel to exist, to add transparency.
1711 if (image->matte == MagickFalse)
1712 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1713 modify_outside_overlay=MagickTrue;
1716 case BlurCompositeOp:
1738 Blur Image dictated by an overlay gradient map: X = red_channel;
1739 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
1741 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1743 if (destination_image == (Image *) NULL)
1744 return(MagickFalse);
1746 Determine the horizontal and vertical maximim blur.
1748 SetGeometryInfo(&geometry_info);
1750 value=GetImageArtifact(composite_image,"compose:args");
1751 if (value != (char *) NULL)
1752 flags=ParseGeometry(value,&geometry_info);
1753 if ((flags & WidthValue) == 0 )
1755 destination_image=DestroyImage(destination_image);
1756 return(MagickFalse);
1758 width=geometry_info.rho;
1759 height=geometry_info.sigma;
1760 blur.x1=geometry_info.rho;
1763 blur.y2=geometry_info.sigma;
1766 if ((flags & HeightValue) == 0)
1768 if ((flags & XValue) != 0 )
1773 angle=DegreesToRadians(geometry_info.xi);
1774 blur.x1=width*cos(angle);
1775 blur.x2=width*sin(angle);
1776 blur.y1=(-height*sin(angle));
1777 blur.y2=height*cos(angle);
1779 if ((flags & YValue) != 0 )
1781 angle_start=DegreesToRadians(geometry_info.xi);
1782 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1785 Blur Image by resampling.
1788 exception=(&image->exception);
1789 resample_filter=AcquireResampleFilter(image,&image->exception);
1790 SetResampleFilter(resample_filter,CubicFilter,2.0);
1791 destination_view=AcquireCacheView(destination_image);
1792 composite_view=AcquireCacheView(composite_image);
1793 for (y=0; y < (ssize_t) composite_image->rows; y++)
1798 register const Quantum
1807 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1809 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1811 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1812 destination_image->columns,1,&image->exception);
1813 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1815 for (x=0; x < (ssize_t) composite_image->columns; x++)
1817 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1819 p+=GetPixelChannels(composite_image);
1822 if (fabs(angle_range) > MagickEpsilon)
1827 angle=angle_start+angle_range*QuantumScale*
1828 GetPixelBlue(composite_image,p);
1829 blur.x1=width*cos(angle);
1830 blur.x2=width*sin(angle);
1831 blur.y1=(-height*sin(angle));
1832 blur.y2=height*cos(angle);
1834 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
1835 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
1836 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
1837 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
1838 GetPixelGreen(composite_image,p));
1839 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1840 (double) y_offset+y,&pixel);
1841 SetPixelPixelInfo(destination_image,&pixel,q);
1842 p+=GetPixelChannels(composite_image);
1843 q+=GetPixelChannels(destination_image);
1845 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1846 if (sync == MagickFalse)
1849 resample_filter=DestroyResampleFilter(resample_filter);
1850 composite_view=DestroyCacheView(composite_view);
1851 destination_view=DestroyCacheView(destination_view);
1852 composite_image=destination_image;
1855 case DisplaceCompositeOp:
1856 case DistortCompositeOp:
1875 Displace/Distort based on overlay gradient map:
1876 X = red_channel; Y = green_channel;
1877 compose:args = x_scale[,y_scale[,center.x,center.y]]
1879 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1881 if (destination_image == (Image *) NULL)
1882 return(MagickFalse);
1883 SetGeometryInfo(&geometry_info);
1885 value=GetImageArtifact(composite_image,"compose:args");
1886 if (value != (char *) NULL)
1887 flags=ParseGeometry(value,&geometry_info);
1888 if ((flags & (WidthValue|HeightValue)) == 0 )
1890 if ((flags & AspectValue) == 0)
1892 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1894 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1898 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1899 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1904 horizontal_scale=geometry_info.rho;
1905 vertical_scale=geometry_info.sigma;
1906 if ((flags & PercentValue) != 0)
1908 if ((flags & AspectValue) == 0)
1910 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1911 vertical_scale*=(composite_image->rows-1.0)/200.0;
1915 horizontal_scale*=(image->columns-1.0)/200.0;
1916 vertical_scale*=(image->rows-1.0)/200.0;
1919 if ((flags & HeightValue) == 0)
1920 vertical_scale=horizontal_scale;
1923 Determine fixed center point for absolute distortion map
1925 Displace offset relative to a fixed absolute point
1926 Select that point according to +X+Y user inputs.
1927 default = center of overlay image
1928 arg flag '!' = locations/percentage relative to background image
1930 center.x=(MagickRealType) x_offset;
1931 center.y=(MagickRealType) y_offset;
1932 if (compose == DistortCompositeOp)
1934 if ((flags & XValue) == 0)
1935 if ((flags & AspectValue) == 0)
1936 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
1939 center.x=((MagickRealType) image->columns-1)/2.0;
1941 if ((flags & AspectValue) == 0)
1942 center.x=(MagickRealType) x_offset+geometry_info.xi;
1944 center.x=geometry_info.xi;
1945 if ((flags & YValue) == 0)
1946 if ((flags & AspectValue) == 0)
1947 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
1949 center.y=((MagickRealType) image->rows-1)/2.0;
1951 if ((flags & AspectValue) == 0)
1952 center.y=(MagickRealType) y_offset+geometry_info.psi;
1954 center.y=geometry_info.psi;
1957 Shift the pixel offset point as defined by the provided,
1958 displacement/distortion map. -- Like a lens...
1961 exception=(&image->exception);
1962 image_view=AcquireCacheView(image);
1963 destination_view=AcquireCacheView(destination_image);
1964 composite_view=AcquireCacheView(composite_image);
1965 for (y=0; y < (ssize_t) composite_image->rows; y++)
1970 register const Quantum
1979 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1981 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1983 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1984 destination_image->columns,1,&image->exception);
1985 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1987 for (x=0; x < (ssize_t) composite_image->columns; x++)
1989 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1991 p+=GetPixelChannels(composite_image);
1995 Displace the offset.
1997 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1998 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1999 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
2001 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
2002 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2003 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
2005 (void) InterpolatePixelInfo(image,image_view,
2006 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2009 Mask with the 'invalid pixel mask' in alpha channel.
2011 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
2012 pixel.alpha)*(1.0-QuantumScale*
2013 GetPixelAlpha(composite_image,p)));
2014 SetPixelPixelInfo(destination_image,&pixel,q);
2015 p+=GetPixelChannels(composite_image);
2016 q+=GetPixelChannels(destination_image);
2018 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
2019 if (sync == MagickFalse)
2022 destination_view=DestroyCacheView(destination_view);
2023 composite_view=DestroyCacheView(composite_view);
2024 image_view=DestroyCacheView(image_view);
2025 composite_image=destination_image;
2028 case DissolveCompositeOp:
2031 Geometry arguments to dissolve factors.
2033 value=GetImageArtifact(composite_image,"compose:args");
2034 if (value != (char *) NULL)
2036 flags=ParseGeometry(value,&geometry_info);
2037 source_dissolve=geometry_info.rho/100.0;
2038 destination_dissolve=1.0;
2039 if ((source_dissolve-MagickEpsilon) < 0.0)
2040 source_dissolve=0.0;
2041 if ((source_dissolve+MagickEpsilon) > 1.0)
2043 destination_dissolve=2.0-source_dissolve;
2044 source_dissolve=1.0;
2046 if ((flags & SigmaValue) != 0)
2047 destination_dissolve=geometry_info.sigma/100.0;
2048 if ((destination_dissolve-MagickEpsilon) < 0.0)
2049 destination_dissolve=0.0;
2050 modify_outside_overlay=MagickTrue;
2051 if ((destination_dissolve+MagickEpsilon) > 1.0 )
2053 destination_dissolve=1.0;
2054 modify_outside_overlay=MagickFalse;
2059 case BlendCompositeOp:
2061 value=GetImageArtifact(composite_image,"compose:args");
2062 if (value != (char *) NULL)
2064 flags=ParseGeometry(value,&geometry_info);
2065 source_dissolve=geometry_info.rho/100.0;
2066 destination_dissolve=1.0-source_dissolve;
2067 if ((flags & SigmaValue) != 0)
2068 destination_dissolve=geometry_info.sigma/100.0;
2069 modify_outside_overlay=MagickTrue;
2070 if ((destination_dissolve+MagickEpsilon) > 1.0)
2071 modify_outside_overlay=MagickFalse;
2075 case MathematicsCompositeOp:
2078 Just collect the values from "compose:args", setting.
2079 Unused values are set to zero automagically.
2081 Arguments are normally a comma separated list, so this probably should
2082 be changed to some 'general comma list' parser, (with a minimum
2085 SetGeometryInfo(&geometry_info);
2086 value=GetImageArtifact(composite_image,"compose:args");
2087 if (value != (char *) NULL)
2088 (void) ParseGeometry(value,&geometry_info);
2091 case ModulateCompositeOp:
2094 Determine the brightness and saturation scale.
2096 value=GetImageArtifact(composite_image,"compose:args");
2097 if (value != (char *) NULL)
2099 flags=ParseGeometry(value,&geometry_info);
2100 percent_brightness=geometry_info.rho;
2101 if ((flags & SigmaValue) != 0)
2102 percent_saturation=geometry_info.sigma;
2106 case ThresholdCompositeOp:
2109 Determine the amount and threshold.
2111 value=GetImageArtifact(composite_image,"compose:args");
2112 if (value != (char *) NULL)
2114 flags=ParseGeometry(value,&geometry_info);
2115 amount=geometry_info.rho;
2116 threshold=geometry_info.sigma;
2117 if ((flags & SigmaValue) == 0)
2120 threshold*=QuantumRange;
2126 value=GetImageArtifact(composite_image,"compose:outside-overlay");
2127 if (value != (const char *) NULL)
2128 modify_outside_overlay=IsMagickTrue(value);
2134 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2135 GetPixelInfo(composite_image,&zero);
2136 exception=(&image->exception);
2137 image_view=AcquireCacheView(image);
2138 composite_view=AcquireCacheView(composite_image);
2139 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2140 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2142 for (y=0; y < (ssize_t) image->rows; y++)
2157 register const Quantum
2166 if (status == MagickFalse)
2168 if (modify_outside_overlay == MagickFalse)
2172 if ((y-y_offset) >= (ssize_t) composite_image->rows)
2176 If pixels is NULL, y is outside overlay region.
2178 pixels=(Quantum *) NULL;
2180 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
2182 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
2183 composite_image->columns,1,exception);
2184 if (p == (const Quantum *) NULL)
2191 p-=x_offset*GetPixelChannels(composite_image);
2193 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2194 if (q == (const Quantum *) NULL)
2204 for (x=0; x < (ssize_t) image->columns; x++)
2206 if (modify_outside_overlay == MagickFalse)
2210 q+=GetPixelChannels(image);
2213 if ((x-x_offset) >= (ssize_t) composite_image->columns)
2216 destination.red=(MagickRealType) GetPixelRed(image,q);
2217 destination.green=(MagickRealType) GetPixelGreen(image,q);
2218 destination.blue=(MagickRealType) GetPixelBlue(image,q);
2219 if (image->colorspace == CMYKColorspace)
2220 destination.black=(MagickRealType) GetPixelBlack(image,q);
2221 if (image->colorspace == CMYKColorspace)
2223 destination.red=(MagickRealType) QuantumRange-destination.red;
2224 destination.green=(MagickRealType) QuantumRange-destination.green;
2225 destination.blue=(MagickRealType) QuantumRange-destination.blue;
2226 destination.black=(MagickRealType) QuantumRange-destination.black;
2228 if (image->matte != MagickFalse)
2229 destination.alpha=(MagickRealType) GetPixelAlpha(image,q);
2231 Handle destination modifications outside overlaid region.
2233 composite=destination;
2234 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2235 ((x-x_offset) >= (ssize_t) composite_image->columns))
2239 case DissolveCompositeOp:
2240 case BlendCompositeOp:
2242 composite.alpha=destination_dissolve*(composite.alpha);
2245 case ClearCompositeOp:
2246 case SrcCompositeOp:
2248 CompositeClear(&destination,&composite);
2252 case SrcInCompositeOp:
2253 case OutCompositeOp:
2254 case SrcOutCompositeOp:
2255 case DstInCompositeOp:
2256 case DstAtopCompositeOp:
2257 case CopyOpacityCompositeOp:
2258 case ChangeMaskCompositeOp:
2260 composite.alpha=(MagickRealType) TransparentAlpha;
2265 (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,y-
2266 y_offset,&composite,exception);
2270 if (image->colorspace == CMYKColorspace)
2272 composite.red=(MagickRealType) QuantumRange-composite.red;
2273 composite.green=(MagickRealType) QuantumRange-composite.green;
2274 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2275 composite.black=(MagickRealType) QuantumRange-composite.black;
2277 SetPixelRed(image,ClampToQuantum(composite.red),q);
2278 SetPixelGreen(image,ClampToQuantum(composite.green),q);
2279 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2280 if (image->matte != MagickFalse)
2281 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2282 if (image->colorspace == CMYKColorspace)
2283 SetPixelBlack(image,ClampToQuantum(composite.black),q);
2284 q+=GetPixelChannels(image);
2288 Handle normal overlay of source onto destination.
2290 source.red=(MagickRealType) GetPixelRed(composite_image,p);
2291 source.green=(MagickRealType) GetPixelGreen(composite_image,p);
2292 source.blue=(MagickRealType) GetPixelBlue(composite_image,p);
2293 if (composite_image->colorspace == CMYKColorspace)
2294 source.black=(MagickRealType) GetPixelBlack(composite_image,p);
2295 if (composite_image->colorspace == CMYKColorspace)
2297 source.red=(MagickRealType) QuantumRange-source.red;
2298 source.green=(MagickRealType) QuantumRange-source.green;
2299 source.blue=(MagickRealType) QuantumRange-source.blue;
2300 source.black=(MagickRealType) QuantumRange-source.black;
2302 if (composite_image->matte != MagickFalse)
2303 source.alpha=(MagickRealType) GetPixelAlpha(composite_image,p);
2305 Porter-Duff compositions.
2309 case ClearCompositeOp:
2311 CompositeClear(&destination,&composite);
2314 case SrcCompositeOp:
2315 case CopyCompositeOp:
2316 case ReplaceCompositeOp:
2322 case DstCompositeOp:
2324 case OverCompositeOp:
2325 case SrcOverCompositeOp:
2327 CompositePixelInfoOver(&source,source.alpha,&destination,
2328 destination.alpha,&composite);
2331 case DstOverCompositeOp:
2333 CompositePixelInfoOver(&destination,destination.alpha,&source,
2334 source.alpha,&composite);
2337 case SrcInCompositeOp:
2340 CompositeIn(&source,&destination,&composite);
2343 case DstInCompositeOp:
2345 CompositeIn(&destination,&source,&composite);
2348 case OutCompositeOp:
2349 case SrcOutCompositeOp:
2351 CompositeOut(&source,&destination,&composite);
2354 case DstOutCompositeOp:
2356 CompositeOut(&destination,&source,&composite);
2359 case AtopCompositeOp:
2360 case SrcAtopCompositeOp:
2362 CompositeAtop(&source,&destination,&composite);
2365 case DstAtopCompositeOp:
2367 CompositeAtop(&destination,&source,&composite);
2370 case XorCompositeOp:
2372 CompositeXor(&source,&destination,&composite);
2375 case PlusCompositeOp:
2377 CompositePlus(image,&source,&destination,&composite);
2380 case MinusDstCompositeOp:
2382 CompositeMinus(image,&source,&destination,&composite);
2385 case MinusSrcCompositeOp:
2387 CompositeMinus(image,&destination,&source,&composite);
2390 case ModulusAddCompositeOp:
2392 CompositeModulusAdd(image,&source,&destination,&composite);
2395 case ModulusSubtractCompositeOp:
2397 CompositeModulusSubtract(image,&source,&destination,&composite);
2400 case DifferenceCompositeOp:
2402 CompositeDifference(image,&source,&destination,&composite);
2405 case ExclusionCompositeOp:
2407 CompositeExclusion(image,&source,&destination,&composite);
2410 case MultiplyCompositeOp:
2412 CompositeMultiply(image,&source,&destination,&composite);
2415 case ScreenCompositeOp:
2417 CompositeScreen(image,&source,&destination,&composite);
2420 case DivideDstCompositeOp:
2422 CompositeDivide(image,&source,&destination,&composite);
2425 case DivideSrcCompositeOp:
2427 CompositeDivide(image,&destination,&source,&composite);
2430 case DarkenCompositeOp:
2432 CompositeDarken(image,&source,&destination,&composite);
2435 case LightenCompositeOp:
2437 CompositeLighten(image,&source,&destination,&composite);
2440 case DarkenIntensityCompositeOp:
2442 CompositeDarkenIntensity(image,&source,&destination,&composite);
2445 case LightenIntensityCompositeOp:
2447 CompositeLightenIntensity(image,&source,&destination,&composite);
2450 case MathematicsCompositeOp:
2452 CompositeMathematics(image,&source,&destination,&geometry_info,
2456 case ColorDodgeCompositeOp:
2458 CompositeColorDodge(&source,&destination,&composite);
2461 case ColorBurnCompositeOp:
2463 CompositeColorBurn(&source,&destination,&composite);
2466 case LinearDodgeCompositeOp:
2468 CompositeLinearDodge(&source,&destination,&composite);
2471 case LinearBurnCompositeOp:
2473 CompositeLinearBurn(&source,&destination,&composite);
2476 case HardLightCompositeOp:
2478 CompositeHardLight(&source,&destination,&composite);
2481 case OverlayCompositeOp:
2483 CompositeHardLight(&destination,&source,&composite);
2486 case SoftLightCompositeOp:
2488 CompositeSoftLight(&source,&destination,&composite);
2491 case LinearLightCompositeOp:
2493 CompositeLinearLight(&source,&destination,&composite);
2496 case PegtopLightCompositeOp:
2498 CompositePegtopLight(&source,&destination,&composite);
2501 case VividLightCompositeOp:
2503 CompositeVividLight(&source,&destination,&composite);
2506 case PinLightCompositeOp:
2508 CompositePinLight(&source,&destination,&composite);
2511 case ChangeMaskCompositeOp:
2513 if ((composite.alpha > ((MagickRealType) QuantumRange/2.0)) ||
2514 (IsFuzzyEquivalencePixelInfo(&source,&destination) != MagickFalse))
2515 composite.alpha=(MagickRealType) TransparentAlpha;
2517 composite.alpha=(MagickRealType) OpaqueAlpha;
2520 case BumpmapCompositeOp:
2522 if (source.alpha == TransparentAlpha)
2524 CompositeBumpmap(&source,&destination,&composite);
2527 case DissolveCompositeOp:
2529 CompositePixelInfoOver(&source,source_dissolve*source.alpha,
2530 &destination,(MagickRealType) (destination_dissolve*
2531 destination.alpha),&composite);
2534 case BlendCompositeOp:
2536 CompositePixelInfoBlend(&source,source_dissolve,&destination,
2537 destination_dissolve,&composite);
2540 case ThresholdCompositeOp:
2542 CompositeThreshold(&source,&destination,threshold,amount,&composite);
2545 case ModulateCompositeOp:
2550 if (source.alpha == TransparentAlpha)
2552 offset=(ssize_t) (GetPixelInfoIntensity(&source)-midpoint);
2555 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2556 &saturation,&brightness);
2557 brightness+=(0.01*percent_brightness*offset)/midpoint;
2558 saturation*=0.01*percent_saturation;
2559 HSBComposite(hue,saturation,brightness,&composite.red,
2560 &composite.green,&composite.blue);
2563 case HueCompositeOp:
2565 if (source.alpha == TransparentAlpha)
2567 if (destination.alpha == TransparentAlpha)
2572 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2573 &saturation,&brightness);
2574 CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
2575 HSBComposite(hue,saturation,brightness,&composite.red,
2576 &composite.green,&composite.blue);
2577 if (source.alpha < destination.alpha)
2578 composite.alpha=source.alpha;
2581 case SaturateCompositeOp:
2583 if (source.alpha == TransparentAlpha)
2585 if (destination.alpha == TransparentAlpha)
2590 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2591 &saturation,&brightness);
2592 CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
2594 HSBComposite(hue,saturation,brightness,&composite.red,
2595 &composite.green,&composite.blue);
2596 if (source.alpha < destination.alpha)
2597 composite.alpha=source.alpha;
2600 case LuminizeCompositeOp:
2602 if (source.alpha == TransparentAlpha)
2604 if (destination.alpha == TransparentAlpha)
2609 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2610 &saturation,&brightness);
2611 CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
2613 HSBComposite(hue,saturation,brightness,&composite.red,
2614 &composite.green,&composite.blue);
2615 if (source.alpha < destination.alpha)
2616 composite.alpha=source.alpha;
2619 case ColorizeCompositeOp:
2621 if (source.alpha == TransparentAlpha)
2623 if (destination.alpha == TransparentAlpha)
2628 CompositeHSB(destination.red,destination.green,destination.blue,&sans,
2630 CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
2632 HSBComposite(hue,saturation,brightness,&composite.red,
2633 &composite.green,&composite.blue);
2634 if (source.alpha < destination.alpha)
2635 composite.alpha=source.alpha;
2638 case CopyRedCompositeOp:
2639 case CopyCyanCompositeOp:
2641 composite.red=source.red;
2644 case CopyGreenCompositeOp:
2645 case CopyMagentaCompositeOp:
2647 composite.green=source.green;
2650 case CopyBlueCompositeOp:
2651 case CopyYellowCompositeOp:
2653 composite.blue=source.blue;
2656 case CopyOpacityCompositeOp:
2658 if (source.matte == MagickFalse)
2660 composite.alpha=(MagickRealType) GetPixelInfoIntensity(&source);
2663 composite.alpha=source.alpha;
2666 case CopyBlackCompositeOp:
2668 if (source.colorspace != CMYKColorspace)
2669 ConvertRGBToCMYK(&source);
2670 composite.black=source.black;
2673 case BlurCompositeOp:
2674 case DisplaceCompositeOp:
2675 case DistortCompositeOp:
2683 if (image->colorspace == CMYKColorspace)
2685 composite.red=(MagickRealType) QuantumRange-composite.red;
2686 composite.green=(MagickRealType) QuantumRange-composite.green;
2687 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2688 composite.black=(MagickRealType) QuantumRange-composite.black;
2690 SetPixelRed(image,ClampToQuantum(composite.red),q);
2691 SetPixelGreen(image,ClampToQuantum(composite.green),q);
2692 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2693 if (image->colorspace == CMYKColorspace)
2694 SetPixelBlack(image,ClampToQuantum(composite.black),q);
2695 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2696 p+=GetPixelChannels(composite_image);
2697 if (p >= (pixels+composite_image->columns*GetPixelChannels(composite_image)))
2699 q+=GetPixelChannels(image);
2701 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2703 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2708 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2709 #pragma omp critical (MagickCore_CompositeImage)
2711 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2713 if (proceed == MagickFalse)
2717 composite_view=DestroyCacheView(composite_view);
2718 image_view=DestroyCacheView(image_view);
2719 if (destination_image != (Image * ) NULL)
2720 destination_image=DestroyImage(destination_image);
2725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2729 % T e x t u r e I m a g e %
2733 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2735 % TextureImage() repeatedly tiles the texture image across and down the image
2738 % The format of the TextureImage method is:
2740 % MagickBooleanType TextureImage(Image *image,const Image *texture)
2742 % A description of each parameter follows:
2744 % o image: the image.
2746 % o texture: This image is the texture to layer on the background.
2749 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
2751 #define TextureImageTag "Texture/Image"
2766 assert(image != (Image *) NULL);
2767 if (image->debug != MagickFalse)
2768 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2769 assert(image->signature == MagickSignature);
2770 if (texture == (const Image *) NULL)
2771 return(MagickFalse);
2772 (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
2773 exception=(&image->exception);
2774 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2775 return(MagickFalse);
2777 if ((image->compose != CopyCompositeOp) &&
2778 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2779 (texture->matte != MagickFalse)))
2782 Tile texture onto the image background.
2784 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2785 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2787 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
2792 if (status == MagickFalse)
2794 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2799 thread_status=CompositeImage(image,image->compose,texture,x+
2800 texture->tile_offset.x,y+texture->tile_offset.y);
2801 if (thread_status == MagickFalse)
2803 status=thread_status;
2807 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2812 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2813 #pragma omp critical (MagickCore_TextureImage)
2815 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2817 if (proceed == MagickFalse)
2821 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2822 image->rows,image->rows);
2826 Tile texture onto the image background (optimized).
2829 image_view=AcquireCacheView(image);
2830 texture_view=AcquireCacheView(texture);
2831 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2832 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2834 for (y=0; y < (ssize_t) image->rows; y++)
2839 register const Quantum
2852 if (status == MagickFalse)
2854 pixels=GetCacheViewVirtualPixels(texture_view,texture->tile_offset.x,(y+
2855 texture->tile_offset.y) % texture->rows,texture->columns,1,exception);
2856 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2858 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2863 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2869 width=texture->columns;
2870 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2871 width=image->columns-x;
2872 for (i=0; i < (ssize_t) width; i++)
2874 SetPixelRed(image,GetPixelRed(texture,p),q);
2875 SetPixelGreen(image,GetPixelGreen(texture,p),q);
2876 SetPixelBlue(image,GetPixelBlue(texture,p),q);
2877 SetPixelAlpha(image,GetPixelAlpha(texture,p),q);
2878 if ((image->colorspace == CMYKColorspace) &&
2879 (texture->colorspace == CMYKColorspace))
2880 SetPixelBlack(image,GetPixelBlack(texture,p),q);
2881 p+=GetPixelChannels(texture);
2882 q+=GetPixelChannels(image);
2885 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2886 if (sync == MagickFalse)
2888 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2893 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2894 #pragma omp critical (MagickCore_TextureImage)
2896 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2898 if (proceed == MagickFalse)
2902 texture_view=DestroyCacheView(texture_view);
2903 image_view=DestroyCacheView(image_view);