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)
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"
135 static inline double MagickMin(const double x,const double y)
141 static inline double MagickMax(const double x,const double y)
149 Programmers notes on SVG specification.
151 A Composition is defined by...
152 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
153 Blending areas : X = 1 for area of overlap ie: f(Sc,Dc)
154 Y = 1 for source preserved
155 Z = 1 for destination preserved
157 Conversion to transparency (then optimized)
158 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
159 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
162 Sca = Sc*Sa normalized Source color divided by Source alpha
163 Dca = Dc*Da normalized Dest color divided by Dest alpha
164 Dc' = Dca'/Da' the desired color value for this channel.
166 Da' in in the follow formula as 'gamma' The resulting alpla value.
168 Most functions use a blending mode of over (X=1,Y=1,Z=1)
169 this results in the following optimizations...
171 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
172 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
174 The above SVG definitions also definate that Mathematical Composition
175 methods should use a 'Over' blending mode for Alpha Channel.
176 It however was not applied for composition modes of 'Plus', 'Minus',
177 the modulus versions of 'Add' and 'Subtract'.
179 Mathematical operator changes to be applied from IM v6.7...
181 1/ Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
182 'ModulusAdd' and 'ModulusSubtract' for clarity.
184 2/ All mathematical compositions work as per the SVG specification
185 with regard to blending. This now includes 'ModulusAdd' and
188 3/ When the special channel flag 'sync' (syncronize channel updates)
189 is turned off (enabled by default) then mathematical compositions are
190 only performed on the channels specified, and are applied
191 independantally of each other. In other words the mathematics is
192 performed as 'pure' mathematical operations, rather than as image
196 static inline MagickRealType Atop(const MagickRealType p,
197 const MagickRealType Sa,const MagickRealType q,
198 const MagickRealType magick_unused(Da))
200 return(p*Sa+q*(1.0-Sa)); /* Da optimized out, Da/gamma => 1.0 */
203 static inline void CompositeAtop(const PixelInfo *p,const PixelInfo *q,
204 PixelInfo *composite)
209 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
210 composite->alpha=q->alpha; /* optimized Da = 1.0-Gamma */
211 composite->red=Atop(p->red,Sa,q->red,1.0);
212 composite->green=Atop(p->green,Sa,q->green,1.0);
213 composite->blue=Atop(p->blue,Sa,q->blue,1.0);
214 if (q->colorspace == CMYKColorspace)
215 composite->black=Atop(p->black,Sa,q->black,1.0);
219 What is this Composition method for? Can't find any specification!
220 WARNING this is not doing correct 'over' blend handling (Anthony Thyssen).
222 static inline void CompositeBumpmap(const PixelInfo *p,const PixelInfo *q,
223 PixelInfo *composite)
228 intensity=(MagickRealType) GetPixelInfoIntensity(p);
229 composite->red=QuantumScale*intensity*q->red;
230 composite->green=QuantumScale*intensity*q->green;
231 composite->blue=QuantumScale*intensity*q->blue;
232 composite->alpha=(MagickRealType) QuantumScale*intensity*p->alpha;
233 if (q->colorspace == CMYKColorspace)
234 composite->black=QuantumScale*intensity*q->black;
237 static inline void CompositeClear(const PixelInfo *q,PixelInfo *composite)
239 composite->alpha=(MagickRealType) TransparentAlpha;
241 composite->green=0.0;
243 if (q->colorspace == CMYKColorspace)
244 composite->black=0.0;
247 static MagickRealType ColorBurn(const MagickRealType Sca,
248 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
250 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
251 return(Sa*Da+Dca*(1.0-Sa));
252 if (Sca < MagickEpsilon)
253 return(Dca*(1.0-Sa));
254 return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
257 static inline void CompositeColorBurn(const PixelInfo *p,const PixelInfo *q,
258 PixelInfo *composite)
265 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
266 Da=QuantumScale*q->alpha;
267 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
268 composite->alpha=(MagickRealType) QuantumRange*gamma;
269 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
270 composite->red=gamma*ColorBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
272 composite->green=gamma*ColorBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
274 composite->blue=gamma*ColorBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
276 if (q->colorspace == CMYKColorspace)
277 composite->black=gamma*ColorBurn(QuantumScale*p->black*Sa,Sa,QuantumScale*
282 static MagickRealType ColorDodge(const MagickRealType Sca,
283 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
286 Working from first principles using the original formula:
290 This works correctly! Looks like the 2004 SVG model was right but just
291 required a extra condition for correct handling.
293 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
294 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
295 if (fabs(Sca-Sa) < MagickEpsilon)
296 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
297 return(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
300 static inline void CompositeColorDodge(const PixelInfo *p,const PixelInfo *q,
301 PixelInfo *composite)
308 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
309 Da=QuantumScale*q->alpha;
310 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
311 composite->alpha=(MagickRealType) QuantumRange*gamma;
312 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
313 composite->red=gamma*ColorDodge(QuantumScale*p->red*Sa,Sa,QuantumScale*
315 composite->green=gamma*ColorDodge(QuantumScale*p->green*Sa,Sa,QuantumScale*
317 composite->blue=gamma*ColorDodge(QuantumScale*p->blue*Sa,Sa,QuantumScale*
319 if (q->colorspace == CMYKColorspace)
320 composite->black=gamma*ColorDodge(QuantumScale*p->black*Sa,Sa,QuantumScale*
324 static inline MagickRealType Darken(const MagickRealType p,
325 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
328 return(MagickOver_(p,alpha,q,beta)); /* src-over */
329 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
332 static inline void CompositeDarken(const Image *image,const PixelInfo *p,
333 const PixelInfo *q,PixelInfo *composite)
339 Darken is equivalent to a 'Minimum' method OR a greyscale version of a
340 binary 'Or' OR the 'Intersection' of pixel sets.
342 if (image->sync == MagickFalse)
345 Handle channels as separate grayscale channels.
347 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
348 composite->red=MagickMin(p->red,q->red);
349 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
350 composite->green=MagickMin(p->green,q->green);
351 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
352 composite->blue=MagickMin(p->blue,q->blue);
353 if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 &&
354 (q->colorspace == CMYKColorspace))
355 composite->black=MagickMin(p->black,q->black);
356 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
357 composite->alpha=MagickMax(p->alpha,q->alpha);
360 composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */
361 gamma=1.0-QuantumScale*composite->alpha;
362 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
363 composite->red=gamma*Darken(p->red,p->alpha,q->red,q->alpha);
364 composite->green=gamma*Darken(p->green,p->alpha,q->green,q->alpha);
365 composite->blue=gamma*Darken(p->blue,p->alpha,q->blue,q->alpha);
366 if (q->colorspace == CMYKColorspace)
367 composite->black=gamma*Darken(p->black,p->alpha,q->black,q->alpha);
370 static inline void CompositeDarkenIntensity(const Image *image,
371 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
378 Select the pixel based on the intensity level.
379 If 'Sync' flag select whole pixel based on alpha weighted intensity.
380 Otherwise use intensity only, but restrict copy according to channel.
382 if (image->sync == MagickFalse)
387 from_p=GetPixelInfoIntensity(p) < GetPixelInfoIntensity(q) ? MagickTrue :
389 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
390 composite->red=from_p != MagickFalse ? p->red : q->red;
391 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
392 composite->green=from_p != MagickFalse ? p->green : q->green;
393 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
394 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
395 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
396 (q->colorspace == CMYKColorspace))
397 composite->black=from_p != MagickFalse ? p->black : q->black;
398 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
399 composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha;
402 Sa=QuantumScale*p->alpha;
403 Da=QuantumScale*q->alpha;
404 *composite=(Sa*GetPixelInfoIntensity(p) < Da*GetPixelInfoIntensity(q)) ?
408 static inline MagickRealType Difference(const MagickRealType p,
409 const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
412 Optimized by Multipling by QuantumRange (taken from gamma).
414 return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q));
417 static inline void CompositeDifference(const Image *image,const PixelInfo *p,
418 const PixelInfo *q,PixelInfo *composite)
425 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
426 Da=QuantumScale*q->alpha;
427 if (image->sync == MagickFalse)
430 Handle channels as separate grayscale channels.
432 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
433 composite->red=fabs((double) (p->red-q->red));
434 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
435 composite->green=fabs((double) (p->green-q->green));
436 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
437 composite->blue=fabs((double) (p->blue-q->blue));
438 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
439 (q->colorspace == CMYKColorspace))
440 composite->black=fabs((double) (p->black-q->black));
441 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
442 composite->alpha=fabs((double) (p->alpha-q->alpha));
445 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
446 composite->alpha=(MagickRealType) QuantumRange*gamma;
447 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
448 composite->red=gamma*Difference(p->red,Sa,q->red,Da);
449 composite->green=gamma*Difference(p->green,Sa,q->green,Da);
450 composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da);
451 if (q->colorspace == CMYKColorspace)
452 composite->black=gamma*Difference(p->black,Sa,q->black,Da);
455 static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
456 const MagickRealType Dca,const MagickRealType Da)
459 Divide Source by Destination
463 But with appropriate handling for special case of Dc == 0 specifically
464 so that f(Black,Black)=Black and f(non-Black,Black)=White.
465 It is however also important to correctly do 'over' alpha blending which
466 is why the formula becomes so complex.
468 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
469 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
470 if (fabs(Dca) < MagickEpsilon)
471 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
472 return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
475 static inline void CompositeDivide(const Image *image,const PixelInfo *p,
476 const PixelInfo *q,PixelInfo *composite)
483 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
484 Da=QuantumScale*q->alpha;
485 if (image->sync == MagickFalse)
488 Handle channels as separate grayscale channels.
490 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
491 composite->red=QuantumRange*Divide(QuantumScale*p->red,1.0,
492 QuantumScale*q->red,1.0);
493 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
494 composite->green=QuantumRange*Divide(QuantumScale*p->green,1.0,
495 QuantumScale*q->green,1.0);
496 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
497 composite->blue=QuantumRange*Divide(QuantumScale*p->blue,1.0,
498 QuantumScale*q->blue,1.0);
499 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
500 (q->colorspace == CMYKColorspace))
501 composite->black=QuantumRange*Divide(QuantumScale*p->black,1.0,
502 QuantumScale*q->black,1.0);
503 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
504 composite->alpha=QuantumRange*(1.0-Divide(Sa,1.0,Da,1.0));
507 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
508 composite->alpha=(MagickRealType) QuantumRange*gamma;
509 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
510 composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale*
512 composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale*
514 composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale*
516 if (q->colorspace == CMYKColorspace)
517 composite->black=gamma*Divide(QuantumScale*p->black*Sa,Sa,QuantumScale*
521 static MagickRealType Exclusion(const MagickRealType Sca,
522 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
524 return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
527 static inline void CompositeExclusion(const Image *image,const PixelInfo *p,
528 const PixelInfo *q,PixelInfo *composite)
535 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
536 Da=QuantumScale*q->alpha;
537 if (image->sync == MagickFalse)
540 Handle channels as separate grayscale channels.
542 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
543 composite->red=QuantumRange*Exclusion(QuantumScale*p->red,1.0,
544 QuantumScale*q->red,1.0);
545 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
546 composite->green=QuantumRange*Exclusion(QuantumScale*p->green,1.0,
547 QuantumScale*q->green,1.0);
548 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
549 composite->blue=QuantumRange*Exclusion(QuantumScale*p->blue,1.0,
550 QuantumScale*q->blue,1.0);
551 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
552 (q->colorspace == CMYKColorspace))
553 composite->black=QuantumRange*Exclusion(QuantumScale*p->black,1.0,
554 QuantumScale*q->black,1.0);
555 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
556 composite->alpha=QuantumRange*(1.0-Exclusion(Sa,1.0,Da,1.0));
559 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
560 composite->alpha=(MagickRealType) QuantumRange*gamma;
561 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
562 composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale*
564 composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale*
566 composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale*
568 if (q->colorspace == CMYKColorspace)
569 composite->black=gamma*Exclusion(QuantumScale*p->black*Sa,Sa,
570 QuantumScale*q->black*Da,Da);
573 static MagickRealType HardLight(const MagickRealType Sca,
574 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
577 return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
578 return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
581 static inline void CompositeHardLight(const PixelInfo *p,const PixelInfo *q,
582 PixelInfo *composite)
589 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
590 Da=QuantumScale*q->alpha;
591 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
592 composite->alpha=(MagickRealType) QuantumRange*gamma;
593 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
594 composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
596 composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
598 composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
600 if (q->colorspace == CMYKColorspace)
601 composite->black=gamma*HardLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
605 static void CompositeHSB(const MagickRealType red,const MagickRealType green,
606 const MagickRealType blue,double *hue,double *saturation,double *brightness)
614 Convert RGB to HSB colorspace.
616 assert(hue != (double *) NULL);
617 assert(saturation != (double *) NULL);
618 assert(brightness != (double *) NULL);
619 max=(red > green ? red : green);
622 min=(red < green ? red : green);
627 *brightness=(double) (QuantumScale*max);
630 *saturation=(double) (1.0-min/max);
635 *hue=(double) ((green-blue)/delta);
638 *hue=(double) (2.0+(blue-red)/delta);
641 *hue=(double) (4.0+(red-green)/delta);
647 static inline MagickRealType In(const MagickRealType p,const MagickRealType Sa,
648 const MagickRealType magick_unused(q),const MagickRealType Da)
653 static inline void CompositeIn(const PixelInfo *p,const PixelInfo *q,
654 PixelInfo *composite)
661 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
662 Da=QuantumScale*q->alpha;
664 composite->alpha=(MagickRealType) QuantumRange*gamma;
665 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
666 composite->red=gamma*In(p->red,Sa,q->red,Da);
667 composite->green=gamma*In(p->green,Sa,q->green,Da);
668 composite->blue=gamma*In(p->blue,Sa,q->blue,Da);
669 if (q->colorspace == CMYKColorspace)
670 composite->black=gamma*In(p->black,Sa,q->black,Da);
673 static inline MagickRealType Lighten(const MagickRealType p,
674 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
677 return(MagickOver_(p,alpha,q,beta)); /* src-over */
678 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
681 static inline void CompositeLighten(const Image *image,const PixelInfo *p,
682 const PixelInfo *q,PixelInfo *composite)
688 Lighten is also equvalent to a 'Maximum' method OR a greyscale version of a
689 binary 'And' OR the 'Union' of pixel sets.
691 if (image->sync == MagickFalse)
694 Handle channels as separate grayscale channels
696 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
697 composite->red=MagickMax(p->red,q->red);
698 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
699 composite->green=MagickMax(p->green,q->green);
700 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
701 composite->blue=MagickMax(p->blue,q->blue);
702 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
703 (q->colorspace == CMYKColorspace))
704 composite->black=MagickMax(p->black,q->black);
705 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
706 composite->alpha=MagickMin(p->alpha,q->alpha);
709 composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */
710 gamma=1.0-QuantumScale*composite->alpha;
711 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
712 composite->red=gamma*Lighten(p->red,p->alpha,q->red,q->alpha);
713 composite->green=gamma*Lighten(p->green,p->alpha,q->green,q->alpha);
714 composite->blue=gamma*Lighten(p->blue,p->alpha,q->blue,q->alpha);
715 if (q->colorspace == CMYKColorspace)
716 composite->black=gamma*Lighten(p->black,p->alpha,q->black,q->alpha);
719 static inline void CompositeLightenIntensity(const Image *image,
720 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
727 Select the pixel based on the intensity level.
728 If 'Sync' flag select whole pixel based on alpha weighted intensity.
729 Otherwise use Intenisty only, but restrict copy according to channel.
731 if (image->sync == MagickFalse)
736 from_p=GetPixelInfoIntensity(p) > GetPixelInfoIntensity(q) ? MagickTrue :
738 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
739 composite->red=from_p != MagickFalse ? p->red : q->red;
740 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
741 composite->green=from_p != MagickFalse ? p->green : q->green;
742 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
743 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
744 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
745 (q->colorspace == CMYKColorspace))
746 composite->black=from_p != MagickFalse ? p->black : q->black;
747 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
748 composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha;
751 Sa=QuantumScale*p->alpha;
752 Da=QuantumScale*q->alpha;
753 *composite=(Sa*GetPixelInfoIntensity(p) > Da*GetPixelInfoIntensity(q)) ?
757 static inline void CompositeLinearDodge(const PixelInfo *p,const PixelInfo *q,
758 PixelInfo *composite)
765 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
766 Da=QuantumScale*q->alpha;
767 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
768 composite->alpha=(MagickRealType) QuantumRange*gamma;
769 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
770 composite->red=gamma*(p->red*Sa+q->red*Da);
771 composite->green=gamma*(p->green*Sa+q->green*Da);
772 composite->blue=gamma*(p->blue*Sa+q->blue*Da);
773 if (q->colorspace == CMYKColorspace)
774 composite->black=gamma*(p->black*Sa+q->black*Da);
778 static inline MagickRealType LinearBurn(const MagickRealType Sca,
779 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
782 LinearBurn: as defined by Abode Photoshop, according to
783 http://www.simplefilter.de/en/basics/mixmods.html is:
785 f(Sc,Dc) = Sc + Dc - 1
787 return(Sca+Dca-Sa*Da);
790 static inline void CompositeLinearBurn(const PixelInfo *p,const PixelInfo *q,
791 PixelInfo *composite)
798 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
799 Da=QuantumScale*q->alpha;
800 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
801 composite->alpha=(MagickRealType) QuantumRange*gamma;
802 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
803 composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
805 composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
807 composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
809 if (q->colorspace == CMYKColorspace)
810 composite->black=gamma*LinearBurn(QuantumScale*p->black*Sa,Sa,QuantumScale*
814 static inline MagickRealType LinearLight(const MagickRealType Sca,
815 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
818 LinearLight: as defined by Abode Photoshop, according to
819 http://www.simplefilter.de/en/basics/mixmods.html is:
821 f(Sc,Dc) = Dc + 2*Sc - 1
823 return((Sca-Sa)*Da+Sca+Dca);
826 static inline void CompositeLinearLight(const PixelInfo *p,const PixelInfo *q,
827 PixelInfo *composite)
834 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
835 Da=QuantumScale*q->alpha;
836 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
837 composite->alpha=(MagickRealType) QuantumRange*gamma;
838 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
839 composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
841 composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
843 composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
845 if (q->colorspace == CMYKColorspace)
846 composite->black=gamma*LinearLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
850 static inline MagickRealType Mathematics(const MagickRealType Sca,
851 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
852 const GeometryInfo *geometry_info)
858 'Mathematics' a free form user control mathematical composition is defined
861 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
863 Where the arguments A,B,C,D are (currently) passed to composite as
864 a command separated 'geometry' string in "compose:args" image artifact.
866 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
868 Applying the SVG transparency formula (see above), we get...
870 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
872 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
875 gamma=geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
876 geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
881 static inline void CompositeMathematics(const Image *image,const PixelInfo *p,
882 const PixelInfo *q,const GeometryInfo *args,PixelInfo *composite)
889 Sa=QuantumScale*p->alpha; /* ??? - AT */
890 Da=QuantumScale*q->alpha;
891 if (image->sync == MagickFalse)
894 Handle channels as separate grayscale channels.
896 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
897 composite->red=QuantumRange*Mathematics(QuantumScale*p->red,1.0,
898 QuantumScale*q->red,1.0,args);
899 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
900 composite->green=QuantumRange*Mathematics(QuantumScale*p->green,1.0,
901 QuantumScale*q->green,1.0,args);
902 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
903 composite->blue=QuantumRange*Mathematics(QuantumScale*p->blue,1.0,
904 QuantumScale*q->blue,1.0,args);
905 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
906 (q->colorspace == CMYKColorspace))
907 composite->black=QuantumRange*Mathematics(QuantumScale*p->black,1.0,
908 QuantumScale*q->black,1.0,args);
909 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
910 composite->alpha=QuantumRange*(1.0-Mathematics(Sa,1.0,Da,1.0,args));
913 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
914 composite->alpha=(MagickRealType) QuantumRange*gamma;
915 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
916 composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
918 composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,
919 QuantumScale*q->green*Da,Da,args);
920 composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
922 if (q->colorspace == CMYKColorspace)
923 composite->black=gamma*Mathematics(QuantumScale*p->black*Sa,Sa,
924 QuantumScale*q->black*Da,Da,args);
927 static inline void CompositePlus(const Image *image,const PixelInfo *p,
928 const PixelInfo *q,PixelInfo *composite)
931 NOTE: "Plus" does not use 'over' alpha-blending but uses a special
932 'plus' form of alph-blending. It is the ONLY mathematical operator to
933 do this. this is what makes it different to the otherwise equivalent
934 "LinearDodge" composition method.
936 Note however that color channels are still effected by the alpha channel
937 as a result of the blending, making it just as useless for independant
938 channel maths, just like all other mathematical composition methods.
940 As such the removal of the 'sync' flag, is still a usful convention.
942 The CompositePixelInfoPlus() function is defined in
943 "composite-private.h" so it can also be used for Image Blending.
945 if (image->sync == MagickFalse)
948 Handle channels as separate grayscale channels.
950 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
951 composite->red=p->red+q->red;
952 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
953 composite->green=p->green+q->green;
954 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
955 composite->blue=p->blue+q->blue;
956 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
957 (q->colorspace == CMYKColorspace))
958 composite->black=p->black+q->black;
959 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
960 composite->alpha=p->alpha+q->alpha-QuantumRange;
963 CompositePixelInfoPlus(p,p->alpha,q,q->alpha,composite);
966 static inline MagickRealType Minus(const MagickRealType Sca,
967 const MagickRealType Sa,const MagickRealType Dca,
968 const MagickRealType magick_unused(Da))
971 Minus Source from Destination
975 return(Sca+Dca-2.0*Dca*Sa);
978 static inline void CompositeMinus(const Image *image,const PixelInfo *p,
979 const PixelInfo *q,PixelInfo *composite)
986 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
987 Da=QuantumScale*q->alpha;
988 if (image->sync == MagickFalse)
991 Handle channels as separate grayscale channels.
993 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
994 composite->red=p->red-q->red;
995 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
996 composite->green=p->green-q->green;
997 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
998 composite->blue=p->blue-q->blue;
999 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1000 (q->colorspace == CMYKColorspace))
1001 composite->black=p->black-q->black;
1002 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1003 composite->alpha=QuantumRange*(1.0-(Sa-Da));
1006 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1007 composite->alpha=(MagickRealType) QuantumRange*gamma;
1008 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1009 composite->red=gamma*Minus(p->red*Sa,Sa,q->red*Da,Da);
1010 composite->green=gamma*Minus(p->green*Sa,Sa,q->green*Da,Da);
1011 composite->blue=gamma*Minus(p->blue*Sa,Sa,q->blue*Da,Da);
1012 if (q->colorspace == CMYKColorspace)
1013 composite->black=gamma*Minus(p->black*Sa,Sa,q->black*Da,Da);
1016 static inline MagickRealType ModulusAdd(const MagickRealType p,
1017 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1023 if (pixel > QuantumRange)
1024 pixel-=(QuantumRange+1.0);
1025 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1028 static inline void CompositeModulusAdd(const Image *image,const PixelInfo *p,
1029 const PixelInfo *q,PixelInfo *composite)
1036 if (image->sync == MagickFalse)
1039 Handle channels as separate grayscale channels.
1041 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1042 composite->red=ModulusAdd(p->red,1.0,q->red,1.0);
1043 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1044 composite->green=ModulusAdd(p->green,1.0,q->green,1.0);
1045 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1046 composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0);
1047 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1048 (q->colorspace == CMYKColorspace))
1049 composite->black=ModulusAdd(p->black,1.0,q->black,1.0);
1050 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1051 composite->alpha=ModulusAdd(p->alpha,1.0,q->alpha,1.0);
1054 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1055 Da=QuantumScale*q->alpha;
1056 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1057 composite->alpha=(MagickRealType) QuantumRange*gamma;
1058 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1059 composite->red=ModulusAdd(p->red,Sa,q->red,Da);
1060 composite->green=ModulusAdd(p->green,Sa,q->green,Da);
1061 composite->blue=ModulusAdd(p->blue,Sa,q->blue,Da);
1062 if (q->colorspace == CMYKColorspace)
1063 composite->black=ModulusAdd(p->black,Sa,q->black,Da);
1066 static inline MagickRealType ModulusSubtract(const MagickRealType p,
1067 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1074 pixel+=(QuantumRange+1.0);
1075 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1078 static inline void CompositeModulusSubtract(const Image *image,
1079 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
1086 if (image->sync == MagickFalse)
1089 Handle channels as separate grayscale channels,
1091 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1092 composite->red=ModulusSubtract(p->red,1.0,q->red,1.0);
1093 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1094 composite->green=ModulusSubtract(p->green,1.0,q->green,1.0);
1095 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1096 composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0);
1097 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1098 (q->colorspace == CMYKColorspace))
1099 composite->black=ModulusSubtract(p->black,1.0,q->black,1.0);
1100 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1101 composite->alpha=ModulusSubtract(p->alpha,1.0,q->alpha,1.0);
1104 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1105 Da=QuantumScale*q->alpha;
1106 gamma = RoundToUnity(Sa+Da-Sa*Da);
1107 composite->alpha=(MagickRealType) QuantumRange*gamma;
1108 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1109 composite->red=ModulusSubtract(p->red,Sa,q->red,Da);
1110 composite->green=ModulusSubtract(p->green,Sa,q->green,Da);
1111 composite->blue=ModulusSubtract(p->blue,Sa,q->blue,Da);
1112 if (q->colorspace == CMYKColorspace)
1113 composite->black=ModulusSubtract(p->black,Sa,q->black,Da);
1116 static inline MagickRealType Multiply(const MagickRealType Sca,
1117 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1119 return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1122 static inline void CompositeMultiply(const Image *image,const PixelInfo *p,
1123 const PixelInfo *q,PixelInfo *composite)
1130 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1131 Da=QuantumScale*q->alpha;
1132 if (image->sync == MagickFalse)
1135 Handle channels as separate grayscale channels.
1137 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1138 composite->red=QuantumScale*p->red*q->red;
1139 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1140 composite->green=QuantumScale*p->green*q->green;
1141 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1142 composite->blue=QuantumScale*p->blue*q->blue;
1143 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1144 (q->colorspace == CMYKColorspace))
1145 composite->black=QuantumScale*p->black*q->black;
1146 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1147 composite->alpha=QuantumRange*(1.0-Sa*Da);
1150 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1151 composite->alpha=(MagickRealType) QuantumRange*gamma;
1152 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1153 composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
1155 composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
1157 composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1159 if (q->colorspace == CMYKColorspace)
1160 composite->black=gamma*Multiply(QuantumScale*p->black*Sa,Sa,
1161 QuantumScale*q->black*Da,Da);
1164 static inline MagickRealType Out(const MagickRealType p,const MagickRealType Sa,
1165 const MagickRealType magick_unused(q),const MagickRealType Da)
1167 return(Sa*p*(1.0-Da));
1170 static inline void CompositeOut(const PixelInfo *p,const PixelInfo *q,
1171 PixelInfo *composite)
1178 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1179 Da=QuantumScale*q->alpha;
1181 composite->alpha=(MagickRealType) QuantumRange*gamma;
1182 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1183 composite->red=gamma*Out(p->red,Sa,q->red,Da);
1184 composite->green=gamma*Out(p->green,Sa,q->green,Da);
1185 composite->blue=gamma*Out(p->blue,Sa,q->blue,Da);
1186 if (q->colorspace == CMYKColorspace)
1187 composite->black=gamma*Out(p->black,Sa,q->black,Da);
1190 static MagickRealType PegtopLight(const MagickRealType Sca,
1191 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1194 PegTop: A Soft-Light alternative: A continuous version of the Softlight
1195 function, producing very similar results.
1197 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
1199 See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
1201 if (fabs(Da) < MagickEpsilon)
1203 return(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+Dca*(1.0-Sa));
1206 static inline void CompositePegtopLight(const PixelInfo *p,const PixelInfo *q,
1207 PixelInfo *composite)
1214 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1215 Da=QuantumScale*q->alpha;
1216 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1217 composite->alpha=(MagickRealType) QuantumRange*gamma;
1218 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1219 composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1221 composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1223 composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1225 if (q->colorspace == CMYKColorspace)
1226 composite->black=gamma*PegtopLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1230 static MagickRealType PinLight(const MagickRealType Sca,const MagickRealType Sa,
1231 const MagickRealType Dca,const MagickRealType Da)
1234 PinLight: A Photoshop 7 composition method
1235 http://www.simplefilter.de/en/basics/mixmods.html
1237 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
1239 if (Dca*Sa < Da*(2.0*Sca-Sa))
1240 return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
1241 if ((Dca*Sa) > (2.0*Sca*Da))
1242 return(Sca*Da+Sca+Dca*(1.0-Sa));
1243 return(Sca*(1.0-Da)+Dca);
1246 static inline void CompositePinLight(const PixelInfo *p,const PixelInfo *q,
1247 PixelInfo *composite)
1254 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1255 Da=QuantumScale*q->alpha;
1256 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1257 composite->alpha=(MagickRealType) QuantumRange*gamma;
1258 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1259 composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1261 composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1263 composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1265 if (q->colorspace == CMYKColorspace)
1266 composite->black=gamma*PinLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1270 static inline MagickRealType Screen(const MagickRealType Sca,
1271 const MagickRealType Dca)
1274 Screen: A negated multiply
1275 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
1277 return(Sca+Dca-Sca*Dca);
1280 static inline void CompositeScreen(const Image *image,const PixelInfo *p,
1281 const PixelInfo *q,PixelInfo *composite)
1288 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1289 Da=QuantumScale*q->alpha;
1290 if (image->sync == MagickFalse)
1293 Handle channels as separate grayscale channels.
1295 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1296 composite->red=QuantumRange*Screen(QuantumScale*p->red,
1297 QuantumScale*q->red);
1298 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1299 composite->green=QuantumRange*Screen(QuantumScale*p->green,
1300 QuantumScale*q->green);
1301 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1302 composite->blue=QuantumRange*Screen(QuantumScale*p->blue,
1303 QuantumScale*q->blue);
1304 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1305 (q->colorspace == CMYKColorspace))
1306 composite->black=QuantumRange*Screen(QuantumScale*p->black,
1307 QuantumScale*q->black);
1308 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1309 composite->alpha=QuantumRange*(1.0-Screen(Sa,Da));
1312 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1313 composite->alpha=(MagickRealType) QuantumRange*gamma;
1314 Sa*=QuantumScale; Da*=QuantumScale; /* optimization */
1315 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1316 composite->red=gamma*Screen(p->red*Sa,q->red*Da);
1317 composite->green=gamma*Screen(p->green*Sa,q->green*Da);
1318 composite->blue=gamma*Screen(p->blue*Sa,q->blue*Da);
1319 if (q->colorspace == CMYKColorspace)
1320 composite->black=gamma*Screen(p->black*Sa,q->black*Da);
1323 static MagickRealType SoftLight(const MagickRealType Sca,
1324 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1331 New specification: March 2009 SVG specification.
1335 return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1336 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1338 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
1339 alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1342 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1346 static inline void CompositeSoftLight(const PixelInfo *p,const PixelInfo *q,
1347 PixelInfo *composite)
1354 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1355 Da=QuantumScale*q->alpha;
1356 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1357 composite->alpha=(MagickRealType) QuantumRange*gamma;
1358 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1359 composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1361 composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1363 composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1365 if (q->colorspace == CMYKColorspace)
1366 composite->black=gamma*SoftLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1370 static inline MagickRealType Threshold(const MagickRealType p,
1371 const MagickRealType q,const MagickRealType threshold,
1372 const MagickRealType amount)
1378 Multiply difference by amount, if differance larger than threshold???
1379 What use this is is completely unknown. The Opacity calculation appears to
1380 be inverted -- Anthony Thyssen
1385 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
1387 return(q+delta*amount);
1390 static inline void CompositeThreshold(const PixelInfo *p,const PixelInfo *q,
1391 const MagickRealType threshold,const MagickRealType amount,
1392 PixelInfo *composite)
1394 composite->red=Threshold(p->red,q->red,threshold,amount);
1395 composite->green=Threshold(p->green,q->green,threshold,amount);
1396 composite->blue=Threshold(p->blue,q->blue,threshold,amount);
1397 composite->alpha=Threshold(p->alpha,q->alpha,threshold,amount);
1398 if (q->colorspace == CMYKColorspace)
1399 composite->black=Threshold(p->black,q->black,threshold,amount);
1403 static MagickRealType VividLight(const MagickRealType Sca,
1404 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1407 VividLight: A Photoshop 7 composition method. See
1408 http://www.simplefilter.de/en/basics/mixmods.html.
1410 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
1412 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
1413 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1414 if ((2.0*Sca) <= Sa)
1415 return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1416 return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1419 static inline void CompositeVividLight(const PixelInfo *p,const PixelInfo *q,
1420 PixelInfo *composite)
1427 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1428 Da=QuantumScale*q->alpha;
1429 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1430 composite->alpha=(MagickRealType) QuantumRange*gamma;
1431 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1432 composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1434 composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1436 composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1438 if (q->colorspace == CMYKColorspace)
1439 composite->black=gamma*VividLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1443 static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
1444 const MagickRealType Dca,const MagickRealType Da)
1446 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
1449 static inline void CompositeXor(const PixelInfo *p,const PixelInfo *q,
1450 PixelInfo *composite)
1457 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1458 Da=QuantumScale*q->alpha;
1459 gamma=Sa+Da-2.0*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
1460 composite->alpha=(MagickRealType) QuantumRange*gamma;
1461 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1462 composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
1463 composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
1464 composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
1465 if (q->colorspace == CMYKColorspace)
1466 composite->black=gamma*Xor(p->black*Sa,Sa,q->black*Da,Da);
1469 static void HSBComposite(const double hue,const double saturation,
1470 const double brightness,MagickRealType *red,MagickRealType *green,
1471 MagickRealType *blue)
1481 Convert HSB to RGB colorspace.
1483 assert(red != (MagickRealType *) NULL);
1484 assert(green != (MagickRealType *) NULL);
1485 assert(blue != (MagickRealType *) NULL);
1486 if (saturation == 0.0)
1488 *red=(MagickRealType) QuantumRange*brightness;
1493 h=6.0*(hue-floor(hue));
1494 f=h-floor((double) h);
1495 p=brightness*(1.0-saturation);
1496 q=brightness*(1.0-saturation*f);
1497 t=brightness*(1.0-saturation*(1.0-f));
1503 *red=(MagickRealType) QuantumRange*brightness;
1504 *green=(MagickRealType) QuantumRange*t;
1505 *blue=(MagickRealType) QuantumRange*p;
1510 *red=(MagickRealType) QuantumRange*q;
1511 *green=(MagickRealType) QuantumRange*brightness;
1512 *blue=(MagickRealType) QuantumRange*p;
1517 *red=(MagickRealType) QuantumRange*p;
1518 *green=(MagickRealType) QuantumRange*brightness;
1519 *blue=(MagickRealType) QuantumRange*t;
1524 *red=(MagickRealType) QuantumRange*p;
1525 *green=(MagickRealType) QuantumRange*q;
1526 *blue=(MagickRealType) QuantumRange*brightness;
1531 *red=(MagickRealType) QuantumRange*t;
1532 *green=(MagickRealType) QuantumRange*p;
1533 *blue=(MagickRealType) QuantumRange*brightness;
1538 *red=(MagickRealType) QuantumRange*brightness;
1539 *green=(MagickRealType) QuantumRange*p;
1540 *blue=(MagickRealType) QuantumRange*q;
1546 MagickExport MagickBooleanType CompositeImage(Image *image,
1547 const CompositeOperator compose,const Image *composite_image,
1548 const ssize_t x_offset,const ssize_t y_offset)
1550 #define CompositeImageTag "Composite/Image"
1572 modify_outside_overlay,
1580 destination_dissolve,
1597 Prepare composite image.
1599 assert(image != (Image *) NULL);
1600 assert(image->signature == MagickSignature);
1601 if (image->debug != MagickFalse)
1602 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1603 assert(composite_image != (Image *) NULL);
1604 assert(composite_image->signature == MagickSignature);
1605 exception=(&image->exception);
1606 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1607 return(MagickFalse);
1608 GetPixelInfo(image,&zero);
1609 destination_image=(Image *) NULL;
1611 destination_dissolve=1.0;
1612 modify_outside_overlay=MagickFalse;
1613 percent_brightness=100.0;
1614 percent_saturation=100.0;
1615 source_dissolve=1.0;
1619 case ClearCompositeOp:
1620 case SrcCompositeOp:
1622 case SrcInCompositeOp:
1623 case OutCompositeOp:
1624 case SrcOutCompositeOp:
1625 case DstInCompositeOp:
1626 case DstAtopCompositeOp:
1629 Modify destination outside the overlaid region.
1631 modify_outside_overlay=MagickTrue;
1634 case CopyCompositeOp:
1636 if ((x_offset < 0) || (y_offset < 0))
1638 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
1640 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
1643 image_view=AcquireCacheView(image);
1644 composite_view=AcquireCacheView(composite_image);
1645 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1646 #pragma omp parallel for schedule(dynamic,4) shared(status)
1648 for (y=0; y < (ssize_t) composite_image->rows; y++)
1653 register const Quantum
1662 if (status == MagickFalse)
1664 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1666 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1667 composite_image->columns,1,exception);
1668 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1673 for (x=0; x < (ssize_t) composite_image->columns; x++)
1675 SetPixelRed(image,GetPixelRed(composite_image,p),q);
1676 SetPixelGreen(image,GetPixelGreen(composite_image,p),q);
1677 SetPixelBlue(image,GetPixelBlue(composite_image,p),q);
1678 SetPixelAlpha(image,GetPixelAlpha(composite_image,p),q);
1679 if (image->colorspace == CMYKColorspace)
1680 SetPixelBlack(image,GetPixelBlack(composite_image,p),q);
1681 p+=GetPixelChannels(composite_image);
1682 q+=GetPixelChannels(image);
1684 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1685 if (sync == MagickFalse)
1687 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1692 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1693 #pragma omp critical (MagickCore_CompositeImage)
1695 proceed=SetImageProgress(image,CompositeImageTag,
1696 (MagickOffsetType) y,image->rows);
1697 if (proceed == MagickFalse)
1701 composite_view=DestroyCacheView(composite_view);
1702 image_view=DestroyCacheView(image_view);
1705 case CopyOpacityCompositeOp:
1706 case ChangeMaskCompositeOp:
1709 Modify destination outside the overlaid region and require an alpha
1710 channel to exist, to add transparency.
1712 if (image->matte == MagickFalse)
1713 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1714 modify_outside_overlay=MagickTrue;
1717 case BlurCompositeOp:
1739 Blur Image dictated by an overlay gradient map: X = red_channel;
1740 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
1742 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1744 if (destination_image == (Image *) NULL)
1745 return(MagickFalse);
1747 Determine the horizontal and vertical maximim blur.
1749 SetGeometryInfo(&geometry_info);
1751 value=GetImageArtifact(composite_image,"compose:args");
1752 if (value != (char *) NULL)
1753 flags=ParseGeometry(value,&geometry_info);
1754 if ((flags & WidthValue) == 0 )
1756 destination_image=DestroyImage(destination_image);
1757 return(MagickFalse);
1759 width=geometry_info.rho;
1760 height=geometry_info.sigma;
1761 blur.x1=geometry_info.rho;
1764 blur.y2=geometry_info.sigma;
1767 if ((flags & HeightValue) == 0)
1769 if ((flags & XValue) != 0 )
1774 angle=DegreesToRadians(geometry_info.xi);
1775 blur.x1=width*cos(angle);
1776 blur.x2=width*sin(angle);
1777 blur.y1=(-height*sin(angle));
1778 blur.y2=height*cos(angle);
1780 if ((flags & YValue) != 0 )
1782 angle_start=DegreesToRadians(geometry_info.xi);
1783 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1786 Blur Image by resampling.
1789 exception=(&image->exception);
1790 resample_filter=AcquireResampleFilter(image,&image->exception);
1791 SetResampleFilter(resample_filter,CubicFilter,2.0);
1792 destination_view=AcquireCacheView(destination_image);
1793 composite_view=AcquireCacheView(composite_image);
1794 for (y=0; y < (ssize_t) composite_image->rows; y++)
1799 register const Quantum
1808 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1810 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1812 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1813 destination_image->columns,1,&image->exception);
1814 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1816 for (x=0; x < (ssize_t) composite_image->columns; x++)
1818 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1820 p+=GetPixelChannels(composite_image);
1823 if (fabs(angle_range) > MagickEpsilon)
1828 angle=angle_start+angle_range*QuantumScale*
1829 GetPixelBlue(composite_image,p);
1830 blur.x1=width*cos(angle);
1831 blur.x2=width*sin(angle);
1832 blur.y1=(-height*sin(angle));
1833 blur.y2=height*cos(angle);
1835 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
1836 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
1837 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
1838 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
1839 GetPixelGreen(composite_image,p));
1840 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1841 (double) y_offset+y,&pixel);
1842 SetPixelPixelInfo(destination_image,&pixel,q);
1843 p+=GetPixelChannels(composite_image);
1844 q+=GetPixelChannels(destination_image);
1846 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1847 if (sync == MagickFalse)
1850 resample_filter=DestroyResampleFilter(resample_filter);
1851 composite_view=DestroyCacheView(composite_view);
1852 destination_view=DestroyCacheView(destination_view);
1853 composite_image=destination_image;
1856 case DisplaceCompositeOp:
1857 case DistortCompositeOp:
1876 Displace/Distort based on overlay gradient map:
1877 X = red_channel; Y = green_channel;
1878 compose:args = x_scale[,y_scale[,center.x,center.y]]
1880 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1882 if (destination_image == (Image *) NULL)
1883 return(MagickFalse);
1884 SetGeometryInfo(&geometry_info);
1886 value=GetImageArtifact(composite_image,"compose:args");
1887 if (value != (char *) NULL)
1888 flags=ParseGeometry(value,&geometry_info);
1889 if ((flags & (WidthValue|HeightValue)) == 0 )
1891 if ((flags & AspectValue) == 0)
1893 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1895 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1899 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1900 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1905 horizontal_scale=geometry_info.rho;
1906 vertical_scale=geometry_info.sigma;
1907 if ((flags & PercentValue) != 0)
1909 if ((flags & AspectValue) == 0)
1911 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1912 vertical_scale*=(composite_image->rows-1.0)/200.0;
1916 horizontal_scale*=(image->columns-1.0)/200.0;
1917 vertical_scale*=(image->rows-1.0)/200.0;
1920 if ((flags & HeightValue) == 0)
1921 vertical_scale=horizontal_scale;
1924 Determine fixed center point for absolute distortion map
1926 Displace offset relative to a fixed absolute point
1927 Select that point according to +X+Y user inputs.
1928 default = center of overlay image
1929 arg flag '!' = locations/percentage relative to background image
1931 center.x=(MagickRealType) x_offset;
1932 center.y=(MagickRealType) y_offset;
1933 if (compose == DistortCompositeOp)
1935 if ((flags & XValue) == 0)
1936 if ((flags & AspectValue) == 0)
1937 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
1940 center.x=((MagickRealType) image->columns-1)/2.0;
1942 if ((flags & AspectValue) == 0)
1943 center.x=(MagickRealType) x_offset+geometry_info.xi;
1945 center.x=geometry_info.xi;
1946 if ((flags & YValue) == 0)
1947 if ((flags & AspectValue) == 0)
1948 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
1950 center.y=((MagickRealType) image->rows-1)/2.0;
1952 if ((flags & AspectValue) == 0)
1953 center.y=(MagickRealType) y_offset+geometry_info.psi;
1955 center.y=geometry_info.psi;
1958 Shift the pixel offset point as defined by the provided,
1959 displacement/distortion map. -- Like a lens...
1962 exception=(&image->exception);
1963 image_view=AcquireCacheView(image);
1964 destination_view=AcquireCacheView(destination_image);
1965 composite_view=AcquireCacheView(composite_image);
1966 for (y=0; y < (ssize_t) composite_image->rows; y++)
1971 register const Quantum
1980 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1982 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1984 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1985 destination_image->columns,1,&image->exception);
1986 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1988 for (x=0; x < (ssize_t) composite_image->columns; x++)
1990 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1992 p+=GetPixelChannels(composite_image);
1996 Displace the offset.
1998 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1999 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2000 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
2002 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
2003 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2004 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
2006 (void) InterpolatePixelInfo(image,image_view,
2007 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2010 Mask with the 'invalid pixel mask' in alpha channel.
2012 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
2013 pixel.alpha)*(1.0-QuantumScale*
2014 GetPixelAlpha(composite_image,p)));
2015 SetPixelPixelInfo(destination_image,&pixel,q);
2016 p+=GetPixelChannels(composite_image);
2017 q+=GetPixelChannels(destination_image);
2019 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
2020 if (sync == MagickFalse)
2023 destination_view=DestroyCacheView(destination_view);
2024 composite_view=DestroyCacheView(composite_view);
2025 image_view=DestroyCacheView(image_view);
2026 composite_image=destination_image;
2029 case DissolveCompositeOp:
2032 Geometry arguments to dissolve factors.
2034 value=GetImageArtifact(composite_image,"compose:args");
2035 if (value != (char *) NULL)
2037 flags=ParseGeometry(value,&geometry_info);
2038 source_dissolve=geometry_info.rho/100.0;
2039 destination_dissolve=1.0;
2040 if ((source_dissolve-MagickEpsilon) < 0.0)
2041 source_dissolve=0.0;
2042 if ((source_dissolve+MagickEpsilon) > 1.0)
2044 destination_dissolve=2.0-source_dissolve;
2045 source_dissolve=1.0;
2047 if ((flags & SigmaValue) != 0)
2048 destination_dissolve=geometry_info.sigma/100.0;
2049 if ((destination_dissolve-MagickEpsilon) < 0.0)
2050 destination_dissolve=0.0;
2051 modify_outside_overlay=MagickTrue;
2052 if ((destination_dissolve+MagickEpsilon) > 1.0 )
2054 destination_dissolve=1.0;
2055 modify_outside_overlay=MagickFalse;
2060 case BlendCompositeOp:
2062 value=GetImageArtifact(composite_image,"compose:args");
2063 if (value != (char *) NULL)
2065 flags=ParseGeometry(value,&geometry_info);
2066 source_dissolve=geometry_info.rho/100.0;
2067 destination_dissolve=1.0-source_dissolve;
2068 if ((flags & SigmaValue) != 0)
2069 destination_dissolve=geometry_info.sigma/100.0;
2070 modify_outside_overlay=MagickTrue;
2071 if ((destination_dissolve+MagickEpsilon) > 1.0)
2072 modify_outside_overlay=MagickFalse;
2076 case MathematicsCompositeOp:
2079 Just collect the values from "compose:args", setting.
2080 Unused values are set to zero automagically.
2082 Arguments are normally a comma separated list, so this probably should
2083 be changed to some 'general comma list' parser, (with a minimum
2086 SetGeometryInfo(&geometry_info);
2087 value=GetImageArtifact(composite_image,"compose:args");
2088 if (value != (char *) NULL)
2089 (void) ParseGeometry(value,&geometry_info);
2092 case ModulateCompositeOp:
2095 Determine the brightness and saturation scale.
2097 value=GetImageArtifact(composite_image,"compose:args");
2098 if (value != (char *) NULL)
2100 flags=ParseGeometry(value,&geometry_info);
2101 percent_brightness=geometry_info.rho;
2102 if ((flags & SigmaValue) != 0)
2103 percent_saturation=geometry_info.sigma;
2107 case ThresholdCompositeOp:
2110 Determine the amount and threshold.
2112 value=GetImageArtifact(composite_image,"compose:args");
2113 if (value != (char *) NULL)
2115 flags=ParseGeometry(value,&geometry_info);
2116 amount=geometry_info.rho;
2117 threshold=geometry_info.sigma;
2118 if ((flags & SigmaValue) == 0)
2121 threshold*=QuantumRange;
2127 value=GetImageArtifact(composite_image,"compose:outside-overlay");
2128 if (value != (const char *) NULL)
2129 modify_outside_overlay=IsMagickTrue(value);
2135 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2136 GetPixelInfo(composite_image,&zero);
2137 exception=(&image->exception);
2138 image_view=AcquireCacheView(image);
2139 composite_view=AcquireCacheView(composite_image);
2140 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2141 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2143 for (y=0; y < (ssize_t) image->rows; y++)
2158 register const Quantum
2167 if (status == MagickFalse)
2169 if (modify_outside_overlay == MagickFalse)
2173 if ((y-y_offset) >= (ssize_t) composite_image->rows)
2177 If pixels is NULL, y is outside overlay region.
2179 pixels=(Quantum *) NULL;
2181 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
2183 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
2184 composite_image->columns,1,exception);
2185 if (p == (const Quantum *) NULL)
2192 p-=x_offset*GetPixelChannels(composite_image);
2194 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2195 if (q == (Quantum *) NULL)
2205 for (x=0; x < (ssize_t) image->columns; x++)
2207 if (modify_outside_overlay == MagickFalse)
2211 q+=GetPixelChannels(image);
2214 if ((x-x_offset) >= (ssize_t) composite_image->columns)
2217 destination.red=(MagickRealType) GetPixelRed(image,q);
2218 destination.green=(MagickRealType) GetPixelGreen(image,q);
2219 destination.blue=(MagickRealType) GetPixelBlue(image,q);
2220 if (image->colorspace == CMYKColorspace)
2221 destination.black=(MagickRealType) GetPixelBlack(image,q);
2222 if (image->colorspace == CMYKColorspace)
2224 destination.red=(MagickRealType) QuantumRange-destination.red;
2225 destination.green=(MagickRealType) QuantumRange-destination.green;
2226 destination.blue=(MagickRealType) QuantumRange-destination.blue;
2227 destination.black=(MagickRealType) QuantumRange-destination.black;
2229 if (image->matte != MagickFalse)
2230 destination.alpha=(MagickRealType) GetPixelAlpha(image,q);
2232 Handle destination modifications outside overlaid region.
2234 composite=destination;
2235 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2236 ((x-x_offset) >= (ssize_t) composite_image->columns))
2240 case DissolveCompositeOp:
2241 case BlendCompositeOp:
2243 composite.alpha=destination_dissolve*(composite.alpha);
2246 case ClearCompositeOp:
2247 case SrcCompositeOp:
2249 CompositeClear(&destination,&composite);
2253 case SrcInCompositeOp:
2254 case OutCompositeOp:
2255 case SrcOutCompositeOp:
2256 case DstInCompositeOp:
2257 case DstAtopCompositeOp:
2258 case CopyOpacityCompositeOp:
2259 case ChangeMaskCompositeOp:
2261 composite.alpha=(MagickRealType) TransparentAlpha;
2266 (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,y-
2267 y_offset,&composite,exception);
2271 if (image->colorspace == CMYKColorspace)
2273 composite.red=(MagickRealType) QuantumRange-composite.red;
2274 composite.green=(MagickRealType) QuantumRange-composite.green;
2275 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2276 composite.black=(MagickRealType) QuantumRange-composite.black;
2278 SetPixelRed(image,ClampToQuantum(composite.red),q);
2279 SetPixelGreen(image,ClampToQuantum(composite.green),q);
2280 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2281 if (image->matte != MagickFalse)
2282 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2283 if (image->colorspace == CMYKColorspace)
2284 SetPixelBlack(image,ClampToQuantum(composite.black),q);
2285 q+=GetPixelChannels(image);
2289 Handle normal overlay of source onto destination.
2291 source.red=(MagickRealType) GetPixelRed(composite_image,p);
2292 source.green=(MagickRealType) GetPixelGreen(composite_image,p);
2293 source.blue=(MagickRealType) GetPixelBlue(composite_image,p);
2294 if (composite_image->colorspace == CMYKColorspace)
2295 source.black=(MagickRealType) GetPixelBlack(composite_image,p);
2296 if (composite_image->colorspace == CMYKColorspace)
2298 source.red=(MagickRealType) QuantumRange-source.red;
2299 source.green=(MagickRealType) QuantumRange-source.green;
2300 source.blue=(MagickRealType) QuantumRange-source.blue;
2301 source.black=(MagickRealType) QuantumRange-source.black;
2303 if (composite_image->matte != MagickFalse)
2304 source.alpha=(MagickRealType) GetPixelAlpha(composite_image,p);
2306 Porter-Duff compositions.
2310 case ClearCompositeOp:
2312 CompositeClear(&destination,&composite);
2315 case SrcCompositeOp:
2316 case CopyCompositeOp:
2317 case ReplaceCompositeOp:
2323 case DstCompositeOp:
2325 case OverCompositeOp:
2326 case SrcOverCompositeOp:
2328 CompositePixelInfoOver(&source,source.alpha,&destination,
2329 destination.alpha,&composite);
2332 case DstOverCompositeOp:
2334 CompositePixelInfoOver(&destination,destination.alpha,&source,
2335 source.alpha,&composite);
2338 case SrcInCompositeOp:
2341 CompositeIn(&source,&destination,&composite);
2344 case DstInCompositeOp:
2346 CompositeIn(&destination,&source,&composite);
2349 case OutCompositeOp:
2350 case SrcOutCompositeOp:
2352 CompositeOut(&source,&destination,&composite);
2355 case DstOutCompositeOp:
2357 CompositeOut(&destination,&source,&composite);
2360 case AtopCompositeOp:
2361 case SrcAtopCompositeOp:
2363 CompositeAtop(&source,&destination,&composite);
2366 case DstAtopCompositeOp:
2368 CompositeAtop(&destination,&source,&composite);
2371 case XorCompositeOp:
2373 CompositeXor(&source,&destination,&composite);
2376 case PlusCompositeOp:
2378 CompositePlus(image,&source,&destination,&composite);
2381 case MinusDstCompositeOp:
2383 CompositeMinus(image,&source,&destination,&composite);
2386 case MinusSrcCompositeOp:
2388 CompositeMinus(image,&destination,&source,&composite);
2391 case ModulusAddCompositeOp:
2393 CompositeModulusAdd(image,&source,&destination,&composite);
2396 case ModulusSubtractCompositeOp:
2398 CompositeModulusSubtract(image,&source,&destination,&composite);
2401 case DifferenceCompositeOp:
2403 CompositeDifference(image,&source,&destination,&composite);
2406 case ExclusionCompositeOp:
2408 CompositeExclusion(image,&source,&destination,&composite);
2411 case MultiplyCompositeOp:
2413 CompositeMultiply(image,&source,&destination,&composite);
2416 case ScreenCompositeOp:
2418 CompositeScreen(image,&source,&destination,&composite);
2421 case DivideDstCompositeOp:
2423 CompositeDivide(image,&source,&destination,&composite);
2426 case DivideSrcCompositeOp:
2428 CompositeDivide(image,&destination,&source,&composite);
2431 case DarkenCompositeOp:
2433 CompositeDarken(image,&source,&destination,&composite);
2436 case LightenCompositeOp:
2438 CompositeLighten(image,&source,&destination,&composite);
2441 case DarkenIntensityCompositeOp:
2443 CompositeDarkenIntensity(image,&source,&destination,&composite);
2446 case LightenIntensityCompositeOp:
2448 CompositeLightenIntensity(image,&source,&destination,&composite);
2451 case MathematicsCompositeOp:
2453 CompositeMathematics(image,&source,&destination,&geometry_info,
2457 case ColorDodgeCompositeOp:
2459 CompositeColorDodge(&source,&destination,&composite);
2462 case ColorBurnCompositeOp:
2464 CompositeColorBurn(&source,&destination,&composite);
2467 case LinearDodgeCompositeOp:
2469 CompositeLinearDodge(&source,&destination,&composite);
2472 case LinearBurnCompositeOp:
2474 CompositeLinearBurn(&source,&destination,&composite);
2477 case HardLightCompositeOp:
2479 CompositeHardLight(&source,&destination,&composite);
2482 case OverlayCompositeOp:
2484 CompositeHardLight(&destination,&source,&composite);
2487 case SoftLightCompositeOp:
2489 CompositeSoftLight(&source,&destination,&composite);
2492 case LinearLightCompositeOp:
2494 CompositeLinearLight(&source,&destination,&composite);
2497 case PegtopLightCompositeOp:
2499 CompositePegtopLight(&source,&destination,&composite);
2502 case VividLightCompositeOp:
2504 CompositeVividLight(&source,&destination,&composite);
2507 case PinLightCompositeOp:
2509 CompositePinLight(&source,&destination,&composite);
2512 case ChangeMaskCompositeOp:
2514 if ((composite.alpha > ((MagickRealType) QuantumRange/2.0)) ||
2515 (IsFuzzyEquivalencePixelInfo(&source,&destination) != MagickFalse))
2516 composite.alpha=(MagickRealType) TransparentAlpha;
2518 composite.alpha=(MagickRealType) OpaqueAlpha;
2521 case BumpmapCompositeOp:
2523 if (source.alpha == TransparentAlpha)
2525 CompositeBumpmap(&source,&destination,&composite);
2528 case DissolveCompositeOp:
2530 CompositePixelInfoOver(&source,source_dissolve*source.alpha,
2531 &destination,(MagickRealType) (destination_dissolve*
2532 destination.alpha),&composite);
2535 case BlendCompositeOp:
2537 CompositePixelInfoBlend(&source,source_dissolve,&destination,
2538 destination_dissolve,&composite);
2541 case ThresholdCompositeOp:
2543 CompositeThreshold(&source,&destination,threshold,amount,&composite);
2546 case ModulateCompositeOp:
2551 if (source.alpha == TransparentAlpha)
2553 offset=(ssize_t) (GetPixelInfoIntensity(&source)-midpoint);
2556 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2557 &saturation,&brightness);
2558 brightness+=(0.01*percent_brightness*offset)/midpoint;
2559 saturation*=0.01*percent_saturation;
2560 HSBComposite(hue,saturation,brightness,&composite.red,
2561 &composite.green,&composite.blue);
2564 case HueCompositeOp:
2566 if (source.alpha == TransparentAlpha)
2568 if (destination.alpha == TransparentAlpha)
2573 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2574 &saturation,&brightness);
2575 CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
2576 HSBComposite(hue,saturation,brightness,&composite.red,
2577 &composite.green,&composite.blue);
2578 if (source.alpha < destination.alpha)
2579 composite.alpha=source.alpha;
2582 case SaturateCompositeOp:
2584 if (source.alpha == TransparentAlpha)
2586 if (destination.alpha == TransparentAlpha)
2591 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2592 &saturation,&brightness);
2593 CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
2595 HSBComposite(hue,saturation,brightness,&composite.red,
2596 &composite.green,&composite.blue);
2597 if (source.alpha < destination.alpha)
2598 composite.alpha=source.alpha;
2601 case LuminizeCompositeOp:
2603 if (source.alpha == TransparentAlpha)
2605 if (destination.alpha == TransparentAlpha)
2610 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2611 &saturation,&brightness);
2612 CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
2614 HSBComposite(hue,saturation,brightness,&composite.red,
2615 &composite.green,&composite.blue);
2616 if (source.alpha < destination.alpha)
2617 composite.alpha=source.alpha;
2620 case ColorizeCompositeOp:
2622 if (source.alpha == TransparentAlpha)
2624 if (destination.alpha == TransparentAlpha)
2629 CompositeHSB(destination.red,destination.green,destination.blue,&sans,
2631 CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
2633 HSBComposite(hue,saturation,brightness,&composite.red,
2634 &composite.green,&composite.blue);
2635 if (source.alpha < destination.alpha)
2636 composite.alpha=source.alpha;
2639 case CopyRedCompositeOp:
2640 case CopyCyanCompositeOp:
2642 composite.red=source.red;
2645 case CopyGreenCompositeOp:
2646 case CopyMagentaCompositeOp:
2648 composite.green=source.green;
2651 case CopyBlueCompositeOp:
2652 case CopyYellowCompositeOp:
2654 composite.blue=source.blue;
2657 case CopyOpacityCompositeOp:
2659 if (source.matte == MagickFalse)
2661 composite.alpha=(MagickRealType) GetPixelInfoIntensity(&source);
2664 composite.alpha=source.alpha;
2667 case CopyBlackCompositeOp:
2669 if (source.colorspace != CMYKColorspace)
2670 ConvertRGBToCMYK(&source);
2671 composite.black=source.black;
2674 case BlurCompositeOp:
2675 case DisplaceCompositeOp:
2676 case DistortCompositeOp:
2684 if (image->colorspace == CMYKColorspace)
2686 composite.red=(MagickRealType) QuantumRange-composite.red;
2687 composite.green=(MagickRealType) QuantumRange-composite.green;
2688 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2689 composite.black=(MagickRealType) QuantumRange-composite.black;
2691 SetPixelRed(image,ClampToQuantum(composite.red),q);
2692 SetPixelGreen(image,ClampToQuantum(composite.green),q);
2693 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2694 if (image->colorspace == CMYKColorspace)
2695 SetPixelBlack(image,ClampToQuantum(composite.black),q);
2696 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2697 p+=GetPixelChannels(composite_image);
2698 if (p >= (pixels+composite_image->columns*GetPixelChannels(composite_image)))
2700 q+=GetPixelChannels(image);
2702 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2704 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2709 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2710 #pragma omp critical (MagickCore_CompositeImage)
2712 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2714 if (proceed == MagickFalse)
2718 composite_view=DestroyCacheView(composite_view);
2719 image_view=DestroyCacheView(image_view);
2720 if (destination_image != (Image * ) NULL)
2721 destination_image=DestroyImage(destination_image);
2726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2730 % T e x t u r e I m a g e %
2734 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2736 % TextureImage() repeatedly tiles the texture image across and down the image
2739 % The format of the TextureImage method is:
2741 % MagickBooleanType TextureImage(Image *image,const Image *texture)
2743 % A description of each parameter follows:
2745 % o image: the image.
2747 % o texture: This image is the texture to layer on the background.
2750 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
2752 #define TextureImageTag "Texture/Image"
2767 assert(image != (Image *) NULL);
2768 if (image->debug != MagickFalse)
2769 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2770 assert(image->signature == MagickSignature);
2771 if (texture == (const Image *) NULL)
2772 return(MagickFalse);
2773 (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
2774 exception=(&image->exception);
2775 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2776 return(MagickFalse);
2778 if ((image->compose != CopyCompositeOp) &&
2779 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2780 (texture->matte != MagickFalse)))
2783 Tile texture onto the image background.
2785 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2786 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2788 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
2793 if (status == MagickFalse)
2795 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2800 thread_status=CompositeImage(image,image->compose,texture,x+
2801 texture->tile_offset.x,y+texture->tile_offset.y);
2802 if (thread_status == MagickFalse)
2804 status=thread_status;
2808 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2813 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2814 #pragma omp critical (MagickCore_TextureImage)
2816 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2818 if (proceed == MagickFalse)
2822 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2823 image->rows,image->rows);
2827 Tile texture onto the image background (optimized).
2830 image_view=AcquireCacheView(image);
2831 texture_view=AcquireCacheView(texture);
2832 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2833 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2835 for (y=0; y < (ssize_t) image->rows; y++)
2840 register const Quantum
2853 if (status == MagickFalse)
2855 pixels=GetCacheViewVirtualPixels(texture_view,texture->tile_offset.x,(y+
2856 texture->tile_offset.y) % texture->rows,texture->columns,1,exception);
2857 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2859 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2864 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2870 width=texture->columns;
2871 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2872 width=image->columns-x;
2873 for (i=0; i < (ssize_t) width; i++)
2875 SetPixelRed(image,GetPixelRed(texture,p),q);
2876 SetPixelGreen(image,GetPixelGreen(texture,p),q);
2877 SetPixelBlue(image,GetPixelBlue(texture,p),q);
2878 SetPixelAlpha(image,GetPixelAlpha(texture,p),q);
2879 if ((image->colorspace == CMYKColorspace) &&
2880 (texture->colorspace == CMYKColorspace))
2881 SetPixelBlack(image,GetPixelBlack(texture,p),q);
2882 p+=GetPixelChannels(texture);
2883 q+=GetPixelChannels(image);
2886 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2887 if (sync == MagickFalse)
2889 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2894 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2895 #pragma omp critical (MagickCore_TextureImage)
2897 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2899 if (proceed == MagickFalse)
2903 texture_view=DestroyCacheView(texture_view);
2904 image_view=DestroyCacheView(image_view);