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/utility-private.h"
76 #include "MagickCore/version.h"
79 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83 % C o m p o s i t e I m a g e %
87 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89 % CompositeImage() returns the second image composited onto the first
90 % at the specified offset, using the specified composite method.
92 % The format of the CompositeImage method is:
94 % MagickBooleanType CompositeImage(Image *image,
95 % const CompositeOperator compose,Image *composite_image,
96 % const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
98 % A description of each parameter follows:
100 % o image: the destination image, modified by he composition
102 % o compose: This operator affects how the composite is applied to
103 % the image. The operators and how they are utilized are listed here
104 % http://www.w3.org/TR/SVG12/#compositing.
106 % o composite_image: the composite (source) image.
108 % o x_offset: the column offset of the composited image.
110 % o y_offset: the row offset of the composited image.
112 % Extra Controls from Image meta-data in 'composite_image' (artifacts)
115 % A string containing extra numerical arguments for specific compose
116 % methods, generally expressed as a 'geometry' or a comma separated list
119 % Compose methods needing such arguments include "BlendCompositeOp" and
120 % "DisplaceCompositeOp".
122 % o "compose:outside-overlay"
123 % Modify how the composition is to effect areas not directly covered
124 % by the 'composite_image' at the offset given. Normally this is
125 % dependant on the 'compose' method, especially Duff-Porter methods.
127 % If set to "false" then disable all normal handling of pixels not
128 % covered by the composite_image. Typically used for repeated tiling
129 % of the composite_image by the calling API.
131 % Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
133 % o exception: return any errors or warnings in this structure.
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.
170 Most functions use a blending mode of over (X=1,Y=1,Z=1)
171 this results in the following optimizations...
173 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
174 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
176 The above SVG definitions also definate that Mathematical Composition
177 methods should use a 'Over' blending mode for Alpha Channel.
178 It however was not applied for composition modes of 'Plus', 'Minus',
179 the modulus versions of 'Add' and 'Subtract'.
181 Mathematical operator changes to be applied from IM v6.7...
183 1/ Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
184 'ModulusAdd' and 'ModulusSubtract' for clarity.
186 2/ All mathematical compositions work as per the SVG specification
187 with regard to blending. This now includes 'ModulusAdd' and
190 3/ When the special channel flag 'sync' (syncronize channel updates)
191 is turned off (enabled by default) then mathematical compositions are
192 only performed on the channels specified, and are applied
193 independantally of each other. In other words the mathematics is
194 performed as 'pure' mathematical operations, rather than as image
198 static inline MagickRealType Atop(const MagickRealType p,
199 const MagickRealType Sa,const MagickRealType q,
200 const MagickRealType magick_unused(Da))
202 return(p*Sa+q*(1.0-Sa)); /* Da optimized out, Da/gamma => 1.0 */
205 static inline void CompositeAtop(const PixelInfo *p,const PixelInfo *q,
206 PixelInfo *composite)
211 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
212 composite->alpha=q->alpha; /* optimized Da = 1.0-Gamma */
213 composite->red=Atop(p->red,Sa,q->red,1.0);
214 composite->green=Atop(p->green,Sa,q->green,1.0);
215 composite->blue=Atop(p->blue,Sa,q->blue,1.0);
216 if (q->colorspace == CMYKColorspace)
217 composite->black=Atop(p->black,Sa,q->black,1.0);
221 What is this Composition method for? Can't find any specification!
222 WARNING this is not doing correct 'over' blend handling (Anthony Thyssen).
224 static inline void CompositeBumpmap(const PixelInfo *p,const PixelInfo *q,
225 PixelInfo *composite)
230 intensity=(MagickRealType) GetPixelInfoIntensity(p);
231 composite->red=QuantumScale*intensity*q->red;
232 composite->green=QuantumScale*intensity*q->green;
233 composite->blue=QuantumScale*intensity*q->blue;
234 composite->alpha=(MagickRealType) QuantumScale*intensity*p->alpha;
235 if (q->colorspace == CMYKColorspace)
236 composite->black=QuantumScale*intensity*q->black;
239 static inline void CompositeClear(const PixelInfo *q,PixelInfo *composite)
241 composite->alpha=(MagickRealType) TransparentAlpha;
243 composite->green=0.0;
245 if (q->colorspace == CMYKColorspace)
246 composite->black=0.0;
249 static MagickRealType ColorBurn(const MagickRealType Sca,
250 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
252 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
253 return(Sa*Da+Dca*(1.0-Sa));
254 if (Sca < MagickEpsilon)
255 return(Dca*(1.0-Sa));
256 return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
259 static inline void CompositeColorBurn(const PixelInfo *p,const PixelInfo *q,
260 PixelInfo *composite)
267 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
268 Da=QuantumScale*q->alpha;
269 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
270 composite->alpha=(MagickRealType) QuantumRange*gamma;
271 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
272 composite->red=gamma*ColorBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
274 composite->green=gamma*ColorBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
276 composite->blue=gamma*ColorBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
278 if (q->colorspace == CMYKColorspace)
279 composite->black=gamma*ColorBurn(QuantumScale*p->black*Sa,Sa,QuantumScale*
284 static MagickRealType ColorDodge(const MagickRealType Sca,
285 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
288 Working from first principles using the original formula:
292 This works correctly! Looks like the 2004 SVG model was right but just
293 required a extra condition for correct handling.
295 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
296 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
297 if (fabs(Sca-Sa) < MagickEpsilon)
298 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
299 return(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
302 static inline void CompositeColorDodge(const PixelInfo *p,const PixelInfo *q,
303 PixelInfo *composite)
310 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
311 Da=QuantumScale*q->alpha;
312 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
313 composite->alpha=(MagickRealType) QuantumRange*gamma;
314 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
315 composite->red=gamma*ColorDodge(QuantumScale*p->red*Sa,Sa,QuantumScale*
317 composite->green=gamma*ColorDodge(QuantumScale*p->green*Sa,Sa,QuantumScale*
319 composite->blue=gamma*ColorDodge(QuantumScale*p->blue*Sa,Sa,QuantumScale*
321 if (q->colorspace == CMYKColorspace)
322 composite->black=gamma*ColorDodge(QuantumScale*p->black*Sa,Sa,QuantumScale*
326 static inline MagickRealType Darken(const MagickRealType p,
327 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
330 return(MagickOver_(p,alpha,q,beta)); /* src-over */
331 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
334 static inline void CompositeDarken(const Image *image,const PixelInfo *p,
335 const PixelInfo *q,PixelInfo *composite)
341 Darken is equivalent to a 'Minimum' method OR a greyscale version of a
342 binary 'Or' OR the 'Intersection' of pixel sets.
344 if (image->sync == MagickFalse)
347 Handle channels as separate grayscale channels.
349 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
350 composite->red=MagickMin(p->red,q->red);
351 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
352 composite->green=MagickMin(p->green,q->green);
353 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
354 composite->blue=MagickMin(p->blue,q->blue);
355 if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 &&
356 (q->colorspace == CMYKColorspace))
357 composite->black=MagickMin(p->black,q->black);
358 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
359 composite->alpha=MagickMax(p->alpha,q->alpha);
362 composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */
363 gamma=1.0-QuantumScale*composite->alpha;
364 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
365 composite->red=gamma*Darken(p->red,p->alpha,q->red,q->alpha);
366 composite->green=gamma*Darken(p->green,p->alpha,q->green,q->alpha);
367 composite->blue=gamma*Darken(p->blue,p->alpha,q->blue,q->alpha);
368 if (q->colorspace == CMYKColorspace)
369 composite->black=gamma*Darken(p->black,p->alpha,q->black,q->alpha);
372 static inline void CompositeDarkenIntensity(const Image *image,
373 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
380 Select the pixel based on the intensity level.
381 If 'Sync' flag select whole pixel based on alpha weighted intensity.
382 Otherwise use intensity only, but restrict copy according to channel.
384 if (image->sync == MagickFalse)
389 from_p=GetPixelInfoIntensity(p) < GetPixelInfoIntensity(q) ? MagickTrue :
391 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
392 composite->red=from_p != MagickFalse ? p->red : q->red;
393 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
394 composite->green=from_p != MagickFalse ? p->green : q->green;
395 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
396 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
397 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
398 (q->colorspace == CMYKColorspace))
399 composite->black=from_p != MagickFalse ? p->black : q->black;
400 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
401 composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha;
404 Sa=QuantumScale*p->alpha;
405 Da=QuantumScale*q->alpha;
406 *composite=(Sa*GetPixelInfoIntensity(p) < Da*GetPixelInfoIntensity(q)) ?
410 static inline MagickRealType Difference(const MagickRealType p,
411 const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
414 Optimized by Multipling by QuantumRange (taken from gamma).
416 return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q));
419 static inline void CompositeDifference(const Image *image,const PixelInfo *p,
420 const PixelInfo *q,PixelInfo *composite)
427 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
428 Da=QuantumScale*q->alpha;
429 if (image->sync == MagickFalse)
432 Handle channels as separate grayscale channels.
434 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
435 composite->red=fabs((double) (p->red-q->red));
436 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
437 composite->green=fabs((double) (p->green-q->green));
438 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
439 composite->blue=fabs((double) (p->blue-q->blue));
440 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
441 (q->colorspace == CMYKColorspace))
442 composite->black=fabs((double) (p->black-q->black));
443 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
444 composite->alpha=fabs((double) (p->alpha-q->alpha));
447 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
448 composite->alpha=(MagickRealType) QuantumRange*gamma;
449 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
450 composite->red=gamma*Difference(p->red,Sa,q->red,Da);
451 composite->green=gamma*Difference(p->green,Sa,q->green,Da);
452 composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da);
453 if (q->colorspace == CMYKColorspace)
454 composite->black=gamma*Difference(p->black,Sa,q->black,Da);
457 static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
458 const MagickRealType Dca,const MagickRealType Da)
461 Divide Source by Destination
465 But with appropriate handling for special case of Dc == 0 specifically
466 so that f(Black,Black)=Black and f(non-Black,Black)=White.
467 It is however also important to correctly do 'over' alpha blending which
468 is why the formula becomes so complex.
470 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
471 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
472 if (fabs(Dca) < MagickEpsilon)
473 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
474 return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
477 static inline void CompositeDivide(const Image *image,const PixelInfo *p,
478 const PixelInfo *q,PixelInfo *composite)
485 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
486 Da=QuantumScale*q->alpha;
487 if (image->sync == MagickFalse)
490 Handle channels as separate grayscale channels.
492 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
493 composite->red=QuantumRange*Divide(QuantumScale*p->red,1.0,
494 QuantumScale*q->red,1.0);
495 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
496 composite->green=QuantumRange*Divide(QuantumScale*p->green,1.0,
497 QuantumScale*q->green,1.0);
498 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
499 composite->blue=QuantumRange*Divide(QuantumScale*p->blue,1.0,
500 QuantumScale*q->blue,1.0);
501 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
502 (q->colorspace == CMYKColorspace))
503 composite->black=QuantumRange*Divide(QuantumScale*p->black,1.0,
504 QuantumScale*q->black,1.0);
505 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
506 composite->alpha=QuantumRange*(1.0-Divide(Sa,1.0,Da,1.0));
509 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
510 composite->alpha=(MagickRealType) QuantumRange*gamma;
511 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
512 composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale*
514 composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale*
516 composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale*
518 if (q->colorspace == CMYKColorspace)
519 composite->black=gamma*Divide(QuantumScale*p->black*Sa,Sa,QuantumScale*
523 static MagickRealType Exclusion(const MagickRealType Sca,
524 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
526 return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
529 static inline void CompositeExclusion(const Image *image,const PixelInfo *p,
530 const PixelInfo *q,PixelInfo *composite)
537 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
538 Da=QuantumScale*q->alpha;
539 if (image->sync == MagickFalse)
542 Handle channels as separate grayscale channels.
544 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
545 composite->red=QuantumRange*Exclusion(QuantumScale*p->red,1.0,
546 QuantumScale*q->red,1.0);
547 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
548 composite->green=QuantumRange*Exclusion(QuantumScale*p->green,1.0,
549 QuantumScale*q->green,1.0);
550 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
551 composite->blue=QuantumRange*Exclusion(QuantumScale*p->blue,1.0,
552 QuantumScale*q->blue,1.0);
553 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
554 (q->colorspace == CMYKColorspace))
555 composite->black=QuantumRange*Exclusion(QuantumScale*p->black,1.0,
556 QuantumScale*q->black,1.0);
557 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
558 composite->alpha=QuantumRange*(1.0-Exclusion(Sa,1.0,Da,1.0));
561 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
562 composite->alpha=(MagickRealType) QuantumRange*gamma;
563 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
564 composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale*
566 composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale*
568 composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale*
570 if (q->colorspace == CMYKColorspace)
571 composite->black=gamma*Exclusion(QuantumScale*p->black*Sa,Sa,
572 QuantumScale*q->black*Da,Da);
575 static MagickRealType HardLight(const MagickRealType Sca,
576 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
579 return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
580 return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
583 static inline void CompositeHardLight(const PixelInfo *p,const PixelInfo *q,
584 PixelInfo *composite)
591 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
592 Da=QuantumScale*q->alpha;
593 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
594 composite->alpha=(MagickRealType) QuantumRange*gamma;
595 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
596 composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
598 composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
600 composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
602 if (q->colorspace == CMYKColorspace)
603 composite->black=gamma*HardLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
607 static void CompositeHSB(const MagickRealType red,const MagickRealType green,
608 const MagickRealType blue,double *hue,double *saturation,double *brightness)
616 Convert RGB to HSB colorspace.
618 assert(hue != (double *) NULL);
619 assert(saturation != (double *) NULL);
620 assert(brightness != (double *) NULL);
621 max=(red > green ? red : green);
624 min=(red < green ? red : green);
629 *brightness=(double) (QuantumScale*max);
630 if (fabs(max) < MagickEpsilon)
632 *saturation=(double) (1.0-min/max);
634 if (fabs(delta) < MagickEpsilon)
636 if (fabs(red-max) < MagickEpsilon)
637 *hue=(double) ((green-blue)/delta);
639 if (fabs(green-max) < MagickEpsilon)
640 *hue=(double) (2.0+(blue-red)/delta);
642 if (fabs(blue-max) < MagickEpsilon)
643 *hue=(double) (4.0+(red-green)/delta);
649 static inline MagickRealType In(const MagickRealType p,const MagickRealType Sa,
650 const MagickRealType magick_unused(q),const MagickRealType Da)
655 static inline void CompositeIn(const PixelInfo *p,const PixelInfo *q,
656 PixelInfo *composite)
663 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
664 Da=QuantumScale*q->alpha;
666 composite->alpha=(MagickRealType) QuantumRange*gamma;
667 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
668 composite->red=gamma*In(p->red,Sa,q->red,Da);
669 composite->green=gamma*In(p->green,Sa,q->green,Da);
670 composite->blue=gamma*In(p->blue,Sa,q->blue,Da);
671 if (q->colorspace == CMYKColorspace)
672 composite->black=gamma*In(p->black,Sa,q->black,Da);
675 static inline MagickRealType Lighten(const MagickRealType p,
676 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
679 return(MagickOver_(p,alpha,q,beta)); /* src-over */
680 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
683 static inline void CompositeLighten(const Image *image,const PixelInfo *p,
684 const PixelInfo *q,PixelInfo *composite)
690 Lighten is also equvalent to a 'Maximum' method OR a greyscale version of a
691 binary 'And' OR the 'Union' of pixel sets.
693 if (image->sync == MagickFalse)
696 Handle channels as separate grayscale channels
698 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
699 composite->red=MagickMax(p->red,q->red);
700 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
701 composite->green=MagickMax(p->green,q->green);
702 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
703 composite->blue=MagickMax(p->blue,q->blue);
704 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
705 (q->colorspace == CMYKColorspace))
706 composite->black=MagickMax(p->black,q->black);
707 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
708 composite->alpha=MagickMin(p->alpha,q->alpha);
711 composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */
712 gamma=1.0-QuantumScale*composite->alpha;
713 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
714 composite->red=gamma*Lighten(p->red,p->alpha,q->red,q->alpha);
715 composite->green=gamma*Lighten(p->green,p->alpha,q->green,q->alpha);
716 composite->blue=gamma*Lighten(p->blue,p->alpha,q->blue,q->alpha);
717 if (q->colorspace == CMYKColorspace)
718 composite->black=gamma*Lighten(p->black,p->alpha,q->black,q->alpha);
721 static inline void CompositeLightenIntensity(const Image *image,
722 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
729 Select the pixel based on the intensity level.
730 If 'Sync' flag select whole pixel based on alpha weighted intensity.
731 Otherwise use Intenisty only, but restrict copy according to channel.
733 if (image->sync == MagickFalse)
738 from_p=GetPixelInfoIntensity(p) > GetPixelInfoIntensity(q) ? MagickTrue :
740 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
741 composite->red=from_p != MagickFalse ? p->red : q->red;
742 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
743 composite->green=from_p != MagickFalse ? p->green : q->green;
744 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
745 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
746 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
747 (q->colorspace == CMYKColorspace))
748 composite->black=from_p != MagickFalse ? p->black : q->black;
749 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
750 composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha;
753 Sa=QuantumScale*p->alpha;
754 Da=QuantumScale*q->alpha;
755 *composite=(Sa*GetPixelInfoIntensity(p) > Da*GetPixelInfoIntensity(q)) ?
759 static inline void CompositeLinearDodge(const PixelInfo *p,const PixelInfo *q,
760 PixelInfo *composite)
767 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
768 Da=QuantumScale*q->alpha;
769 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
770 composite->alpha=(MagickRealType) QuantumRange*gamma;
771 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
772 composite->red=gamma*(p->red*Sa+q->red*Da);
773 composite->green=gamma*(p->green*Sa+q->green*Da);
774 composite->blue=gamma*(p->blue*Sa+q->blue*Da);
775 if (q->colorspace == CMYKColorspace)
776 composite->black=gamma*(p->black*Sa+q->black*Da);
780 static inline MagickRealType LinearBurn(const MagickRealType Sca,
781 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
784 LinearBurn: as defined by Abode Photoshop, according to
785 http://www.simplefilter.de/en/basics/mixmods.html is:
787 f(Sc,Dc) = Sc + Dc - 1
789 return(Sca+Dca-Sa*Da);
792 static inline void CompositeLinearBurn(const PixelInfo *p,const PixelInfo *q,
793 PixelInfo *composite)
800 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
801 Da=QuantumScale*q->alpha;
802 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
803 composite->alpha=(MagickRealType) QuantumRange*gamma;
804 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
805 composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
807 composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
809 composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
811 if (q->colorspace == CMYKColorspace)
812 composite->black=gamma*LinearBurn(QuantumScale*p->black*Sa,Sa,QuantumScale*
816 static inline MagickRealType LinearLight(const MagickRealType Sca,
817 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
820 LinearLight: as defined by Abode Photoshop, according to
821 http://www.simplefilter.de/en/basics/mixmods.html is:
823 f(Sc,Dc) = Dc + 2*Sc - 1
825 return((Sca-Sa)*Da+Sca+Dca);
828 static inline void CompositeLinearLight(const PixelInfo *p,const PixelInfo *q,
829 PixelInfo *composite)
836 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
837 Da=QuantumScale*q->alpha;
838 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
839 composite->alpha=(MagickRealType) QuantumRange*gamma;
840 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
841 composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
843 composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
845 composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
847 if (q->colorspace == CMYKColorspace)
848 composite->black=gamma*LinearLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
852 static inline MagickRealType Mathematics(const MagickRealType Sca,
853 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
854 const GeometryInfo *geometry_info)
860 'Mathematics' a free form user control mathematical composition is defined
863 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
865 Where the arguments A,B,C,D are (currently) passed to composite as
866 a command separated 'geometry' string in "compose:args" image artifact.
868 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
870 Applying the SVG transparency formula (see above), we get...
872 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
874 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
877 gamma=geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
878 geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
883 static inline void CompositeMathematics(const Image *image,const PixelInfo *p,
884 const PixelInfo *q,const GeometryInfo *args,PixelInfo *composite)
891 Sa=QuantumScale*p->alpha; /* ??? - AT */
892 Da=QuantumScale*q->alpha;
893 if (image->sync == MagickFalse)
896 Handle channels as separate grayscale channels.
898 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
899 composite->red=QuantumRange*Mathematics(QuantumScale*p->red,1.0,
900 QuantumScale*q->red,1.0,args);
901 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
902 composite->green=QuantumRange*Mathematics(QuantumScale*p->green,1.0,
903 QuantumScale*q->green,1.0,args);
904 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
905 composite->blue=QuantumRange*Mathematics(QuantumScale*p->blue,1.0,
906 QuantumScale*q->blue,1.0,args);
907 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
908 (q->colorspace == CMYKColorspace))
909 composite->black=QuantumRange*Mathematics(QuantumScale*p->black,1.0,
910 QuantumScale*q->black,1.0,args);
911 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
912 composite->alpha=QuantumRange*(1.0-Mathematics(Sa,1.0,Da,1.0,args));
915 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
916 composite->alpha=(MagickRealType) QuantumRange*gamma;
917 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
918 composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
920 composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,
921 QuantumScale*q->green*Da,Da,args);
922 composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
924 if (q->colorspace == CMYKColorspace)
925 composite->black=gamma*Mathematics(QuantumScale*p->black*Sa,Sa,
926 QuantumScale*q->black*Da,Da,args);
929 static inline void CompositePlus(const Image *image,const PixelInfo *p,
930 const PixelInfo *q,PixelInfo *composite)
933 NOTE: "Plus" does not use 'over' alpha-blending but uses a special
934 'plus' form of alph-blending. It is the ONLY mathematical operator to
935 do this. this is what makes it different to the otherwise equivalent
936 "LinearDodge" composition method.
938 Note however that color channels are still effected by the alpha channel
939 as a result of the blending, making it just as useless for independant
940 channel maths, just like all other mathematical composition methods.
942 As such the removal of the 'sync' flag, is still a usful convention.
944 The CompositePixelInfoPlus() function is defined in
945 "composite-private.h" so it can also be used for Image Blending.
947 if (image->sync == MagickFalse)
950 Handle channels as separate grayscale channels.
952 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
953 composite->red=p->red+q->red;
954 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
955 composite->green=p->green+q->green;
956 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
957 composite->blue=p->blue+q->blue;
958 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
959 (q->colorspace == CMYKColorspace))
960 composite->black=p->black+q->black;
961 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
962 composite->alpha=p->alpha+q->alpha-QuantumRange;
965 CompositePixelInfoPlus(p,p->alpha,q,q->alpha,composite);
968 static inline MagickRealType Minus(const MagickRealType Sca,
969 const MagickRealType Sa,const MagickRealType Dca,
970 const MagickRealType magick_unused(Da))
973 Minus Source from Destination
977 return(Sca+Dca-2.0*Dca*Sa);
980 static inline void CompositeMinus(const Image *image,const PixelInfo *p,
981 const PixelInfo *q,PixelInfo *composite)
988 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
989 Da=QuantumScale*q->alpha;
990 if (image->sync == MagickFalse)
993 Handle channels as separate grayscale channels.
995 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
996 composite->red=p->red-q->red;
997 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
998 composite->green=p->green-q->green;
999 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1000 composite->blue=p->blue-q->blue;
1001 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1002 (q->colorspace == CMYKColorspace))
1003 composite->black=p->black-q->black;
1004 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1005 composite->alpha=QuantumRange*(1.0-(Sa-Da));
1008 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1009 composite->alpha=(MagickRealType) QuantumRange*gamma;
1010 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1011 composite->red=gamma*Minus(p->red*Sa,Sa,q->red*Da,Da);
1012 composite->green=gamma*Minus(p->green*Sa,Sa,q->green*Da,Da);
1013 composite->blue=gamma*Minus(p->blue*Sa,Sa,q->blue*Da,Da);
1014 if (q->colorspace == CMYKColorspace)
1015 composite->black=gamma*Minus(p->black*Sa,Sa,q->black*Da,Da);
1018 static inline MagickRealType ModulusAdd(const MagickRealType p,
1019 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1025 if (pixel > QuantumRange)
1026 pixel-=(QuantumRange+1.0);
1027 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1030 static inline void CompositeModulusAdd(const Image *image,const PixelInfo *p,
1031 const PixelInfo *q,PixelInfo *composite)
1038 if (image->sync == MagickFalse)
1041 Handle channels as separate grayscale channels.
1043 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1044 composite->red=ModulusAdd(p->red,1.0,q->red,1.0);
1045 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1046 composite->green=ModulusAdd(p->green,1.0,q->green,1.0);
1047 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1048 composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0);
1049 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1050 (q->colorspace == CMYKColorspace))
1051 composite->black=ModulusAdd(p->black,1.0,q->black,1.0);
1052 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1053 composite->alpha=ModulusAdd(p->alpha,1.0,q->alpha,1.0);
1056 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1057 Da=QuantumScale*q->alpha;
1058 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1059 composite->alpha=(MagickRealType) QuantumRange*gamma;
1060 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1061 composite->red=ModulusAdd(p->red,Sa,q->red,Da);
1062 composite->green=ModulusAdd(p->green,Sa,q->green,Da);
1063 composite->blue=ModulusAdd(p->blue,Sa,q->blue,Da);
1064 if (q->colorspace == CMYKColorspace)
1065 composite->black=ModulusAdd(p->black,Sa,q->black,Da);
1068 static inline MagickRealType ModulusSubtract(const MagickRealType p,
1069 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1076 pixel+=(QuantumRange+1.0);
1077 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1080 static inline void CompositeModulusSubtract(const Image *image,
1081 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
1088 if (image->sync == MagickFalse)
1091 Handle channels as separate grayscale channels,
1093 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1094 composite->red=ModulusSubtract(p->red,1.0,q->red,1.0);
1095 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1096 composite->green=ModulusSubtract(p->green,1.0,q->green,1.0);
1097 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1098 composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0);
1099 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1100 (q->colorspace == CMYKColorspace))
1101 composite->black=ModulusSubtract(p->black,1.0,q->black,1.0);
1102 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1103 composite->alpha=ModulusSubtract(p->alpha,1.0,q->alpha,1.0);
1106 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1107 Da=QuantumScale*q->alpha;
1108 gamma = RoundToUnity(Sa+Da-Sa*Da);
1109 composite->alpha=(MagickRealType) QuantumRange*gamma;
1110 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1111 composite->red=ModulusSubtract(p->red,Sa,q->red,Da);
1112 composite->green=ModulusSubtract(p->green,Sa,q->green,Da);
1113 composite->blue=ModulusSubtract(p->blue,Sa,q->blue,Da);
1114 if (q->colorspace == CMYKColorspace)
1115 composite->black=ModulusSubtract(p->black,Sa,q->black,Da);
1118 static inline MagickRealType Multiply(const MagickRealType Sca,
1119 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1121 return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1124 static inline void CompositeMultiply(const Image *image,const PixelInfo *p,
1125 const PixelInfo *q,PixelInfo *composite)
1132 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1133 Da=QuantumScale*q->alpha;
1134 if (image->sync == MagickFalse)
1137 Handle channels as separate grayscale channels.
1139 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1140 composite->red=QuantumScale*p->red*q->red;
1141 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1142 composite->green=QuantumScale*p->green*q->green;
1143 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1144 composite->blue=QuantumScale*p->blue*q->blue;
1145 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1146 (q->colorspace == CMYKColorspace))
1147 composite->black=QuantumScale*p->black*q->black;
1148 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1149 composite->alpha=QuantumRange*(1.0-Sa*Da);
1152 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1153 composite->alpha=(MagickRealType) QuantumRange*gamma;
1154 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1155 composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
1157 composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
1159 composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1161 if (q->colorspace == CMYKColorspace)
1162 composite->black=gamma*Multiply(QuantumScale*p->black*Sa,Sa,
1163 QuantumScale*q->black*Da,Da);
1166 static inline MagickRealType Out(const MagickRealType p,const MagickRealType Sa,
1167 const MagickRealType magick_unused(q),const MagickRealType Da)
1169 return(Sa*p*(1.0-Da));
1172 static inline void CompositeOut(const PixelInfo *p,const PixelInfo *q,
1173 PixelInfo *composite)
1180 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1181 Da=QuantumScale*q->alpha;
1183 composite->alpha=(MagickRealType) QuantumRange*gamma;
1184 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1185 composite->red=gamma*Out(p->red,Sa,q->red,Da);
1186 composite->green=gamma*Out(p->green,Sa,q->green,Da);
1187 composite->blue=gamma*Out(p->blue,Sa,q->blue,Da);
1188 if (q->colorspace == CMYKColorspace)
1189 composite->black=gamma*Out(p->black,Sa,q->black,Da);
1192 static MagickRealType PegtopLight(const MagickRealType Sca,
1193 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1196 PegTop: A Soft-Light alternative: A continuous version of the Softlight
1197 function, producing very similar results.
1199 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
1201 See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
1203 if (fabs(Da) < MagickEpsilon)
1205 return(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+Dca*(1.0-Sa));
1208 static inline void CompositePegtopLight(const PixelInfo *p,const PixelInfo *q,
1209 PixelInfo *composite)
1216 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1217 Da=QuantumScale*q->alpha;
1218 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1219 composite->alpha=(MagickRealType) QuantumRange*gamma;
1220 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1221 composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1223 composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1225 composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1227 if (q->colorspace == CMYKColorspace)
1228 composite->black=gamma*PegtopLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1232 static MagickRealType PinLight(const MagickRealType Sca,const MagickRealType Sa,
1233 const MagickRealType Dca,const MagickRealType Da)
1236 PinLight: A Photoshop 7 composition method
1237 http://www.simplefilter.de/en/basics/mixmods.html
1239 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
1241 if (Dca*Sa < Da*(2.0*Sca-Sa))
1242 return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
1243 if ((Dca*Sa) > (2.0*Sca*Da))
1244 return(Sca*Da+Sca+Dca*(1.0-Sa));
1245 return(Sca*(1.0-Da)+Dca);
1248 static inline void CompositePinLight(const PixelInfo *p,const PixelInfo *q,
1249 PixelInfo *composite)
1256 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1257 Da=QuantumScale*q->alpha;
1258 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1259 composite->alpha=(MagickRealType) QuantumRange*gamma;
1260 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1261 composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1263 composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1265 composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1267 if (q->colorspace == CMYKColorspace)
1268 composite->black=gamma*PinLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1272 static inline MagickRealType Screen(const MagickRealType Sca,
1273 const MagickRealType Dca)
1276 Screen: A negated multiply
1277 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
1279 return(Sca+Dca-Sca*Dca);
1282 static inline void CompositeScreen(const Image *image,const PixelInfo *p,
1283 const PixelInfo *q,PixelInfo *composite)
1290 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1291 Da=QuantumScale*q->alpha;
1292 if (image->sync == MagickFalse)
1295 Handle channels as separate grayscale channels.
1297 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1298 composite->red=QuantumRange*Screen(QuantumScale*p->red,
1299 QuantumScale*q->red);
1300 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1301 composite->green=QuantumRange*Screen(QuantumScale*p->green,
1302 QuantumScale*q->green);
1303 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1304 composite->blue=QuantumRange*Screen(QuantumScale*p->blue,
1305 QuantumScale*q->blue);
1306 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1307 (q->colorspace == CMYKColorspace))
1308 composite->black=QuantumRange*Screen(QuantumScale*p->black,
1309 QuantumScale*q->black);
1310 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1311 composite->alpha=QuantumRange*(1.0-Screen(Sa,Da));
1314 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1315 composite->alpha=(MagickRealType) QuantumRange*gamma;
1316 Sa*=QuantumScale; Da*=QuantumScale; /* optimization */
1317 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1318 composite->red=gamma*Screen(p->red*Sa,q->red*Da);
1319 composite->green=gamma*Screen(p->green*Sa,q->green*Da);
1320 composite->blue=gamma*Screen(p->blue*Sa,q->blue*Da);
1321 if (q->colorspace == CMYKColorspace)
1322 composite->black=gamma*Screen(p->black*Sa,q->black*Da);
1325 static MagickRealType SoftLight(const MagickRealType Sca,
1326 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1333 New specification: March 2009 SVG specification.
1337 return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1338 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1340 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
1341 alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1344 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1348 static inline void CompositeSoftLight(const PixelInfo *p,const PixelInfo *q,
1349 PixelInfo *composite)
1356 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1357 Da=QuantumScale*q->alpha;
1358 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1359 composite->alpha=(MagickRealType) QuantumRange*gamma;
1360 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1361 composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1363 composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1365 composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1367 if (q->colorspace == CMYKColorspace)
1368 composite->black=gamma*SoftLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1372 static inline MagickRealType Threshold(const MagickRealType p,
1373 const MagickRealType q,const MagickRealType threshold,
1374 const MagickRealType amount)
1380 Multiply difference by amount, if differance larger than threshold???
1381 What use this is is completely unknown. The Opacity calculation appears to
1382 be inverted -- Anthony Thyssen
1387 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
1389 return(q+delta*amount);
1392 static inline void CompositeThreshold(const PixelInfo *p,const PixelInfo *q,
1393 const MagickRealType threshold,const MagickRealType amount,
1394 PixelInfo *composite)
1396 composite->red=Threshold(p->red,q->red,threshold,amount);
1397 composite->green=Threshold(p->green,q->green,threshold,amount);
1398 composite->blue=Threshold(p->blue,q->blue,threshold,amount);
1399 composite->alpha=Threshold(p->alpha,q->alpha,threshold,amount);
1400 if (q->colorspace == CMYKColorspace)
1401 composite->black=Threshold(p->black,q->black,threshold,amount);
1405 static MagickRealType VividLight(const MagickRealType Sca,
1406 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1409 VividLight: A Photoshop 7 composition method. See
1410 http://www.simplefilter.de/en/basics/mixmods.html.
1412 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
1414 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
1415 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1416 if ((2.0*Sca) <= Sa)
1417 return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1418 return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1421 static inline void CompositeVividLight(const PixelInfo *p,const PixelInfo *q,
1422 PixelInfo *composite)
1429 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1430 Da=QuantumScale*q->alpha;
1431 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1432 composite->alpha=(MagickRealType) QuantumRange*gamma;
1433 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1434 composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1436 composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1438 composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1440 if (q->colorspace == CMYKColorspace)
1441 composite->black=gamma*VividLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1445 static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
1446 const MagickRealType Dca,const MagickRealType Da)
1448 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
1451 static inline void CompositeXor(const PixelInfo *p,const PixelInfo *q,
1452 PixelInfo *composite)
1459 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1460 Da=QuantumScale*q->alpha;
1461 gamma=Sa+Da-2.0*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
1462 composite->alpha=(MagickRealType) QuantumRange*gamma;
1463 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1464 composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
1465 composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
1466 composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
1467 if (q->colorspace == CMYKColorspace)
1468 composite->black=gamma*Xor(p->black*Sa,Sa,q->black*Da,Da);
1471 static void HSBComposite(const double hue,const double saturation,
1472 const double brightness,MagickRealType *red,MagickRealType *green,
1473 MagickRealType *blue)
1483 Convert HSB to RGB colorspace.
1485 assert(red != (MagickRealType *) NULL);
1486 assert(green != (MagickRealType *) NULL);
1487 assert(blue != (MagickRealType *) NULL);
1488 if (saturation == 0.0)
1490 *red=(MagickRealType) QuantumRange*brightness;
1495 h=6.0*(hue-floor(hue));
1496 f=h-floor((double) h);
1497 p=brightness*(1.0-saturation);
1498 q=brightness*(1.0-saturation*f);
1499 t=brightness*(1.0-saturation*(1.0-f));
1505 *red=(MagickRealType) QuantumRange*brightness;
1506 *green=(MagickRealType) QuantumRange*t;
1507 *blue=(MagickRealType) QuantumRange*p;
1512 *red=(MagickRealType) QuantumRange*q;
1513 *green=(MagickRealType) QuantumRange*brightness;
1514 *blue=(MagickRealType) QuantumRange*p;
1519 *red=(MagickRealType) QuantumRange*p;
1520 *green=(MagickRealType) QuantumRange*brightness;
1521 *blue=(MagickRealType) QuantumRange*t;
1526 *red=(MagickRealType) QuantumRange*p;
1527 *green=(MagickRealType) QuantumRange*q;
1528 *blue=(MagickRealType) QuantumRange*brightness;
1533 *red=(MagickRealType) QuantumRange*t;
1534 *green=(MagickRealType) QuantumRange*p;
1535 *blue=(MagickRealType) QuantumRange*brightness;
1540 *red=(MagickRealType) QuantumRange*brightness;
1541 *green=(MagickRealType) QuantumRange*p;
1542 *blue=(MagickRealType) QuantumRange*q;
1548 MagickExport MagickBooleanType CompositeImage(Image *image,
1549 const CompositeOperator compose,const Image *composite_image,
1550 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
1552 #define CompositeImageTag "Composite/Image"
1571 modify_outside_overlay,
1579 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 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1605 return(MagickFalse);
1606 GetPixelInfo(image,&zero);
1607 destination_image=(Image *) NULL;
1609 destination_dissolve=1.0;
1610 modify_outside_overlay=MagickFalse;
1611 percent_brightness=100.0;
1612 percent_saturation=100.0;
1613 source_dissolve=1.0;
1617 case ClearCompositeOp:
1618 case SrcCompositeOp:
1620 case SrcInCompositeOp:
1621 case OutCompositeOp:
1622 case SrcOutCompositeOp:
1623 case DstInCompositeOp:
1624 case DstAtopCompositeOp:
1627 Modify destination outside the overlaid region.
1629 modify_outside_overlay=MagickTrue;
1632 case CopyCompositeOp:
1634 if ((x_offset < 0) || (y_offset < 0))
1636 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
1638 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
1641 image_view=AcquireCacheView(image);
1642 composite_view=AcquireCacheView(composite_image);
1643 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1644 #pragma omp parallel for schedule(dynamic,4) shared(status)
1646 for (y=0; y < (ssize_t) composite_image->rows; y++)
1651 register const Quantum
1660 if (status == MagickFalse)
1662 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1664 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1665 composite_image->columns,1,exception);
1666 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1671 for (x=0; x < (ssize_t) composite_image->columns; x++)
1673 SetPixelRed(image,GetPixelRed(composite_image,p),q);
1674 SetPixelGreen(image,GetPixelGreen(composite_image,p),q);
1675 SetPixelBlue(image,GetPixelBlue(composite_image,p),q);
1676 SetPixelAlpha(image,GetPixelAlpha(composite_image,p),q);
1677 if (image->colorspace == CMYKColorspace)
1678 SetPixelBlack(image,GetPixelBlack(composite_image,p),q);
1679 p+=GetPixelChannels(composite_image);
1680 q+=GetPixelChannels(image);
1682 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1683 if (sync == MagickFalse)
1685 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1690 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1691 #pragma omp critical (MagickCore_CompositeImage)
1693 proceed=SetImageProgress(image,CompositeImageTag,
1694 (MagickOffsetType) y,image->rows);
1695 if (proceed == MagickFalse)
1699 composite_view=DestroyCacheView(composite_view);
1700 image_view=DestroyCacheView(image_view);
1703 case CopyOpacityCompositeOp:
1704 case ChangeMaskCompositeOp:
1707 Modify destination outside the overlaid region and require an alpha
1708 channel to exist, to add transparency.
1710 if (image->matte == MagickFalse)
1711 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1712 modify_outside_overlay=MagickTrue;
1715 case BlurCompositeOp:
1737 Blur Image dictated by an overlay gradient map: X = red_channel;
1738 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
1740 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1742 if (destination_image == (Image *) NULL)
1743 return(MagickFalse);
1745 Determine the horizontal and vertical maximim blur.
1747 SetGeometryInfo(&geometry_info);
1749 value=GetImageArtifact(composite_image,"compose:args");
1750 if (value != (char *) NULL)
1751 flags=ParseGeometry(value,&geometry_info);
1752 if ((flags & WidthValue) == 0 )
1754 destination_image=DestroyImage(destination_image);
1755 return(MagickFalse);
1757 width=geometry_info.rho;
1758 height=geometry_info.sigma;
1759 blur.x1=geometry_info.rho;
1762 blur.y2=geometry_info.sigma;
1765 if ((flags & HeightValue) == 0)
1767 if ((flags & XValue) != 0 )
1772 angle=DegreesToRadians(geometry_info.xi);
1773 blur.x1=width*cos(angle);
1774 blur.x2=width*sin(angle);
1775 blur.y1=(-height*sin(angle));
1776 blur.y2=height*cos(angle);
1778 if ((flags & YValue) != 0 )
1780 angle_start=DegreesToRadians(geometry_info.xi);
1781 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1784 Blur Image by resampling.
1787 resample_filter=AcquireResampleFilter(image,exception);
1788 SetResampleFilter(resample_filter,CubicFilter,2.0);
1789 destination_view=AcquireCacheView(destination_image);
1790 composite_view=AcquireCacheView(composite_image);
1791 for (y=0; y < (ssize_t) composite_image->rows; y++)
1796 register const Quantum
1805 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1807 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1809 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1810 destination_image->columns,1,exception);
1811 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1813 for (x=0; x < (ssize_t) composite_image->columns; x++)
1815 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1817 p+=GetPixelChannels(composite_image);
1820 if (fabs(angle_range) > MagickEpsilon)
1825 angle=angle_start+angle_range*QuantumScale*
1826 GetPixelBlue(composite_image,p);
1827 blur.x1=width*cos(angle);
1828 blur.x2=width*sin(angle);
1829 blur.y1=(-height*sin(angle));
1830 blur.y2=height*cos(angle);
1832 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
1833 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
1834 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
1835 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
1836 GetPixelGreen(composite_image,p));
1837 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1838 (double) y_offset+y,&pixel);
1839 SetPixelPixelInfo(destination_image,&pixel,q);
1840 p+=GetPixelChannels(composite_image);
1841 q+=GetPixelChannels(destination_image);
1843 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1844 if (sync == MagickFalse)
1847 resample_filter=DestroyResampleFilter(resample_filter);
1848 composite_view=DestroyCacheView(composite_view);
1849 destination_view=DestroyCacheView(destination_view);
1850 composite_image=destination_image;
1853 case DisplaceCompositeOp:
1854 case DistortCompositeOp:
1873 Displace/Distort based on overlay gradient map:
1874 X = red_channel; Y = green_channel;
1875 compose:args = x_scale[,y_scale[,center.x,center.y]]
1877 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1879 if (destination_image == (Image *) NULL)
1880 return(MagickFalse);
1881 SetGeometryInfo(&geometry_info);
1883 value=GetImageArtifact(composite_image,"compose:args");
1884 if (value != (char *) NULL)
1885 flags=ParseGeometry(value,&geometry_info);
1886 if ((flags & (WidthValue|HeightValue)) == 0 )
1888 if ((flags & AspectValue) == 0)
1890 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1892 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1896 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1897 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1902 horizontal_scale=geometry_info.rho;
1903 vertical_scale=geometry_info.sigma;
1904 if ((flags & PercentValue) != 0)
1906 if ((flags & AspectValue) == 0)
1908 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1909 vertical_scale*=(composite_image->rows-1.0)/200.0;
1913 horizontal_scale*=(image->columns-1.0)/200.0;
1914 vertical_scale*=(image->rows-1.0)/200.0;
1917 if ((flags & HeightValue) == 0)
1918 vertical_scale=horizontal_scale;
1921 Determine fixed center point for absolute distortion map
1923 Displace offset relative to a fixed absolute point
1924 Select that point according to +X+Y user inputs.
1925 default = center of overlay image
1926 arg flag '!' = locations/percentage relative to background image
1928 center.x=(MagickRealType) x_offset;
1929 center.y=(MagickRealType) y_offset;
1930 if (compose == DistortCompositeOp)
1932 if ((flags & XValue) == 0)
1933 if ((flags & AspectValue) == 0)
1934 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
1937 center.x=((MagickRealType) image->columns-1)/2.0;
1939 if ((flags & AspectValue) == 0)
1940 center.x=(MagickRealType) x_offset+geometry_info.xi;
1942 center.x=geometry_info.xi;
1943 if ((flags & YValue) == 0)
1944 if ((flags & AspectValue) == 0)
1945 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
1947 center.y=((MagickRealType) image->rows-1)/2.0;
1949 if ((flags & AspectValue) == 0)
1950 center.y=(MagickRealType) y_offset+geometry_info.psi;
1952 center.y=geometry_info.psi;
1955 Shift the pixel offset point as defined by the provided,
1956 displacement/distortion map. -- Like a lens...
1959 image_view=AcquireCacheView(image);
1960 destination_view=AcquireCacheView(destination_image);
1961 composite_view=AcquireCacheView(composite_image);
1962 for (y=0; y < (ssize_t) composite_image->rows; y++)
1967 register const Quantum
1976 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1978 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1980 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1981 destination_image->columns,1,exception);
1982 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1984 for (x=0; x < (ssize_t) composite_image->columns; x++)
1986 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1988 p+=GetPixelChannels(composite_image);
1992 Displace the offset.
1994 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1995 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1996 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1998 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1999 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2000 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
2002 (void) InterpolatePixelInfo(image,image_view,
2003 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2006 Mask with the 'invalid pixel mask' in alpha channel.
2008 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
2009 pixel.alpha)*(1.0-QuantumScale*
2010 GetPixelAlpha(composite_image,p)));
2011 SetPixelPixelInfo(destination_image,&pixel,q);
2012 p+=GetPixelChannels(composite_image);
2013 q+=GetPixelChannels(destination_image);
2015 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
2016 if (sync == MagickFalse)
2019 destination_view=DestroyCacheView(destination_view);
2020 composite_view=DestroyCacheView(composite_view);
2021 image_view=DestroyCacheView(image_view);
2022 composite_image=destination_image;
2025 case DissolveCompositeOp:
2028 Geometry arguments to dissolve factors.
2030 value=GetImageArtifact(composite_image,"compose:args");
2031 if (value != (char *) NULL)
2033 flags=ParseGeometry(value,&geometry_info);
2034 source_dissolve=geometry_info.rho/100.0;
2035 destination_dissolve=1.0;
2036 if ((source_dissolve-MagickEpsilon) < 0.0)
2037 source_dissolve=0.0;
2038 if ((source_dissolve+MagickEpsilon) > 1.0)
2040 destination_dissolve=2.0-source_dissolve;
2041 source_dissolve=1.0;
2043 if ((flags & SigmaValue) != 0)
2044 destination_dissolve=geometry_info.sigma/100.0;
2045 if ((destination_dissolve-MagickEpsilon) < 0.0)
2046 destination_dissolve=0.0;
2047 modify_outside_overlay=MagickTrue;
2048 if ((destination_dissolve+MagickEpsilon) > 1.0 )
2050 destination_dissolve=1.0;
2051 modify_outside_overlay=MagickFalse;
2056 case BlendCompositeOp:
2058 value=GetImageArtifact(composite_image,"compose:args");
2059 if (value != (char *) NULL)
2061 flags=ParseGeometry(value,&geometry_info);
2062 source_dissolve=geometry_info.rho/100.0;
2063 destination_dissolve=1.0-source_dissolve;
2064 if ((flags & SigmaValue) != 0)
2065 destination_dissolve=geometry_info.sigma/100.0;
2066 modify_outside_overlay=MagickTrue;
2067 if ((destination_dissolve+MagickEpsilon) > 1.0)
2068 modify_outside_overlay=MagickFalse;
2072 case MathematicsCompositeOp:
2075 Just collect the values from "compose:args", setting.
2076 Unused values are set to zero automagically.
2078 Arguments are normally a comma separated list, so this probably should
2079 be changed to some 'general comma list' parser, (with a minimum
2082 SetGeometryInfo(&geometry_info);
2083 value=GetImageArtifact(composite_image,"compose:args");
2084 if (value != (char *) NULL)
2085 (void) ParseGeometry(value,&geometry_info);
2088 case ModulateCompositeOp:
2091 Determine the brightness and saturation scale.
2093 value=GetImageArtifact(composite_image,"compose:args");
2094 if (value != (char *) NULL)
2096 flags=ParseGeometry(value,&geometry_info);
2097 percent_brightness=geometry_info.rho;
2098 if ((flags & SigmaValue) != 0)
2099 percent_saturation=geometry_info.sigma;
2103 case ThresholdCompositeOp:
2106 Determine the amount and threshold.
2108 value=GetImageArtifact(composite_image,"compose:args");
2109 if (value != (char *) NULL)
2111 flags=ParseGeometry(value,&geometry_info);
2112 amount=geometry_info.rho;
2113 threshold=geometry_info.sigma;
2114 if ((flags & SigmaValue) == 0)
2117 threshold*=QuantumRange;
2123 value=GetImageArtifact(composite_image,"compose:outside-overlay");
2124 if (value != (const char *) NULL)
2125 modify_outside_overlay=IsMagickTrue(value);
2131 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2132 GetPixelInfo(composite_image,&zero);
2133 image_view=AcquireCacheView(image);
2134 composite_view=AcquireCacheView(composite_image);
2135 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2136 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2138 for (y=0; y < (ssize_t) image->rows; y++)
2153 register const Quantum
2162 if (status == MagickFalse)
2164 if (modify_outside_overlay == MagickFalse)
2168 if ((y-y_offset) >= (ssize_t) composite_image->rows)
2172 If pixels is NULL, y is outside overlay region.
2174 pixels=(Quantum *) NULL;
2176 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
2178 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
2179 composite_image->columns,1,exception);
2180 if (p == (const Quantum *) NULL)
2187 p-=x_offset*GetPixelChannels(composite_image);
2189 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2190 if (q == (Quantum *) NULL)
2200 for (x=0; x < (ssize_t) image->columns; x++)
2202 if (modify_outside_overlay == MagickFalse)
2206 q+=GetPixelChannels(image);
2209 if ((x-x_offset) >= (ssize_t) composite_image->columns)
2212 destination.red=(MagickRealType) GetPixelRed(image,q);
2213 destination.green=(MagickRealType) GetPixelGreen(image,q);
2214 destination.blue=(MagickRealType) GetPixelBlue(image,q);
2215 if (image->colorspace == CMYKColorspace)
2216 destination.black=(MagickRealType) GetPixelBlack(image,q);
2217 if (image->colorspace == CMYKColorspace)
2219 destination.red=(MagickRealType) QuantumRange-destination.red;
2220 destination.green=(MagickRealType) QuantumRange-destination.green;
2221 destination.blue=(MagickRealType) QuantumRange-destination.blue;
2222 destination.black=(MagickRealType) QuantumRange-destination.black;
2224 if (image->matte != MagickFalse)
2225 destination.alpha=(MagickRealType) GetPixelAlpha(image,q);
2227 Handle destination modifications outside overlaid region.
2229 composite=destination;
2230 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2231 ((x-x_offset) >= (ssize_t) composite_image->columns))
2235 case DissolveCompositeOp:
2236 case BlendCompositeOp:
2238 composite.alpha=destination_dissolve*(composite.alpha);
2241 case ClearCompositeOp:
2242 case SrcCompositeOp:
2244 CompositeClear(&destination,&composite);
2248 case SrcInCompositeOp:
2249 case OutCompositeOp:
2250 case SrcOutCompositeOp:
2251 case DstInCompositeOp:
2252 case DstAtopCompositeOp:
2253 case CopyOpacityCompositeOp:
2254 case ChangeMaskCompositeOp:
2256 composite.alpha=(MagickRealType) TransparentAlpha;
2261 (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,y-
2262 y_offset,&composite,exception);
2266 if (image->colorspace == CMYKColorspace)
2268 composite.red=(MagickRealType) QuantumRange-composite.red;
2269 composite.green=(MagickRealType) QuantumRange-composite.green;
2270 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2271 composite.black=(MagickRealType) QuantumRange-composite.black;
2273 SetPixelRed(image,ClampToQuantum(composite.red),q);
2274 SetPixelGreen(image,ClampToQuantum(composite.green),q);
2275 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2276 if (image->matte != MagickFalse)
2277 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2278 if (image->colorspace == CMYKColorspace)
2279 SetPixelBlack(image,ClampToQuantum(composite.black),q);
2280 q+=GetPixelChannels(image);
2284 Handle normal overlay of source onto destination.
2286 source.red=(MagickRealType) GetPixelRed(composite_image,p);
2287 source.green=(MagickRealType) GetPixelGreen(composite_image,p);
2288 source.blue=(MagickRealType) GetPixelBlue(composite_image,p);
2289 if (composite_image->colorspace == CMYKColorspace)
2290 source.black=(MagickRealType) GetPixelBlack(composite_image,p);
2291 if (composite_image->colorspace == CMYKColorspace)
2293 source.red=(MagickRealType) QuantumRange-source.red;
2294 source.green=(MagickRealType) QuantumRange-source.green;
2295 source.blue=(MagickRealType) QuantumRange-source.blue;
2296 source.black=(MagickRealType) QuantumRange-source.black;
2298 if (composite_image->matte != MagickFalse)
2299 source.alpha=(MagickRealType) GetPixelAlpha(composite_image,p);
2301 Porter-Duff compositions.
2305 case ClearCompositeOp:
2307 CompositeClear(&destination,&composite);
2310 case SrcCompositeOp:
2311 case CopyCompositeOp:
2312 case ReplaceCompositeOp:
2318 case DstCompositeOp:
2320 case OverCompositeOp:
2321 case SrcOverCompositeOp:
2323 CompositePixelInfoOver(&source,source.alpha,&destination,
2324 destination.alpha,&composite);
2327 case DstOverCompositeOp:
2329 CompositePixelInfoOver(&destination,destination.alpha,&source,
2330 source.alpha,&composite);
2333 case SrcInCompositeOp:
2336 CompositeIn(&source,&destination,&composite);
2339 case DstInCompositeOp:
2341 CompositeIn(&destination,&source,&composite);
2344 case OutCompositeOp:
2345 case SrcOutCompositeOp:
2347 CompositeOut(&source,&destination,&composite);
2350 case DstOutCompositeOp:
2352 CompositeOut(&destination,&source,&composite);
2355 case AtopCompositeOp:
2356 case SrcAtopCompositeOp:
2358 CompositeAtop(&source,&destination,&composite);
2361 case DstAtopCompositeOp:
2363 CompositeAtop(&destination,&source,&composite);
2366 case XorCompositeOp:
2368 CompositeXor(&source,&destination,&composite);
2371 case PlusCompositeOp:
2373 CompositePlus(image,&source,&destination,&composite);
2376 case MinusDstCompositeOp:
2378 CompositeMinus(image,&source,&destination,&composite);
2381 case MinusSrcCompositeOp:
2383 CompositeMinus(image,&destination,&source,&composite);
2386 case ModulusAddCompositeOp:
2388 CompositeModulusAdd(image,&source,&destination,&composite);
2391 case ModulusSubtractCompositeOp:
2393 CompositeModulusSubtract(image,&source,&destination,&composite);
2396 case DifferenceCompositeOp:
2398 CompositeDifference(image,&source,&destination,&composite);
2401 case ExclusionCompositeOp:
2403 CompositeExclusion(image,&source,&destination,&composite);
2406 case MultiplyCompositeOp:
2408 CompositeMultiply(image,&source,&destination,&composite);
2411 case ScreenCompositeOp:
2413 CompositeScreen(image,&source,&destination,&composite);
2416 case DivideDstCompositeOp:
2418 CompositeDivide(image,&source,&destination,&composite);
2421 case DivideSrcCompositeOp:
2423 CompositeDivide(image,&destination,&source,&composite);
2426 case DarkenCompositeOp:
2428 CompositeDarken(image,&source,&destination,&composite);
2431 case LightenCompositeOp:
2433 CompositeLighten(image,&source,&destination,&composite);
2436 case DarkenIntensityCompositeOp:
2438 CompositeDarkenIntensity(image,&source,&destination,&composite);
2441 case LightenIntensityCompositeOp:
2443 CompositeLightenIntensity(image,&source,&destination,&composite);
2446 case MathematicsCompositeOp:
2448 CompositeMathematics(image,&source,&destination,&geometry_info,
2452 case ColorDodgeCompositeOp:
2454 CompositeColorDodge(&source,&destination,&composite);
2457 case ColorBurnCompositeOp:
2459 CompositeColorBurn(&source,&destination,&composite);
2462 case LinearDodgeCompositeOp:
2464 CompositeLinearDodge(&source,&destination,&composite);
2467 case LinearBurnCompositeOp:
2469 CompositeLinearBurn(&source,&destination,&composite);
2472 case HardLightCompositeOp:
2474 CompositeHardLight(&source,&destination,&composite);
2477 case OverlayCompositeOp:
2479 CompositeHardLight(&destination,&source,&composite);
2482 case SoftLightCompositeOp:
2484 CompositeSoftLight(&source,&destination,&composite);
2487 case LinearLightCompositeOp:
2489 CompositeLinearLight(&source,&destination,&composite);
2492 case PegtopLightCompositeOp:
2494 CompositePegtopLight(&source,&destination,&composite);
2497 case VividLightCompositeOp:
2499 CompositeVividLight(&source,&destination,&composite);
2502 case PinLightCompositeOp:
2504 CompositePinLight(&source,&destination,&composite);
2507 case ChangeMaskCompositeOp:
2509 if ((composite.alpha > ((MagickRealType) QuantumRange/2.0)) ||
2510 (IsFuzzyEquivalencePixelInfo(&source,&destination) != MagickFalse))
2511 composite.alpha=(MagickRealType) TransparentAlpha;
2513 composite.alpha=(MagickRealType) OpaqueAlpha;
2516 case BumpmapCompositeOp:
2518 if (source.alpha == TransparentAlpha)
2520 CompositeBumpmap(&source,&destination,&composite);
2523 case DissolveCompositeOp:
2525 CompositePixelInfoOver(&source,source_dissolve*source.alpha,
2526 &destination,(MagickRealType) (destination_dissolve*
2527 destination.alpha),&composite);
2530 case BlendCompositeOp:
2532 CompositePixelInfoBlend(&source,source_dissolve,&destination,
2533 destination_dissolve,&composite);
2536 case ThresholdCompositeOp:
2538 CompositeThreshold(&source,&destination,threshold,amount,&composite);
2541 case ModulateCompositeOp:
2546 if (source.alpha == TransparentAlpha)
2548 offset=(ssize_t) (GetPixelInfoIntensity(&source)-midpoint);
2551 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2552 &saturation,&brightness);
2553 brightness+=(0.01*percent_brightness*offset)/midpoint;
2554 saturation*=0.01*percent_saturation;
2555 HSBComposite(hue,saturation,brightness,&composite.red,
2556 &composite.green,&composite.blue);
2559 case HueCompositeOp:
2561 if (source.alpha == TransparentAlpha)
2563 if (destination.alpha == TransparentAlpha)
2568 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2569 &saturation,&brightness);
2570 CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
2571 HSBComposite(hue,saturation,brightness,&composite.red,
2572 &composite.green,&composite.blue);
2573 if (source.alpha < destination.alpha)
2574 composite.alpha=source.alpha;
2577 case SaturateCompositeOp:
2579 if (source.alpha == TransparentAlpha)
2581 if (destination.alpha == TransparentAlpha)
2586 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2587 &saturation,&brightness);
2588 CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
2590 HSBComposite(hue,saturation,brightness,&composite.red,
2591 &composite.green,&composite.blue);
2592 if (source.alpha < destination.alpha)
2593 composite.alpha=source.alpha;
2596 case LuminizeCompositeOp:
2598 if (source.alpha == TransparentAlpha)
2600 if (destination.alpha == TransparentAlpha)
2605 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2606 &saturation,&brightness);
2607 CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
2609 HSBComposite(hue,saturation,brightness,&composite.red,
2610 &composite.green,&composite.blue);
2611 if (source.alpha < destination.alpha)
2612 composite.alpha=source.alpha;
2615 case ColorizeCompositeOp:
2617 if (source.alpha == TransparentAlpha)
2619 if (destination.alpha == TransparentAlpha)
2624 CompositeHSB(destination.red,destination.green,destination.blue,&sans,
2626 CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
2628 HSBComposite(hue,saturation,brightness,&composite.red,
2629 &composite.green,&composite.blue);
2630 if (source.alpha < destination.alpha)
2631 composite.alpha=source.alpha;
2634 case CopyRedCompositeOp:
2635 case CopyCyanCompositeOp:
2637 composite.red=source.red;
2640 case CopyGreenCompositeOp:
2641 case CopyMagentaCompositeOp:
2643 composite.green=source.green;
2646 case CopyBlueCompositeOp:
2647 case CopyYellowCompositeOp:
2649 composite.blue=source.blue;
2652 case CopyOpacityCompositeOp:
2654 if (source.matte == MagickFalse)
2656 composite.alpha=(MagickRealType) GetPixelInfoIntensity(&source);
2659 composite.alpha=source.alpha;
2662 case CopyBlackCompositeOp:
2664 if (source.colorspace != CMYKColorspace)
2665 ConvertRGBToCMYK(&source);
2666 composite.black=source.black;
2669 case BlurCompositeOp:
2670 case DisplaceCompositeOp:
2671 case DistortCompositeOp:
2679 if (image->colorspace == CMYKColorspace)
2681 composite.red=(MagickRealType) QuantumRange-composite.red;
2682 composite.green=(MagickRealType) QuantumRange-composite.green;
2683 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2684 composite.black=(MagickRealType) QuantumRange-composite.black;
2686 SetPixelRed(image,ClampToQuantum(composite.red),q);
2687 SetPixelGreen(image,ClampToQuantum(composite.green),q);
2688 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2689 if (image->colorspace == CMYKColorspace)
2690 SetPixelBlack(image,ClampToQuantum(composite.black),q);
2691 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2692 p+=GetPixelChannels(composite_image);
2693 if (p >= (pixels+composite_image->columns*GetPixelChannels(composite_image)))
2695 q+=GetPixelChannels(image);
2697 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2699 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2704 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2705 #pragma omp critical (MagickCore_CompositeImage)
2707 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2709 if (proceed == MagickFalse)
2713 composite_view=DestroyCacheView(composite_view);
2714 image_view=DestroyCacheView(image_view);
2715 if (destination_image != (Image * ) NULL)
2716 destination_image=DestroyImage(destination_image);
2721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2725 % T e x t u r e I m a g e %
2729 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2731 % TextureImage() repeatedly tiles the texture image across and down the image
2734 % The format of the TextureImage method is:
2736 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2737 % ExceptionInfo *exception)
2739 % A description of each parameter follows:
2741 % o image: the image.
2743 % o texture: This image is the texture to layer on the background.
2746 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2747 ExceptionInfo *exception)
2749 #define TextureImageTag "Texture/Image"
2761 assert(image != (Image *) NULL);
2762 if (image->debug != MagickFalse)
2763 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2764 assert(image->signature == MagickSignature);
2765 if (texture == (const Image *) NULL)
2766 return(MagickFalse);
2767 (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
2768 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2769 return(MagickFalse);
2771 if ((image->compose != CopyCompositeOp) &&
2772 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2773 (texture->matte != MagickFalse)))
2776 Tile texture onto the image background.
2778 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2779 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2781 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
2786 if (status == MagickFalse)
2788 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2793 thread_status=CompositeImage(image,image->compose,texture,x+
2794 texture->tile_offset.x,y+texture->tile_offset.y,exception);
2795 if (thread_status == MagickFalse)
2797 status=thread_status;
2801 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2806 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2807 #pragma omp critical (MagickCore_TextureImage)
2809 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2811 if (proceed == MagickFalse)
2815 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2816 image->rows,image->rows);
2820 Tile texture onto the image background (optimized).
2823 image_view=AcquireCacheView(image);
2824 texture_view=AcquireCacheView(texture);
2825 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2826 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2828 for (y=0; y < (ssize_t) image->rows; y++)
2833 register const Quantum
2846 if (status == MagickFalse)
2848 pixels=GetCacheViewVirtualPixels(texture_view,texture->tile_offset.x,(y+
2849 texture->tile_offset.y) % texture->rows,texture->columns,1,exception);
2850 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2852 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2857 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2863 width=texture->columns;
2864 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2865 width=image->columns-x;
2866 for (i=0; i < (ssize_t) width; i++)
2868 SetPixelRed(image,GetPixelRed(texture,p),q);
2869 SetPixelGreen(image,GetPixelGreen(texture,p),q);
2870 SetPixelBlue(image,GetPixelBlue(texture,p),q);
2871 SetPixelAlpha(image,GetPixelAlpha(texture,p),q);
2872 if ((image->colorspace == CMYKColorspace) &&
2873 (texture->colorspace == CMYKColorspace))
2874 SetPixelBlack(image,GetPixelBlack(texture,p),q);
2875 p+=GetPixelChannels(texture);
2876 q+=GetPixelChannels(image);
2879 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2880 if (sync == MagickFalse)
2882 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2887 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2888 #pragma omp critical (MagickCore_TextureImage)
2890 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2892 if (proceed == MagickFalse)
2896 texture_view=DestroyCacheView(texture_view);
2897 image_view=DestroyCacheView(image_view);