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-2012 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-private.h"
47 #include "MagickCore/cache-view.h"
48 #include "MagickCore/client.h"
49 #include "MagickCore/color.h"
50 #include "MagickCore/color-private.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/colorspace-private.h"
53 #include "MagickCore/composite.h"
54 #include "MagickCore/composite-private.h"
55 #include "MagickCore/constitute.h"
56 #include "MagickCore/draw.h"
57 #include "MagickCore/fx.h"
58 #include "MagickCore/gem.h"
59 #include "MagickCore/geometry.h"
60 #include "MagickCore/image.h"
61 #include "MagickCore/image-private.h"
62 #include "MagickCore/list.h"
63 #include "MagickCore/log.h"
64 #include "MagickCore/monitor.h"
65 #include "MagickCore/monitor-private.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/option.h"
68 #include "MagickCore/pixel-accessor.h"
69 #include "MagickCore/property.h"
70 #include "MagickCore/quantum.h"
71 #include "MagickCore/resample.h"
72 #include "MagickCore/resource_.h"
73 #include "MagickCore/string_.h"
74 #include "MagickCore/thread-private.h"
75 #include "MagickCore/utility.h"
76 #include "MagickCore/utility-private.h"
77 #include "MagickCore/version.h"
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84 % C o m p o s i t e I m a g e %
88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 % CompositeImage() returns the second image composited onto the first
91 % at the specified offset, using the specified composite method.
93 % The format of the CompositeImage method is:
95 % MagickBooleanType CompositeImage(Image *image,
96 % const CompositeOperator compose,Image *composite_image,
97 % const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
99 % A description of each parameter follows:
101 % o image: the destination image, modified by he composition
103 % o compose: This operator affects how the composite is applied to
104 % the image. The operators and how they are utilized are listed here
105 % http://www.w3.org/TR/SVG12/#compositing.
107 % o composite_image: the composite (source) image.
109 % o x_offset: the column offset of the composited image.
111 % o y_offset: the row offset of the composited image.
113 % Extra Controls from Image meta-data in 'composite_image' (artifacts)
116 % A string containing extra numerical arguments for specific compose
117 % methods, generally expressed as a 'geometry' or a comma separated list
120 % Compose methods needing such arguments include "BlendCompositeOp" and
121 % "DisplaceCompositeOp".
123 % o "compose:outside-overlay"
124 % Modify how the composition is to effect areas not directly covered
125 % by the 'composite_image' at the offset given. Normally this is
126 % dependant on the 'compose' method, especially Duff-Porter methods.
128 % If set to "false" then disable all normal handling of pixels not
129 % covered by the composite_image. Typically used for repeated tiling
130 % of the composite_image by the calling API.
132 % Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
134 % o exception: return any errors or warnings in this structure.
138 static inline double MagickMin(const double x,const double y)
144 static inline double MagickMax(const double x,const double y)
152 Programmers notes on SVG specification.
154 A Composition is defined by...
155 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
156 Blending areas : X = 1 for area of overlap ie: f(Sc,Dc)
157 Y = 1 for source preserved
158 Z = 1 for destination preserved
160 Conversion to transparency (then optimized)
161 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
162 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
165 Sca = Sc*Sa normalized Source color divided by Source alpha
166 Dca = Dc*Da normalized Dest color divided by Dest alpha
167 Dc' = Dca'/Da' the desired color value for this channel.
169 Da' (alpha result) is stored as 'gamma' in the functions.
171 The compose functions defined is just simplifications of the above
172 formula on a case by case bases.
176 The above SVG definitions also defines 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 Bumpmap: Multiply by overlay intensity
222 What is this Composition actually method for? Can't find any specification!
224 I think this was meant to be a 'HardLight effect' using a Shaded Image!
225 That is a Embossing, using a height map! Better to do it piecemeal.
227 static inline void CompositeBumpmap(const PixelInfo *p,const PixelInfo *q,
228 PixelInfo *composite)
233 intensity=(MagickRealType) GetPixelInfoIntensity(p);
234 composite->red=QuantumScale*intensity*q->red;
235 composite->green=QuantumScale*intensity*q->green;
236 composite->blue=QuantumScale*intensity*q->blue;
237 composite->alpha=(MagickRealType) QuantumScale*intensity*p->alpha;
238 if (q->colorspace == CMYKColorspace)
239 composite->black=QuantumScale*intensity*q->black;
242 static inline void CompositeClear(const PixelInfo *q,PixelInfo *composite)
244 composite->alpha=(MagickRealType) TransparentAlpha;
246 composite->green=0.0;
248 composite->black=1.0;
251 static MagickRealType ColorBurn(const MagickRealType Sca,
252 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
254 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
255 return(Sa*Da+Dca*(1.0-Sa));
256 if (Sca < MagickEpsilon)
257 return(Dca*(1.0-Sa));
258 return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
261 static inline void CompositeColorBurn(const PixelInfo *p,const PixelInfo *q,
262 PixelInfo *composite)
269 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
270 Da=QuantumScale*q->alpha;
271 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
272 composite->alpha=(MagickRealType) QuantumRange*gamma;
273 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
274 composite->red=gamma*ColorBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
276 composite->green=gamma*ColorBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
278 composite->blue=gamma*ColorBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
280 if (q->colorspace == CMYKColorspace)
281 composite->black=gamma*ColorBurn(QuantumScale*p->black*Sa,Sa,QuantumScale*
286 static MagickRealType ColorDodge(const MagickRealType Sca,
287 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
290 Working from first principles using the original formula:
294 This works correctly! Looks like the 2004 SVG model was right but just
295 required a extra condition for correct handling.
297 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
298 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
299 if (fabs(Sca-Sa) < MagickEpsilon)
300 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
301 return(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
304 static inline void CompositeColorDodge(const PixelInfo *p,const PixelInfo *q,
305 PixelInfo *composite)
312 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
313 Da=QuantumScale*q->alpha;
314 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
315 composite->alpha=(MagickRealType) QuantumRange*gamma;
316 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
317 composite->red=gamma*ColorDodge(QuantumScale*p->red*Sa,Sa,QuantumScale*
319 composite->green=gamma*ColorDodge(QuantumScale*p->green*Sa,Sa,QuantumScale*
321 composite->blue=gamma*ColorDodge(QuantumScale*p->blue*Sa,Sa,QuantumScale*
323 if (q->colorspace == CMYKColorspace)
324 composite->black=gamma*ColorDodge(QuantumScale*p->black*Sa,Sa,QuantumScale*
328 static inline MagickRealType Darken(const MagickRealType p,
329 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
332 return(MagickOver_(p,alpha,q,beta)); /* src-over */
333 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
336 static inline void CompositeDarken(const Image *image,const PixelInfo *p,
337 const PixelInfo *q,PixelInfo *composite)
343 Darken is equivalent to a 'Minimum' method OR a greyscale version of a
344 binary 'Or' OR the 'Intersection' of pixel sets.
346 if (image->channel_mask != DefaultChannels)
349 Handle channels as separate grayscale channels.
351 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
352 composite->red=MagickMin(p->red,q->red);
353 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
354 composite->green=MagickMin(p->green,q->green);
355 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
356 composite->blue=MagickMin(p->blue,q->blue);
357 if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 &&
358 (q->colorspace == CMYKColorspace))
359 composite->black=MagickMin(p->black,q->black);
360 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
361 composite->alpha=MagickMax(p->alpha,q->alpha);
364 composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */
365 gamma=1.0-QuantumScale*composite->alpha;
366 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
367 composite->red=gamma*Darken(p->red,p->alpha,q->red,q->alpha);
368 composite->green=gamma*Darken(p->green,p->alpha,q->green,q->alpha);
369 composite->blue=gamma*Darken(p->blue,p->alpha,q->blue,q->alpha);
370 if (q->colorspace == CMYKColorspace)
371 composite->black=gamma*Darken(p->black,p->alpha,q->black,q->alpha);
374 static inline void CompositeDarkenIntensity(const Image *image,
375 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
382 Select the pixel based on the intensity level.
383 If 'Sync' flag select whole pixel based on alpha weighted intensity.
384 Otherwise use intensity only, but restrict copy according to channel.
386 if (image->channel_mask != DefaultChannels)
391 from_p=GetPixelInfoIntensity(p) < GetPixelInfoIntensity(q) ? MagickTrue :
393 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
394 composite->red=from_p != MagickFalse ? p->red : q->red;
395 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
396 composite->green=from_p != MagickFalse ? p->green : q->green;
397 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
398 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
399 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
400 (q->colorspace == CMYKColorspace))
401 composite->black=from_p != MagickFalse ? p->black : q->black;
402 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
403 composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha;
406 Sa=QuantumScale*p->alpha;
407 Da=QuantumScale*q->alpha;
408 *composite=(Sa*GetPixelInfoIntensity(p) < Da*GetPixelInfoIntensity(q)) ?
412 static inline MagickRealType Difference(const MagickRealType p,
413 const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
416 Optimized by Multipling by QuantumRange (taken from gamma).
418 return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q));
421 static inline void CompositeDifference(const Image *image,const PixelInfo *p,
422 const PixelInfo *q,PixelInfo *composite)
429 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
430 Da=QuantumScale*q->alpha;
431 if (image->channel_mask != DefaultChannels)
434 Handle channels as separate grayscale channels.
436 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
437 composite->red=fabs((double) (p->red-q->red));
438 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
439 composite->green=fabs((double) (p->green-q->green));
440 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
441 composite->blue=fabs((double) (p->blue-q->blue));
442 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
443 (q->colorspace == CMYKColorspace))
444 composite->black=fabs((double) (p->black-q->black));
445 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
446 composite->alpha=fabs((double) (p->alpha-q->alpha));
449 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
450 composite->alpha=(MagickRealType) QuantumRange*gamma;
451 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
452 composite->red=gamma*Difference(p->red,Sa,q->red,Da);
453 composite->green=gamma*Difference(p->green,Sa,q->green,Da);
454 composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da);
455 if (q->colorspace == CMYKColorspace)
456 composite->black=gamma*Difference(p->black,Sa,q->black,Da);
459 static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
460 const MagickRealType Dca,const MagickRealType Da)
463 Divide Source by Destination
467 But with appropriate handling for special case of Dc == 0 specifically
468 so that f(Black,Black)=Black and f(non-Black,Black)=White.
469 It is however also important to correctly do 'over' alpha blending which
470 is why the formula becomes so complex.
472 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
473 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
474 if (fabs(Dca) < MagickEpsilon)
475 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
476 return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
479 static inline void CompositeDivide(const Image *image,const PixelInfo *p,
480 const PixelInfo *q,PixelInfo *composite)
487 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
488 Da=QuantumScale*q->alpha;
489 if (image->channel_mask != DefaultChannels)
492 Handle channels as separate grayscale channels.
494 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
495 composite->red=QuantumRange*Divide(QuantumScale*p->red,1.0,
496 QuantumScale*q->red,1.0);
497 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
498 composite->green=QuantumRange*Divide(QuantumScale*p->green,1.0,
499 QuantumScale*q->green,1.0);
500 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
501 composite->blue=QuantumRange*Divide(QuantumScale*p->blue,1.0,
502 QuantumScale*q->blue,1.0);
503 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
504 (q->colorspace == CMYKColorspace))
505 composite->black=QuantumRange*Divide(QuantumScale*p->black,1.0,
506 QuantumScale*q->black,1.0);
507 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
508 composite->alpha=QuantumRange*(1.0-Divide(Sa,1.0,Da,1.0));
511 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
512 composite->alpha=(MagickRealType) QuantumRange*gamma;
513 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
514 composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale*
516 composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale*
518 composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale*
520 if (q->colorspace == CMYKColorspace)
521 composite->black=gamma*Divide(QuantumScale*p->black*Sa,Sa,QuantumScale*
525 static MagickRealType Exclusion(const MagickRealType Sca,
526 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
528 return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
531 static inline void CompositeExclusion(const Image *image,const PixelInfo *p,
532 const PixelInfo *q,PixelInfo *composite)
539 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
540 Da=QuantumScale*q->alpha;
541 if (image->channel_mask != DefaultChannels)
544 Handle channels as separate grayscale channels.
546 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
547 composite->red=QuantumRange*Exclusion(QuantumScale*p->red,1.0,
548 QuantumScale*q->red,1.0);
549 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
550 composite->green=QuantumRange*Exclusion(QuantumScale*p->green,1.0,
551 QuantumScale*q->green,1.0);
552 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
553 composite->blue=QuantumRange*Exclusion(QuantumScale*p->blue,1.0,
554 QuantumScale*q->blue,1.0);
555 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
556 (q->colorspace == CMYKColorspace))
557 composite->black=QuantumRange*Exclusion(QuantumScale*p->black,1.0,
558 QuantumScale*q->black,1.0);
559 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
560 composite->alpha=QuantumRange*(1.0-Exclusion(Sa,1.0,Da,1.0));
563 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
564 composite->alpha=(MagickRealType) QuantumRange*gamma;
565 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
566 composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale*
568 composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale*
570 composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale*
572 if (q->colorspace == CMYKColorspace)
573 composite->black=gamma*Exclusion(QuantumScale*p->black*Sa,Sa,
574 QuantumScale*q->black*Da,Da);
577 static MagickRealType HardLight(const MagickRealType Sca,
578 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
581 return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
582 return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
585 static inline void CompositeHardLight(const PixelInfo *p,const PixelInfo *q,
586 PixelInfo *composite)
593 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
594 Da=QuantumScale*q->alpha;
595 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
596 composite->alpha=(MagickRealType) QuantumRange*gamma;
597 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
598 composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
600 composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
602 composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
604 if (q->colorspace == CMYKColorspace)
605 composite->black=gamma*HardLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
609 static void CompositeHSB(const MagickRealType red,const MagickRealType green,
610 const MagickRealType blue,double *hue,double *saturation,double *brightness)
618 Convert RGB to HSB colorspace.
620 assert(hue != (double *) NULL);
621 assert(saturation != (double *) NULL);
622 assert(brightness != (double *) NULL);
623 max=(red > green ? red : green);
626 min=(red < green ? red : green);
631 *brightness=(double) (QuantumScale*max);
632 if (fabs(max) < MagickEpsilon)
634 *saturation=(double) (1.0-min/max);
636 if (fabs(delta) < MagickEpsilon)
638 if (fabs(red-max) < MagickEpsilon)
639 *hue=(double) ((green-blue)/delta);
641 if (fabs(green-max) < MagickEpsilon)
642 *hue=(double) (2.0+(blue-red)/delta);
644 if (fabs(blue-max) < MagickEpsilon)
645 *hue=(double) (4.0+(red-green)/delta);
652 static inline MagickRealType In(const MagickRealType p,const MagickRealType Sa,
653 const MagickRealType magick_unused(q),const MagickRealType Da)
659 static inline void CompositeIn(const PixelInfo *p,const PixelInfo *q,
660 PixelInfo *composite)
668 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
669 Da=QuantumScale*q->alpha;
671 composite->alpha=(MagickRealType) QuantumRange*gamma;
672 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
673 /* really this just preserves the src or p color as is! */
674 composite->red=gamma*In(p->red,Sa,q->red,Da);
675 composite->green=gamma*In(p->green,Sa,q->green,Da);
676 composite->blue=gamma*In(p->blue,Sa,q->blue,Da);
677 if (q->colorspace == CMYKColorspace)
678 composite->black=gamma*In(p->black,Sa,q->black,Da);
680 /* Simplified to a multiply of the Alpha Channel */
681 *composite=*p; /* structure copy */
682 composite->alpha=QuantumScale*p->alpha*q->alpha;
686 static inline MagickRealType Lighten(const MagickRealType p,
687 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
690 return(MagickOver_(p,alpha,q,beta)); /* src-over */
691 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
694 static inline void CompositeLighten(const Image *image,const PixelInfo *p,
695 const PixelInfo *q,PixelInfo *composite)
701 Lighten is also equvalent to a 'Maximum' method OR a greyscale version of a
702 binary 'And' OR the 'Union' of pixel sets.
704 if (image->channel_mask != DefaultChannels)
707 Handle channels as separate grayscale channels
709 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
710 composite->red=MagickMax(p->red,q->red);
711 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
712 composite->green=MagickMax(p->green,q->green);
713 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
714 composite->blue=MagickMax(p->blue,q->blue);
715 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
716 (q->colorspace == CMYKColorspace))
717 composite->black=MagickMax(p->black,q->black);
718 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
719 composite->alpha=MagickMin(p->alpha,q->alpha);
722 composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */
723 gamma=1.0-QuantumScale*composite->alpha;
724 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
725 composite->red=gamma*Lighten(p->red,p->alpha,q->red,q->alpha);
726 composite->green=gamma*Lighten(p->green,p->alpha,q->green,q->alpha);
727 composite->blue=gamma*Lighten(p->blue,p->alpha,q->blue,q->alpha);
728 if (q->colorspace == CMYKColorspace)
729 composite->black=gamma*Lighten(p->black,p->alpha,q->black,q->alpha);
732 static inline void CompositeLightenIntensity(const Image *image,
733 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
740 Select the pixel based on the intensity level.
741 If 'Sync' flag select whole pixel based on alpha weighted intensity.
742 Otherwise use Intenisty only, but restrict copy according to channel.
744 if (image->channel_mask != DefaultChannels)
749 from_p=GetPixelInfoIntensity(p) > GetPixelInfoIntensity(q) ? MagickTrue :
751 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
752 composite->red=from_p != MagickFalse ? p->red : q->red;
753 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
754 composite->green=from_p != MagickFalse ? p->green : q->green;
755 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
756 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
757 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
758 (q->colorspace == CMYKColorspace))
759 composite->black=from_p != MagickFalse ? p->black : q->black;
760 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
761 composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha;
764 Sa=QuantumScale*p->alpha;
765 Da=QuantumScale*q->alpha;
766 *composite=(Sa*GetPixelInfoIntensity(p) > Da*GetPixelInfoIntensity(q)) ?
770 static inline void CompositeLinearDodge(const PixelInfo *p,const PixelInfo *q,
771 PixelInfo *composite)
778 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
779 Da=QuantumScale*q->alpha;
780 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
781 composite->alpha=(MagickRealType) QuantumRange*gamma;
782 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
783 composite->red=gamma*(p->red*Sa+q->red*Da);
784 composite->green=gamma*(p->green*Sa+q->green*Da);
785 composite->blue=gamma*(p->blue*Sa+q->blue*Da);
786 if (q->colorspace == CMYKColorspace)
787 composite->black=gamma*(p->black*Sa+q->black*Da);
791 static inline MagickRealType LinearBurn(const MagickRealType Sca,
792 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
795 LinearBurn: as defined by Abode Photoshop, according to
796 http://www.simplefilter.de/en/basics/mixmods.html is:
798 f(Sc,Dc) = Sc + Dc - 1
800 return(Sca+Dca-Sa*Da);
803 static inline void CompositeLinearBurn(const PixelInfo *p,const PixelInfo *q,
804 PixelInfo *composite)
811 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
812 Da=QuantumScale*q->alpha;
813 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
814 composite->alpha=(MagickRealType) QuantumRange*gamma;
815 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
816 composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
818 composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
820 composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
822 if (q->colorspace == CMYKColorspace)
823 composite->black=gamma*LinearBurn(QuantumScale*p->black*Sa,Sa,QuantumScale*
827 static inline MagickRealType LinearLight(const MagickRealType Sca,
828 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
831 LinearLight: as defined by Abode Photoshop, according to
832 http://www.simplefilter.de/en/basics/mixmods.html is:
834 f(Sc,Dc) = Dc + 2*Sc - 1
836 return((Sca-Sa)*Da+Sca+Dca);
839 static inline void CompositeLinearLight(const PixelInfo *p,const PixelInfo *q,
840 PixelInfo *composite)
847 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
848 Da=QuantumScale*q->alpha;
849 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
850 composite->alpha=(MagickRealType) QuantumRange*gamma;
851 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
852 composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
854 composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
856 composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
858 if (q->colorspace == CMYKColorspace)
859 composite->black=gamma*LinearLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
863 static inline MagickRealType Mathematics(const MagickRealType Sca,
864 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
865 const GeometryInfo *geometry_info)
871 'Mathematics' a free form user control mathematical composition is defined
874 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
876 Where the arguments A,B,C,D are (currently) passed to composite as
877 a command separated 'geometry' string in "compose:args" image artifact.
879 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
881 Applying the SVG transparency formula (see above), we get...
883 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
885 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
888 gamma=geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
889 geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
894 static inline void CompositeMathematics(const Image *image,const PixelInfo *p,
895 const PixelInfo *q,const GeometryInfo *args,PixelInfo *composite)
902 Sa=QuantumScale*p->alpha; /* ??? - AT */
903 Da=QuantumScale*q->alpha;
904 if (image->channel_mask != DefaultChannels)
907 Handle channels as separate grayscale channels.
909 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
910 composite->red=QuantumRange*Mathematics(QuantumScale*p->red,1.0,
911 QuantumScale*q->red,1.0,args);
912 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
913 composite->green=QuantumRange*Mathematics(QuantumScale*p->green,1.0,
914 QuantumScale*q->green,1.0,args);
915 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
916 composite->blue=QuantumRange*Mathematics(QuantumScale*p->blue,1.0,
917 QuantumScale*q->blue,1.0,args);
918 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
919 (q->colorspace == CMYKColorspace))
920 composite->black=QuantumRange*Mathematics(QuantumScale*p->black,1.0,
921 QuantumScale*q->black,1.0,args);
922 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
923 composite->alpha=QuantumRange*(1.0-Mathematics(Sa,1.0,Da,1.0,args));
926 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
927 composite->alpha=(MagickRealType) QuantumRange*gamma;
928 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
929 composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
931 composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,
932 QuantumScale*q->green*Da,Da,args);
933 composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
935 if (q->colorspace == CMYKColorspace)
936 composite->black=gamma*Mathematics(QuantumScale*p->black*Sa,Sa,
937 QuantumScale*q->black*Da,Da,args);
940 static inline void CompositePlus(const Image *image,const PixelInfo *p,
941 const PixelInfo *q,PixelInfo *composite)
944 NOTE: "Plus" does not use 'over' alpha-blending but uses a special
945 'plus' form of alph-blending. It is the ONLY mathematical operator to
946 do this. this is what makes it different to the otherwise equivalent
947 "LinearDodge" composition method.
949 Note however that color channels are still effected by the alpha channel
950 as a result of the blending, making it just as useless for independant
951 channel maths, just like all other mathematical composition methods.
953 As such the removal of the 'sync' flag, is still a usful convention.
955 The CompositePixelInfoPlus() function is defined in
956 "composite-private.h" so it can also be used for Image Blending.
958 if (image->channel_mask != DefaultChannels)
961 Handle channels as separate grayscale channels.
963 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
964 composite->red=p->red+q->red;
965 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
966 composite->green=p->green+q->green;
967 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
968 composite->blue=p->blue+q->blue;
969 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
970 (q->colorspace == CMYKColorspace))
971 composite->black=p->black+q->black;
972 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
973 composite->alpha=p->alpha+q->alpha-QuantumRange;
976 CompositePixelInfoPlus(p,p->alpha,q,q->alpha,composite);
979 static inline MagickRealType Minus(const MagickRealType Sca,
980 const MagickRealType Sa,const MagickRealType Dca,
981 const MagickRealType magick_unused(Da))
984 Minus Source from Destination
988 return(Sca+Dca-2.0*Dca*Sa);
991 static inline void CompositeMinus(const Image *image,const PixelInfo *p,
992 const PixelInfo *q,PixelInfo *composite)
999 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1000 Da=QuantumScale*q->alpha;
1001 if (image->channel_mask != DefaultChannels)
1004 Handle channels as separate grayscale channels.
1006 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1007 composite->red=p->red-q->red;
1008 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1009 composite->green=p->green-q->green;
1010 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1011 composite->blue=p->blue-q->blue;
1012 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1013 (q->colorspace == CMYKColorspace))
1014 composite->black=p->black-q->black;
1015 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1016 composite->alpha=QuantumRange*(1.0-(Sa-Da));
1019 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1020 composite->alpha=(MagickRealType) QuantumRange*gamma;
1021 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1022 composite->red=gamma*Minus(p->red*Sa,Sa,q->red*Da,Da);
1023 composite->green=gamma*Minus(p->green*Sa,Sa,q->green*Da,Da);
1024 composite->blue=gamma*Minus(p->blue*Sa,Sa,q->blue*Da,Da);
1025 if (q->colorspace == CMYKColorspace)
1026 composite->black=gamma*Minus(p->black*Sa,Sa,q->black*Da,Da);
1029 static inline MagickRealType ModulusAdd(const MagickRealType p,
1030 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1036 if (pixel > QuantumRange)
1037 pixel-=(QuantumRange+1.0);
1038 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1041 static inline void CompositeModulusAdd(const Image *image,const PixelInfo *p,
1042 const PixelInfo *q,PixelInfo *composite)
1049 if (image->channel_mask != DefaultChannels)
1052 Handle channels as separate grayscale channels.
1054 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1055 composite->red=ModulusAdd(p->red,1.0,q->red,1.0);
1056 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1057 composite->green=ModulusAdd(p->green,1.0,q->green,1.0);
1058 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1059 composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0);
1060 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1061 (q->colorspace == CMYKColorspace))
1062 composite->black=ModulusAdd(p->black,1.0,q->black,1.0);
1063 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1064 composite->alpha=ModulusAdd(p->alpha,1.0,q->alpha,1.0);
1067 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1068 Da=QuantumScale*q->alpha;
1069 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1070 composite->alpha=(MagickRealType) QuantumRange*gamma;
1071 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1072 composite->red=ModulusAdd(p->red,Sa,q->red,Da);
1073 composite->green=ModulusAdd(p->green,Sa,q->green,Da);
1074 composite->blue=ModulusAdd(p->blue,Sa,q->blue,Da);
1075 if (q->colorspace == CMYKColorspace)
1076 composite->black=ModulusAdd(p->black,Sa,q->black,Da);
1079 static inline MagickRealType ModulusSubtract(const MagickRealType p,
1080 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1087 pixel+=(QuantumRange+1.0);
1088 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1091 static inline void CompositeModulusSubtract(const Image *image,
1092 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
1099 if (image->channel_mask != DefaultChannels)
1102 Handle channels as separate grayscale channels,
1104 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1105 composite->red=ModulusSubtract(p->red,1.0,q->red,1.0);
1106 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1107 composite->green=ModulusSubtract(p->green,1.0,q->green,1.0);
1108 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1109 composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0);
1110 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1111 (q->colorspace == CMYKColorspace))
1112 composite->black=ModulusSubtract(p->black,1.0,q->black,1.0);
1113 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1114 composite->alpha=ModulusSubtract(p->alpha,1.0,q->alpha,1.0);
1117 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1118 Da=QuantumScale*q->alpha;
1119 gamma = RoundToUnity(Sa+Da-Sa*Da);
1120 composite->alpha=(MagickRealType) QuantumRange*gamma;
1121 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1122 composite->red=ModulusSubtract(p->red,Sa,q->red,Da);
1123 composite->green=ModulusSubtract(p->green,Sa,q->green,Da);
1124 composite->blue=ModulusSubtract(p->blue,Sa,q->blue,Da);
1125 if (q->colorspace == CMYKColorspace)
1126 composite->black=ModulusSubtract(p->black,Sa,q->black,Da);
1129 static inline MagickRealType Multiply(const MagickRealType Sca,
1130 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1132 return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1135 static inline void CompositeMultiply(const Image *image,const PixelInfo *p,
1136 const PixelInfo *q,PixelInfo *composite)
1143 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1144 Da=QuantumScale*q->alpha;
1145 if (image->channel_mask != DefaultChannels)
1148 Handle channels as separate grayscale channels.
1150 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1151 composite->red=QuantumScale*p->red*q->red;
1152 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1153 composite->green=QuantumScale*p->green*q->green;
1154 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1155 composite->blue=QuantumScale*p->blue*q->blue;
1156 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1157 (q->colorspace == CMYKColorspace))
1158 composite->black=QuantumScale*p->black*q->black;
1159 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1160 composite->alpha=QuantumRange*(1.0-Sa*Da);
1163 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1164 composite->alpha=(MagickRealType) QuantumRange*gamma;
1165 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1166 composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
1168 composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
1170 composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1172 if (q->colorspace == CMYKColorspace)
1173 composite->black=gamma*Multiply(QuantumScale*p->black*Sa,Sa,
1174 QuantumScale*q->black*Da,Da);
1178 static inline MagickRealType Out(const MagickRealType p,const MagickRealType Sa,
1179 const MagickRealType magick_unused(q),const MagickRealType Da)
1181 return(Sa*p*(1.0-Da));
1185 static inline void CompositeOut(const PixelInfo *p,const PixelInfo *q,
1186 PixelInfo *composite)
1194 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1195 Da=QuantumScale*q->alpha;
1197 composite->alpha=(MagickRealType) QuantumRange*gamma;
1198 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1199 composite->red=gamma*Out(p->red,Sa,q->red,Da);
1200 composite->green=gamma*Out(p->green,Sa,q->green,Da);
1201 composite->blue=gamma*Out(p->blue,Sa,q->blue,Da);
1202 if (q->colorspace == CMYKColorspace)
1203 composite->black=gamma*Out(p->black,Sa,q->black,Da);
1205 /* Simplified to a negated multiply of the Alpha Channel */
1206 *composite=*p; /* structure copy */
1207 composite->alpha=p->alpha*(1.0-QuantumScale*q->alpha);
1211 static MagickRealType PegtopLight(const MagickRealType Sca,
1212 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1215 PegTop: A Soft-Light alternative: A continuous version of the Softlight
1216 function, producing very similar results.
1218 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
1220 See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
1222 if (fabs(Da) < MagickEpsilon)
1224 return(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+Dca*(1.0-Sa));
1227 static inline void CompositePegtopLight(const PixelInfo *p,const PixelInfo *q,
1228 PixelInfo *composite)
1235 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1236 Da=QuantumScale*q->alpha;
1237 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1238 composite->alpha=(MagickRealType) QuantumRange*gamma;
1239 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1240 composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1242 composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1244 composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1246 if (q->colorspace == CMYKColorspace)
1247 composite->black=gamma*PegtopLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1251 static MagickRealType PinLight(const MagickRealType Sca,const MagickRealType Sa,
1252 const MagickRealType Dca,const MagickRealType Da)
1255 PinLight: A Photoshop 7 composition method
1256 http://www.simplefilter.de/en/basics/mixmods.html
1258 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
1260 if (Dca*Sa < Da*(2.0*Sca-Sa))
1261 return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
1262 if ((Dca*Sa) > (2.0*Sca*Da))
1263 return(Sca*Da+Sca+Dca*(1.0-Sa));
1264 return(Sca*(1.0-Da)+Dca);
1267 static inline void CompositePinLight(const PixelInfo *p,const PixelInfo *q,
1268 PixelInfo *composite)
1275 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1276 Da=QuantumScale*q->alpha;
1277 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1278 composite->alpha=(MagickRealType) QuantumRange*gamma;
1279 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1280 composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1282 composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1284 composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1286 if (q->colorspace == CMYKColorspace)
1287 composite->black=gamma*PinLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1291 static inline MagickRealType Screen(const MagickRealType Sca,
1292 const MagickRealType Dca)
1295 Screen: A negated multiply
1296 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
1298 return(Sca+Dca-Sca*Dca);
1301 static inline void CompositeScreen(const Image *image,const PixelInfo *p,
1302 const PixelInfo *q,PixelInfo *composite)
1309 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1310 Da=QuantumScale*q->alpha;
1311 if (image->channel_mask != DefaultChannels)
1314 Handle channels as separate grayscale channels.
1316 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1317 composite->red=QuantumRange*Screen(QuantumScale*p->red,
1318 QuantumScale*q->red);
1319 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1320 composite->green=QuantumRange*Screen(QuantumScale*p->green,
1321 QuantumScale*q->green);
1322 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1323 composite->blue=QuantumRange*Screen(QuantumScale*p->blue,
1324 QuantumScale*q->blue);
1325 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1326 (q->colorspace == CMYKColorspace))
1327 composite->black=QuantumRange*Screen(QuantumScale*p->black,
1328 QuantumScale*q->black);
1329 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1330 composite->alpha=QuantumRange*(1.0-Screen(Sa,Da));
1333 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1334 composite->alpha=(MagickRealType) QuantumRange*gamma;
1335 Sa*=QuantumScale; Da*=QuantumScale; /* optimization */
1336 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1337 composite->red=gamma*Screen(p->red*Sa,q->red*Da);
1338 composite->green=gamma*Screen(p->green*Sa,q->green*Da);
1339 composite->blue=gamma*Screen(p->blue*Sa,q->blue*Da);
1340 if (q->colorspace == CMYKColorspace)
1341 composite->black=gamma*Screen(p->black*Sa,q->black*Da);
1344 static MagickRealType SoftLight(const MagickRealType Sca,
1345 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1352 New specification: March 2009 SVG specification.
1356 return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1357 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1359 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
1360 alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1363 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1367 static inline void CompositeSoftLight(const PixelInfo *p,const PixelInfo *q,
1368 PixelInfo *composite)
1375 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1376 Da=QuantumScale*q->alpha;
1377 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1378 composite->alpha=(MagickRealType) QuantumRange*gamma;
1379 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1380 composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1382 composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1384 composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1386 if (q->colorspace == CMYKColorspace)
1387 composite->black=gamma*SoftLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1391 static inline MagickRealType Threshold(const MagickRealType p,
1392 const MagickRealType q,const MagickRealType threshold,
1393 const MagickRealType amount)
1399 Multiply difference by amount, if differance larger than threshold???
1400 What use this is is completely unknown. The Opacity calculation appears to
1401 be inverted -- Anthony Thyssen
1406 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
1408 return(q+delta*amount);
1411 static inline void CompositeThreshold(const PixelInfo *p,const PixelInfo *q,
1412 const MagickRealType threshold,const MagickRealType amount,
1413 PixelInfo *composite)
1415 composite->red=Threshold(p->red,q->red,threshold,amount);
1416 composite->green=Threshold(p->green,q->green,threshold,amount);
1417 composite->blue=Threshold(p->blue,q->blue,threshold,amount);
1418 composite->alpha=Threshold(p->alpha,q->alpha,threshold,amount);
1419 if (q->colorspace == CMYKColorspace)
1420 composite->black=Threshold(p->black,q->black,threshold,amount);
1424 static MagickRealType VividLight(const MagickRealType Sca,
1425 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1428 VividLight: A Photoshop 7 composition method. See
1429 http://www.simplefilter.de/en/basics/mixmods.html.
1431 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
1433 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
1434 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1435 if ((2.0*Sca) <= Sa)
1436 return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1437 return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1440 static inline void CompositeVividLight(const PixelInfo *p,const PixelInfo *q,
1441 PixelInfo *composite)
1448 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1449 Da=QuantumScale*q->alpha;
1450 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1451 composite->alpha=(MagickRealType) QuantumRange*gamma;
1452 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1453 composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1455 composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1457 composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1459 if (q->colorspace == CMYKColorspace)
1460 composite->black=gamma*VividLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1464 static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
1465 const MagickRealType Dca,const MagickRealType Da)
1467 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
1470 static inline void CompositeXor(const PixelInfo *p,const PixelInfo *q,
1471 PixelInfo *composite)
1478 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1479 Da=QuantumScale*q->alpha;
1480 gamma=Sa+Da-2.0*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
1481 composite->alpha=(MagickRealType) QuantumRange*gamma;
1482 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1483 composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
1484 composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
1485 composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
1486 if (q->colorspace == CMYKColorspace)
1487 composite->black=gamma*Xor(p->black*Sa,Sa,q->black*Da,Da);
1490 static void HSBComposite(const double hue,const double saturation,
1491 const double brightness,double *red,double *green,
1502 Convert HSB to RGB colorspace.
1504 assert(red != (double *) NULL);
1505 assert(green != (double *) NULL);
1506 assert(blue != (double *) NULL);
1507 if (saturation == 0.0)
1509 *red=(double) QuantumRange*brightness;
1514 h=6.0*(hue-floor(hue));
1515 f=h-floor((double) h);
1516 p=brightness*(1.0-saturation);
1517 q=brightness*(1.0-saturation*f);
1518 t=brightness*(1.0-saturation*(1.0-f));
1524 *red=(double) QuantumRange*brightness;
1525 *green=(double) QuantumRange*t;
1526 *blue=(double) QuantumRange*p;
1531 *red=(double) QuantumRange*q;
1532 *green=(double) QuantumRange*brightness;
1533 *blue=(double) QuantumRange*p;
1538 *red=(double) QuantumRange*p;
1539 *green=(double) QuantumRange*brightness;
1540 *blue=(double) QuantumRange*t;
1545 *red=(double) QuantumRange*p;
1546 *green=(double) QuantumRange*q;
1547 *blue=(double) QuantumRange*brightness;
1552 *red=(double) QuantumRange*t;
1553 *green=(double) QuantumRange*p;
1554 *blue=(double) QuantumRange*brightness;
1559 *red=(double) QuantumRange*brightness;
1560 *green=(double) QuantumRange*p;
1561 *blue=(double) QuantumRange*q;
1567 MagickExport MagickBooleanType CompositeImage(Image *image,
1568 const CompositeOperator compose,const Image *composite_image,
1569 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
1571 #define CompositeImageTag "Composite/Image"
1590 modify_outside_overlay,
1598 destination_dissolve,
1612 Prepare composite image.
1614 assert(image != (Image *) NULL);
1615 assert(image->signature == MagickSignature);
1616 if (image->debug != MagickFalse)
1617 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1618 assert(composite_image != (Image *) NULL);
1619 assert(composite_image->signature == MagickSignature);
1620 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1621 return(MagickFalse);
1622 destination_image=(Image *) NULL;
1624 destination_dissolve=1.0;
1625 modify_outside_overlay=MagickFalse;
1626 percent_brightness=100.0;
1627 percent_saturation=100.0;
1628 source_dissolve=1.0;
1632 case ClearCompositeOp:
1633 case DstAtopCompositeOp:
1634 case DstInCompositeOp:
1636 case OutCompositeOp:
1637 case SrcCompositeOp:
1638 case SrcInCompositeOp:
1639 case SrcOutCompositeOp:
1642 Modify destination outside the overlaid region.
1644 modify_outside_overlay=MagickTrue;
1647 case CopyCompositeOp:
1649 if ((x_offset < 0) || (y_offset < 0))
1651 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
1653 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
1656 image_view=AcquireCacheView(image);
1657 composite_view=AcquireCacheView(composite_image);
1658 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1659 #pragma omp parallel for schedule(dynamic,4) shared(status)
1661 for (y=0; y < (ssize_t) composite_image->rows; y++)
1666 register const Quantum
1675 if (status == MagickFalse)
1677 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1679 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1680 composite_image->columns,1,exception);
1681 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1686 for (x=0; x < (ssize_t) composite_image->columns; x++)
1688 SetPixelRed(image,GetPixelRed(composite_image,p),q);
1689 SetPixelGreen(image,GetPixelGreen(composite_image,p),q);
1690 SetPixelBlue(image,GetPixelBlue(composite_image,p),q);
1691 SetPixelAlpha(image,GetPixelAlpha(composite_image,p),q);
1692 if (image->colorspace == CMYKColorspace)
1693 SetPixelBlack(image,GetPixelBlack(composite_image,p),q);
1694 p+=GetPixelChannels(composite_image);
1695 q+=GetPixelChannels(image);
1697 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1698 if (sync == MagickFalse)
1700 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1705 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1706 #pragma omp critical (MagickCore_CompositeImage)
1708 proceed=SetImageProgress(image,CompositeImageTag,
1709 (MagickOffsetType) y,image->rows);
1710 if (proceed == MagickFalse)
1714 composite_view=DestroyCacheView(composite_view);
1715 image_view=DestroyCacheView(image_view);
1718 case CopyOpacityCompositeOp:
1719 case ChangeMaskCompositeOp:
1722 Modify destination outside the overlaid region and require an alpha
1723 channel to exist, to add transparency.
1725 if (image->matte == MagickFalse)
1726 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1727 modify_outside_overlay=MagickTrue;
1730 case BlurCompositeOp:
1752 Blur Image dictated by an overlay gradient map: X = red_channel;
1753 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
1755 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1757 if (destination_image == (Image *) NULL)
1758 return(MagickFalse);
1760 Determine the horizontal and vertical maximim blur.
1762 SetGeometryInfo(&geometry_info);
1764 value=GetImageArtifact(composite_image,"compose:args");
1765 if (value != (char *) NULL)
1766 flags=ParseGeometry(value,&geometry_info);
1767 if ((flags & WidthValue) == 0 )
1769 destination_image=DestroyImage(destination_image);
1770 return(MagickFalse);
1772 width=geometry_info.rho;
1773 height=geometry_info.sigma;
1774 blur.x1=geometry_info.rho;
1777 blur.y2=geometry_info.sigma;
1780 if ((flags & HeightValue) == 0)
1782 if ((flags & XValue) != 0 )
1787 angle=DegreesToRadians(geometry_info.xi);
1788 blur.x1=width*cos(angle);
1789 blur.x2=width*sin(angle);
1790 blur.y1=(-height*sin(angle));
1791 blur.y2=height*cos(angle);
1793 if ((flags & YValue) != 0 )
1795 angle_start=DegreesToRadians(geometry_info.xi);
1796 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1799 Blur Image by resampling.
1801 resample_filter=AcquireResampleFilter(image,exception);
1802 SetResampleFilter(resample_filter,CubicFilter,2.0);
1803 destination_view=AcquireCacheView(destination_image);
1804 composite_view=AcquireCacheView(composite_image);
1805 for (y=0; y < (ssize_t) composite_image->rows; y++)
1810 register const Quantum
1819 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1821 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1823 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1824 destination_image->columns,1,exception);
1825 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1827 for (x=0; x < (ssize_t) composite_image->columns; x++)
1829 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1831 p+=GetPixelChannels(composite_image);
1834 if (fabs(angle_range) > MagickEpsilon)
1839 angle=angle_start+angle_range*QuantumScale*
1840 GetPixelBlue(composite_image,p);
1841 blur.x1=width*cos(angle);
1842 blur.x2=width*sin(angle);
1843 blur.y1=(-height*sin(angle));
1844 blur.y2=height*cos(angle);
1846 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
1847 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
1848 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
1849 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
1850 GetPixelGreen(composite_image,p));
1851 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1852 (double) y_offset+y,&pixel);
1853 SetPixelInfoPixel(destination_image,&pixel,q);
1854 p+=GetPixelChannels(composite_image);
1855 q+=GetPixelChannels(destination_image);
1857 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1858 if (sync == MagickFalse)
1861 resample_filter=DestroyResampleFilter(resample_filter);
1862 composite_view=DestroyCacheView(composite_view);
1863 destination_view=DestroyCacheView(destination_view);
1864 composite_image=destination_image;
1867 case DisplaceCompositeOp:
1868 case DistortCompositeOp:
1887 Displace/Distort based on overlay gradient map:
1888 X = red_channel; Y = green_channel;
1889 compose:args = x_scale[,y_scale[,center.x,center.y]]
1891 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1893 if (destination_image == (Image *) NULL)
1894 return(MagickFalse);
1895 SetGeometryInfo(&geometry_info);
1897 value=GetImageArtifact(composite_image,"compose:args");
1898 if (value != (char *) NULL)
1899 flags=ParseGeometry(value,&geometry_info);
1900 if ((flags & (WidthValue|HeightValue)) == 0 )
1902 if ((flags & AspectValue) == 0)
1904 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1906 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1910 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1911 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1916 horizontal_scale=geometry_info.rho;
1917 vertical_scale=geometry_info.sigma;
1918 if ((flags & PercentValue) != 0)
1920 if ((flags & AspectValue) == 0)
1922 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1923 vertical_scale*=(composite_image->rows-1.0)/200.0;
1927 horizontal_scale*=(image->columns-1.0)/200.0;
1928 vertical_scale*=(image->rows-1.0)/200.0;
1931 if ((flags & HeightValue) == 0)
1932 vertical_scale=horizontal_scale;
1935 Determine fixed center point for absolute distortion map
1937 Displace offset relative to a fixed absolute point
1938 Select that point according to +X+Y user inputs.
1939 default = center of overlay image
1940 arg flag '!' = locations/percentage relative to background image
1942 center.x=(MagickRealType) x_offset;
1943 center.y=(MagickRealType) y_offset;
1944 if (compose == DistortCompositeOp)
1946 if ((flags & XValue) == 0)
1947 if ((flags & AspectValue) == 0)
1948 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
1951 center.x=((MagickRealType) image->columns-1)/2.0;
1953 if ((flags & AspectValue) == 0)
1954 center.x=(MagickRealType) x_offset+geometry_info.xi;
1956 center.x=geometry_info.xi;
1957 if ((flags & YValue) == 0)
1958 if ((flags & AspectValue) == 0)
1959 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
1961 center.y=((MagickRealType) image->rows-1)/2.0;
1963 if ((flags & AspectValue) == 0)
1964 center.y=(MagickRealType) y_offset+geometry_info.psi;
1966 center.y=geometry_info.psi;
1969 Shift the pixel offset point as defined by the provided,
1970 displacement/distortion map. -- Like a lens...
1972 GetPixelInfo(image,&pixel);
1973 image_view=AcquireCacheView(image);
1974 destination_view=AcquireCacheView(destination_image);
1975 composite_view=AcquireCacheView(composite_image);
1976 for (y=0; y < (ssize_t) composite_image->rows; y++)
1981 register const Quantum
1990 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1992 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1994 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1995 destination_image->columns,1,exception);
1996 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1998 for (x=0; x < (ssize_t) composite_image->columns; x++)
2000 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
2002 p+=GetPixelChannels(composite_image);
2006 Displace the offset.
2008 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
2009 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2010 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
2012 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
2013 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2014 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
2016 (void) InterpolatePixelInfo(image,image_view,
2017 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2020 Mask with the 'invalid pixel mask' in alpha channel.
2022 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
2023 pixel.alpha)*(1.0-QuantumScale*
2024 GetPixelAlpha(composite_image,p)));
2025 SetPixelInfoPixel(destination_image,&pixel,q);
2026 p+=GetPixelChannels(composite_image);
2027 q+=GetPixelChannels(destination_image);
2029 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
2030 if (sync == MagickFalse)
2033 destination_view=DestroyCacheView(destination_view);
2034 composite_view=DestroyCacheView(composite_view);
2035 image_view=DestroyCacheView(image_view);
2036 composite_image=destination_image;
2039 case DissolveCompositeOp:
2042 Geometry arguments to dissolve factors.
2044 value=GetImageArtifact(composite_image,"compose:args");
2045 if (value != (char *) NULL)
2047 flags=ParseGeometry(value,&geometry_info);
2048 source_dissolve=geometry_info.rho/100.0;
2049 destination_dissolve=1.0;
2050 if ((source_dissolve-MagickEpsilon) < 0.0)
2051 source_dissolve=0.0;
2052 if ((source_dissolve+MagickEpsilon) > 1.0)
2054 destination_dissolve=2.0-source_dissolve;
2055 source_dissolve=1.0;
2057 if ((flags & SigmaValue) != 0)
2058 destination_dissolve=geometry_info.sigma/100.0;
2059 if ((destination_dissolve-MagickEpsilon) < 0.0)
2060 destination_dissolve=0.0;
2061 modify_outside_overlay=MagickTrue;
2062 if ((destination_dissolve+MagickEpsilon) > 1.0 )
2064 destination_dissolve=1.0;
2065 modify_outside_overlay=MagickFalse;
2070 case BlendCompositeOp:
2072 value=GetImageArtifact(composite_image,"compose:args");
2073 if (value != (char *) NULL)
2075 flags=ParseGeometry(value,&geometry_info);
2076 source_dissolve=geometry_info.rho/100.0;
2077 destination_dissolve=1.0-source_dissolve;
2078 if ((flags & SigmaValue) != 0)
2079 destination_dissolve=geometry_info.sigma/100.0;
2080 modify_outside_overlay=MagickTrue;
2081 if ((destination_dissolve+MagickEpsilon) > 1.0)
2082 modify_outside_overlay=MagickFalse;
2086 case MathematicsCompositeOp:
2089 Just collect the values from "compose:args", setting.
2090 Unused values are set to zero automagically.
2092 Arguments are normally a comma separated list, so this probably should
2093 be changed to some 'general comma list' parser, (with a minimum
2096 SetGeometryInfo(&geometry_info);
2097 value=GetImageArtifact(composite_image,"compose:args");
2098 if (value != (char *) NULL)
2099 (void) ParseGeometry(value,&geometry_info);
2102 case ModulateCompositeOp:
2105 Determine the brightness and saturation scale.
2107 value=GetImageArtifact(composite_image,"compose:args");
2108 if (value != (char *) NULL)
2110 flags=ParseGeometry(value,&geometry_info);
2111 percent_brightness=geometry_info.rho;
2112 if ((flags & SigmaValue) != 0)
2113 percent_saturation=geometry_info.sigma;
2117 case ThresholdCompositeOp:
2120 Determine the amount and threshold.
2122 value=GetImageArtifact(composite_image,"compose:args");
2123 if (value != (char *) NULL)
2125 flags=ParseGeometry(value,&geometry_info);
2126 amount=geometry_info.rho;
2127 threshold=geometry_info.sigma;
2128 if ((flags & SigmaValue) == 0)
2131 threshold*=QuantumRange;
2137 value=GetImageArtifact(composite_image,"compose:outside-overlay");
2138 if (value != (const char *) NULL)
2139 modify_outside_overlay=IsMagickTrue(value);
2145 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2146 image_view=AcquireCacheView(image);
2147 composite_view=AcquireCacheView(composite_image);
2148 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2149 // #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2151 for (y=0; y < (ssize_t) image->rows; y++)
2161 register const Quantum
2170 MagickBooleanType composite_channels;
2172 if (status == MagickFalse)
2174 if (modify_outside_overlay == MagickFalse)
2178 if ((y-y_offset) >= (ssize_t) composite_image->rows)
2182 If pixels is NULL, y is outside overlay region.
2184 pixels=(Quantum *) NULL;
2186 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
2188 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
2189 composite_image->columns,1,exception);
2190 if (p == (const Quantum *) NULL)
2197 p-=x_offset*GetPixelChannels(composite_image);
2199 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2200 if (q == (Quantum *) NULL)
2208 for (x=0; x < (ssize_t) image->columns; x++)
2210 if (modify_outside_overlay == MagickFalse)
2214 q+=GetPixelChannels(image);
2217 if ((x-x_offset) >= (ssize_t) composite_image->columns)
2220 composite_channels=MagickFalse;
2223 case AtopCompositeOp:
2224 case ClearCompositeOp:
2225 case CopyCompositeOp:
2226 case DarkenCompositeOp:
2227 case DarkenIntensityCompositeOp:
2228 case DifferenceCompositeOp:
2229 case DivideDstCompositeOp:
2230 case DivideSrcCompositeOp:
2231 case DstAtopCompositeOp:
2232 case DstCompositeOp:
2233 case DstInCompositeOp:
2234 case DstOverCompositeOp:
2235 case DstOutCompositeOp:
2236 case ExclusionCompositeOp:
2238 case LightenCompositeOp:
2239 case LightenIntensityCompositeOp:
2240 case MinusDstCompositeOp:
2241 case MinusSrcCompositeOp:
2242 case ModulusAddCompositeOp:
2243 case ModulusSubtractCompositeOp:
2244 case MultiplyCompositeOp:
2246 case OutCompositeOp:
2247 case OverCompositeOp:
2248 case PlusCompositeOp:
2249 case ReplaceCompositeOp:
2250 case ScreenCompositeOp:
2251 case SrcAtopCompositeOp:
2252 case SrcCompositeOp:
2253 case SrcInCompositeOp:
2254 case SrcOutCompositeOp:
2255 case SrcOverCompositeOp:
2256 case XorCompositeOp:
2258 composite_channels=MagickTrue;
2264 if (composite_channels != MagickFalse) {
2276 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2277 ((x-x_offset) >= (ssize_t) composite_image->columns))
2280 source[MaxPixelChannels];
2285 Dc: destination color.
2287 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
2289 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2300 channel=GetPixelChannelMapChannel(image,i);
2301 traits=GetPixelChannelMapTraits(image,channel);
2302 if (traits == UndefinedPixelTrait)
2306 case ClearCompositeOp:
2307 case CopyCompositeOp:
2308 case ReplaceCompositeOp:
2309 case SrcCompositeOp:
2312 if (channel == AlphaPixelChannel)
2313 pixel=(MagickRealType) TransparentAlpha;
2316 case DstAtopCompositeOp:
2318 case OutCompositeOp:
2319 case SrcInCompositeOp:
2320 case SrcOutCompositeOp:
2322 pixel=(MagickRealType) q[i];
2323 if (channel == AlphaPixelChannel)
2324 pixel=(MagickRealType) TransparentAlpha;
2329 pixel=source[channel];
2333 q[i]=ClampToQuantum(pixel);
2335 q+=GetPixelChannels(image);
2339 Authentic composite:
2340 Sa: source normalized alpha.
2341 Da: destination normalized alpha.
2343 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
2344 Da=QuantumScale*GetPixelAlpha(image,q);
2347 case DarkenCompositeOp:
2348 case DstAtopCompositeOp:
2349 case DstInCompositeOp:
2351 case LightenCompositeOp:
2352 case SrcInCompositeOp:
2357 case DifferenceCompositeOp:
2358 case DivideDstCompositeOp:
2359 case DivideSrcCompositeOp:
2360 case ExclusionCompositeOp:
2361 case MinusDstCompositeOp:
2362 case MinusSrcCompositeOp:
2363 case ModulusAddCompositeOp:
2364 case ModulusSubtractCompositeOp:
2365 case MultiplyCompositeOp:
2366 case ScreenCompositeOp:
2368 alpha=RoundToUnity(Sa+Da-Sa*Da);
2371 case DstOverCompositeOp:
2373 alpha=Da*(-Sa)+Da+Sa;
2376 case DstOutCompositeOp:
2381 case OutCompositeOp:
2382 case SrcOutCompositeOp:
2387 case OverCompositeOp:
2388 case SrcOverCompositeOp:
2390 alpha=Sa*(-Da)+Sa+Da;
2393 case PlusCompositeOp:
2395 alpha=RoundToUnity(Sa+Da);
2398 case XorCompositeOp:
2400 alpha=Sa+Da-2.0*Sa*Da;
2409 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2421 channel=GetPixelChannelMapChannel(image,i);
2422 traits=GetPixelChannelMapTraits(image,channel);
2423 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
2424 if ((traits == UndefinedPixelTrait) ||
2425 (composite_traits == UndefinedPixelTrait))
2429 Dc: destination color.
2431 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
2432 Dc=(MagickRealType) q[i];
2433 if ((traits & CopyPixelTrait) != 0)
2435 if (channel != AlphaPixelChannel)
2449 case AtopCompositeOp:
2450 case SrcAtopCompositeOp:
2451 case DstCompositeOp:
2454 pixel=QuantumRange*Da;
2457 case CopyCompositeOp:
2458 case DstAtopCompositeOp:
2459 case ReplaceCompositeOp:
2460 case SrcCompositeOp:
2462 pixel=QuantumRange*Sa;
2465 case DarkenIntensityCompositeOp:
2467 pixel=Sa*GetPixelIntensity(composite_image,p) <
2468 Da*GetPixelIntensity(image,q) ? Sa : Da;
2471 case LightenIntensityCompositeOp:
2473 pixel=Sa*GetPixelIntensity(composite_image,p) >
2474 Da*GetPixelIntensity(image,q) ? Sa : Da;
2479 pixel=QuantumRange*alpha;
2483 q[i]=ClampToQuantum(pixel);
2487 Porter-Duff compositions.
2491 case DarkenCompositeOp:
2492 case LightenCompositeOp:
2494 gamma=1.0-QuantumScale*Dc;
2500 gamma=QuantumRange/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
2504 case AtopCompositeOp:
2505 case SrcAtopCompositeOp:
2507 pixel=Sc*Sa+Dc*(1.0-Sa);
2510 case CopyCompositeOp:
2511 case ReplaceCompositeOp:
2512 case SrcCompositeOp:
2517 case DarkenCompositeOp:
2521 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
2524 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
2527 case DarkenIntensityCompositeOp:
2529 pixel=Sa*GetPixelIntensity(composite_image,p) <
2530 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2533 case DifferenceCompositeOp:
2535 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
2538 case DivideDstCompositeOp:
2540 if ((fabs((QuantumScale*Sa*Sc)) < MagickEpsilon) &&
2541 (fabs((QuantumScale*Da*Dc)) < MagickEpsilon))
2543 pixel=gamma*((QuantumScale*Sa*Sc)*(1.0-Da)+
2544 (QuantumScale*Da*Dc)*(1.0-Sa));
2547 if (fabs((QuantumScale*Da*Dc)) < MagickEpsilon)
2549 pixel=gamma*(Sa*Da+(QuantumScale*Sa*Sc)*(1.0-Da)+
2550 (QuantumScale*Da*Dc)*(1.0-Sa));
2553 pixel=gamma*((QuantumScale*Sa*Sc)*Da*Da/(QuantumScale*Da*Dc)+
2554 (QuantumScale*Sa*Sc)*(1.0-Da)+(QuantumScale*Da*Dc)*(1.0-Sa));
2557 case DivideSrcCompositeOp:
2559 if ((fabs((QuantumScale*Da*Dc)) < MagickEpsilon) &&
2560 (fabs((QuantumScale*Sa*Sc)) < MagickEpsilon))
2562 pixel=gamma*((QuantumScale*Da*Dc)*(1.0-Sa)+
2563 (QuantumScale*Sa*Sc)*(1.0-Da));
2566 if (fabs((QuantumScale*Sa*Sc)) < MagickEpsilon)
2568 pixel=gamma*(Da*Sa+(QuantumScale*Da*Dc)*(1.0-Sa)+
2569 (QuantumScale*Sa*Sc)*(1.0-Da));
2572 pixel=gamma*((QuantumScale*Da*Dc)*Sa*Sa/(QuantumScale*Sa*Sc)+
2573 (QuantumScale*Da*Dc)*(1.0-Sa)+(QuantumScale*Sa*Sc)*(1.0-Da));
2576 case DstAtopCompositeOp:
2578 pixel=Dc*Da+Sc*(1.0-Da);
2581 case DstCompositeOp:
2587 case DstInCompositeOp:
2589 pixel=gamma*(Sa*Dc*Sa);
2592 case DstOutCompositeOp:
2594 pixel=gamma*(Da*Dc*(1.0-Sa));
2597 case DstOverCompositeOp:
2599 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
2602 case ExclusionCompositeOp:
2604 pixel=gamma*((QuantumScale*Sa*Sc)*Da+(QuantumScale*Da*Dc)*Sa-2.0*
2605 (QuantumScale*Sa*Sc)*(QuantumScale*Da*Dc)+(QuantumScale*Sa*Sc)*
2606 (1.0-Da)+(QuantumScale*Da*Dc)*(1.0-Sa));
2610 case SrcInCompositeOp:
2612 pixel=gamma*(Da*Sc*Da);
2615 case LightenIntensityCompositeOp:
2617 pixel=Sa*GetPixelIntensity(composite_image,p) >
2618 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2621 case MinusDstCompositeOp:
2623 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2626 case MinusSrcCompositeOp:
2628 pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2631 case LightenCompositeOp:
2635 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
2638 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
2641 case ModulusAddCompositeOp:
2644 if (pixel > QuantumRange)
2645 pixel-=(QuantumRange+1.0);
2646 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2649 case ModulusSubtractCompositeOp:
2653 pixel+=(QuantumRange+1.0);
2654 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2657 case MultiplyCompositeOp:
2659 pixel=gamma*((QuantumScale*Sa*Sc)*(QuantumScale*Da*Dc)+
2660 (QuantumScale*Sa*Sc)*(1.0-Da)+(QuantumScale*Da*Dc)*(1.0-Sa));
2663 case OutCompositeOp:
2664 case SrcOutCompositeOp:
2666 pixel=gamma*(Sa*Sc*(1.0-Da));
2669 case OverCompositeOp:
2670 case SrcOverCompositeOp:
2672 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
2675 case PlusCompositeOp:
2677 pixel=gamma*(Sa*Sc+Da*Dc);
2680 case ScreenCompositeOp:
2682 pixel=gamma*((Sa*Sc)+(Da*Dc)-(Sa*Sc)*(Da*Dc));
2685 case XorCompositeOp:
2687 pixel=gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
2693 q[i]=ClampToQuantum(pixel);
2702 GetPixelInfo(image,&zero);
2705 destination.red=(MagickRealType) GetPixelRed(image,q);
2706 destination.green=(MagickRealType) GetPixelGreen(image,q);
2707 destination.blue=(MagickRealType) GetPixelBlue(image,q);
2708 if (image->colorspace == CMYKColorspace)
2709 destination.black=(MagickRealType) GetPixelBlack(image,q);
2710 if (image->colorspace == CMYKColorspace)
2712 destination.red=(MagickRealType) QuantumRange-destination.red;
2713 destination.green=(MagickRealType) QuantumRange-destination.green;
2714 destination.blue=(MagickRealType) QuantumRange-destination.blue;
2715 destination.black=(MagickRealType) QuantumRange-destination.black;
2717 if (image->matte != MagickFalse)
2718 destination.alpha=(MagickRealType) GetPixelAlpha(image,q);
2720 Handle destination modifications outside overlaid region.
2722 composite=destination;
2723 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2724 ((x-x_offset) >= (ssize_t) composite_image->columns))
2728 case DissolveCompositeOp:
2729 case BlendCompositeOp:
2731 composite.alpha=destination_dissolve*(composite.alpha);
2734 case ClearCompositeOp:
2735 case SrcCompositeOp:
2737 CompositeClear(&destination,&composite);
2741 case SrcInCompositeOp:
2742 case OutCompositeOp:
2743 case SrcOutCompositeOp:
2744 case DstInCompositeOp:
2745 case DstAtopCompositeOp:
2746 case CopyOpacityCompositeOp:
2747 case ChangeMaskCompositeOp:
2749 composite.alpha=(MagickRealType) TransparentAlpha;
2754 (void) GetOneVirtualPixelInfo(composite_image,
2755 GetPixelCacheVirtualMethod(composite_image),x-x_offset,y-
2756 y_offset,&composite,exception);
2760 if (image->colorspace == CMYKColorspace)
2762 composite.red=(MagickRealType) QuantumRange-composite.red;
2763 composite.green=(MagickRealType) QuantumRange-composite.green;
2764 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2765 composite.black=(MagickRealType) QuantumRange-composite.black;
2767 SetPixelRed(image,ClampToQuantum(composite.red),q);
2768 SetPixelGreen(image,ClampToQuantum(composite.green),q);
2769 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2770 if (image->matte != MagickFalse)
2771 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2772 if (image->colorspace == CMYKColorspace)
2773 SetPixelBlack(image,ClampToQuantum(composite.black),q);
2774 q+=GetPixelChannels(image);
2778 Handle normal overlay of source onto destination.
2780 source.red=(MagickRealType) GetPixelRed(composite_image,p);
2781 source.green=(MagickRealType) GetPixelGreen(composite_image,p);
2782 source.blue=(MagickRealType) GetPixelBlue(composite_image,p);
2783 if (composite_image->colorspace == CMYKColorspace)
2784 source.black=(MagickRealType) GetPixelBlack(composite_image,p);
2785 if (composite_image->colorspace == CMYKColorspace)
2787 source.red=(MagickRealType) QuantumRange-source.red;
2788 source.green=(MagickRealType) QuantumRange-source.green;
2789 source.blue=(MagickRealType) QuantumRange-source.blue;
2790 source.black=(MagickRealType) QuantumRange-source.black;
2792 if (composite_image->matte != MagickFalse)
2793 source.alpha=(MagickRealType) GetPixelAlpha(composite_image,p);
2795 Porter-Duff compositions.
2799 case ClearCompositeOp:
2801 CompositeClear(&destination,&composite);
2804 case SrcCompositeOp:
2805 case CopyCompositeOp:
2806 case ReplaceCompositeOp:
2812 case DstCompositeOp:
2814 case OverCompositeOp:
2815 case SrcOverCompositeOp:
2817 CompositePixelInfoOver(&source,source.alpha,&destination,
2818 destination.alpha,&composite);
2821 case DstOverCompositeOp:
2823 CompositePixelInfoOver(&destination,destination.alpha,&source,
2824 source.alpha,&composite);
2827 case SrcInCompositeOp:
2830 CompositeIn(&source,&destination,&composite);
2833 case DstInCompositeOp:
2835 CompositeIn(&destination,&source,&composite);
2838 case OutCompositeOp:
2839 case SrcOutCompositeOp:
2841 CompositeOut(&source,&destination,&composite);
2844 case DstOutCompositeOp:
2846 CompositeOut(&destination,&source,&composite);
2849 case AtopCompositeOp:
2850 case SrcAtopCompositeOp:
2852 CompositeAtop(&source,&destination,&composite);
2855 case DstAtopCompositeOp:
2857 CompositeAtop(&destination,&source,&composite);
2860 case XorCompositeOp:
2862 CompositeXor(&source,&destination,&composite);
2865 case PlusCompositeOp:
2867 CompositePlus(image,&source,&destination,&composite);
2870 case MinusDstCompositeOp:
2872 CompositeMinus(image,&source,&destination,&composite);
2875 case MinusSrcCompositeOp:
2877 CompositeMinus(image,&destination,&source,&composite);
2880 case ModulusAddCompositeOp:
2882 CompositeModulusAdd(image,&source,&destination,&composite);
2885 case ModulusSubtractCompositeOp:
2887 CompositeModulusSubtract(image,&source,&destination,&composite);
2890 case DifferenceCompositeOp:
2892 CompositeDifference(image,&source,&destination,&composite);
2895 case ExclusionCompositeOp:
2897 CompositeExclusion(image,&source,&destination,&composite);
2900 case MultiplyCompositeOp:
2902 CompositeMultiply(image,&source,&destination,&composite);
2905 case ScreenCompositeOp:
2907 CompositeScreen(image,&source,&destination,&composite);
2910 case DivideDstCompositeOp:
2912 CompositeDivide(image,&source,&destination,&composite);
2915 case DivideSrcCompositeOp:
2917 CompositeDivide(image,&destination,&source,&composite);
2920 case DarkenCompositeOp:
2922 CompositeDarken(image,&source,&destination,&composite);
2925 case LightenCompositeOp:
2927 CompositeLighten(image,&source,&destination,&composite);
2930 case DarkenIntensityCompositeOp:
2932 CompositeDarkenIntensity(image,&source,&destination,&composite);
2935 case LightenIntensityCompositeOp:
2937 CompositeLightenIntensity(image,&source,&destination,&composite);
2940 case MathematicsCompositeOp:
2942 CompositeMathematics(image,&source,&destination,&geometry_info,
2946 case ColorDodgeCompositeOp:
2948 CompositeColorDodge(&source,&destination,&composite);
2951 case ColorBurnCompositeOp:
2953 CompositeColorBurn(&source,&destination,&composite);
2956 case LinearDodgeCompositeOp:
2958 CompositeLinearDodge(&source,&destination,&composite);
2961 case LinearBurnCompositeOp:
2963 CompositeLinearBurn(&source,&destination,&composite);
2966 case HardLightCompositeOp:
2968 CompositeHardLight(&source,&destination,&composite);
2971 case OverlayCompositeOp:
2973 CompositeHardLight(&destination,&source,&composite);
2976 case SoftLightCompositeOp:
2978 CompositeSoftLight(&source,&destination,&composite);
2981 case LinearLightCompositeOp:
2983 CompositeLinearLight(&source,&destination,&composite);
2986 case PegtopLightCompositeOp:
2988 CompositePegtopLight(&source,&destination,&composite);
2991 case VividLightCompositeOp:
2993 CompositeVividLight(&source,&destination,&composite);
2996 case PinLightCompositeOp:
2998 CompositePinLight(&source,&destination,&composite);
3001 case ChangeMaskCompositeOp:
3003 if ((composite.alpha > ((MagickRealType) QuantumRange/2.0)) ||
3004 (IsFuzzyEquivalencePixelInfo(&source,&destination) != MagickFalse))
3005 composite.alpha=(MagickRealType) TransparentAlpha;
3007 composite.alpha=(MagickRealType) OpaqueAlpha;
3010 case BumpmapCompositeOp:
3012 if (source.alpha == TransparentAlpha)
3014 CompositeBumpmap(&source,&destination,&composite);
3017 case DissolveCompositeOp:
3019 CompositePixelInfoOver(&source,source_dissolve*source.alpha,
3020 &destination,(MagickRealType) (destination_dissolve*
3021 destination.alpha),&composite);
3024 case BlendCompositeOp:
3026 CompositePixelInfoBlend(&source,source_dissolve,&destination,
3027 destination_dissolve,&composite);
3030 case ThresholdCompositeOp:
3032 CompositeThreshold(&source,&destination,threshold,amount,&composite);
3035 case ModulateCompositeOp:
3045 if (source.alpha == TransparentAlpha)
3047 offset=(ssize_t) (GetPixelInfoIntensity(&source)-midpoint);
3050 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
3051 &saturation,&brightness);
3052 brightness+=(0.01*percent_brightness*offset)/midpoint;
3053 saturation*=0.01*percent_saturation;
3054 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
3056 composite.green=green;
3057 composite.blue=blue;
3060 case HueCompositeOp:
3062 if (source.alpha == TransparentAlpha)
3064 if (destination.alpha == TransparentAlpha)
3069 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
3070 &saturation,&brightness);
3071 CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
3072 HSBComposite(hue,saturation,brightness,&composite.red,
3073 &composite.green,&composite.blue);
3074 if (source.alpha < destination.alpha)
3075 composite.alpha=source.alpha;
3078 case SaturateCompositeOp:
3080 if (source.alpha == TransparentAlpha)
3082 if (destination.alpha == TransparentAlpha)
3087 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
3088 &saturation,&brightness);
3089 CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
3091 HSBComposite(hue,saturation,brightness,&composite.red,
3092 &composite.green,&composite.blue);
3093 if (source.alpha < destination.alpha)
3094 composite.alpha=source.alpha;
3097 case LuminizeCompositeOp:
3099 if (source.alpha == TransparentAlpha)
3101 if (destination.alpha == TransparentAlpha)
3106 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
3107 &saturation,&brightness);
3108 CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
3110 HSBComposite(hue,saturation,brightness,&composite.red,
3111 &composite.green,&composite.blue);
3112 if (source.alpha < destination.alpha)
3113 composite.alpha=source.alpha;
3116 case ColorizeCompositeOp:
3118 if (source.alpha == TransparentAlpha)
3120 if (destination.alpha == TransparentAlpha)
3125 CompositeHSB(destination.red,destination.green,destination.blue,&sans,
3127 CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
3129 HSBComposite(hue,saturation,brightness,&composite.red,
3130 &composite.green,&composite.blue);
3131 if (source.alpha < destination.alpha)
3132 composite.alpha=source.alpha;
3135 case CopyRedCompositeOp:
3136 case CopyCyanCompositeOp:
3138 composite.red=source.red;
3141 case CopyGreenCompositeOp:
3142 case CopyMagentaCompositeOp:
3144 composite.green=source.green;
3147 case CopyBlueCompositeOp:
3148 case CopyYellowCompositeOp:
3150 composite.blue=source.blue;
3153 case CopyOpacityCompositeOp:
3155 if (source.matte == MagickFalse)
3157 composite.alpha=(MagickRealType) GetPixelInfoIntensity(&source);
3160 composite.alpha=source.alpha;
3163 case CopyBlackCompositeOp:
3165 if (source.colorspace != CMYKColorspace)
3166 ConvertRGBToCMYK(&source);
3167 composite.black=source.black;
3170 case BlurCompositeOp:
3171 case DisplaceCompositeOp:
3172 case DistortCompositeOp:
3180 if (image->colorspace == CMYKColorspace)
3182 composite.red=(MagickRealType) QuantumRange-composite.red;
3183 composite.green=(MagickRealType) QuantumRange-composite.green;
3184 composite.blue=(MagickRealType) QuantumRange-composite.blue;
3185 composite.black=(MagickRealType) QuantumRange-composite.black;
3187 SetPixelRed(image,ClampToQuantum(composite.red),q);
3188 SetPixelGreen(image,ClampToQuantum(composite.green),q);
3189 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
3190 if (image->colorspace == CMYKColorspace)
3191 SetPixelBlack(image,ClampToQuantum(composite.black),q);
3192 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
3194 p+=GetPixelChannels(composite_image);
3195 if (p >= (pixels+composite_image->columns*GetPixelChannels(composite_image)))
3197 q+=GetPixelChannels(image);
3199 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3201 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3206 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3207 #pragma omp critical (MagickCore_CompositeImage)
3209 proceed=SetImageProgress(image,CompositeImageTag,progress++,
3211 if (proceed == MagickFalse)
3215 composite_view=DestroyCacheView(composite_view);
3216 image_view=DestroyCacheView(image_view);
3217 if (destination_image != (Image * ) NULL)
3218 destination_image=DestroyImage(destination_image);
3223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3227 % T e x t u r e I m a g e %
3231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3233 % TextureImage() repeatedly tiles the texture image across and down the image
3236 % The format of the TextureImage method is:
3238 % MagickBooleanType TextureImage(Image *image,const Image *texture,
3239 % ExceptionInfo *exception)
3241 % A description of each parameter follows:
3243 % o image: the image.
3245 % o texture: This image is the texture to layer on the background.
3248 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
3249 ExceptionInfo *exception)
3251 #define TextureImageTag "Texture/Image"
3263 assert(image != (Image *) NULL);
3264 if (image->debug != MagickFalse)
3265 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3266 assert(image->signature == MagickSignature);
3267 if (texture == (const Image *) NULL)
3268 return(MagickFalse);
3269 (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
3270 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
3271 return(MagickFalse);
3273 if ((image->compose != CopyCompositeOp) &&
3274 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
3275 (texture->matte != MagickFalse)))
3278 Tile texture onto the image background.
3280 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3281 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
3283 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
3288 if (status == MagickFalse)
3290 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
3295 thread_status=CompositeImage(image,image->compose,texture,x+
3296 texture->tile_offset.x,y+texture->tile_offset.y,exception);
3297 if (thread_status == MagickFalse)
3299 status=thread_status;
3303 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3308 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3309 #pragma omp critical (MagickCore_TextureImage)
3311 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
3313 if (proceed == MagickFalse)
3317 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
3318 image->rows,image->rows);
3322 Tile texture onto the image background (optimized).
3325 image_view=AcquireCacheView(image);
3326 texture_view=AcquireCacheView(texture);
3327 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3328 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
3330 for (y=0; y < (ssize_t) image->rows; y++)
3335 register const Quantum
3348 if (status == MagickFalse)
3350 pixels=GetCacheViewVirtualPixels(texture_view,texture->tile_offset.x,(y+
3351 texture->tile_offset.y) % texture->rows,texture->columns,1,exception);
3352 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3354 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3359 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
3365 width=texture->columns;
3366 if ((x+(ssize_t) width) > (ssize_t) image->columns)
3367 width=image->columns-x;
3368 for (i=0; i < (ssize_t) width; i++)
3370 SetPixelRed(image,GetPixelRed(texture,p),q);
3371 SetPixelGreen(image,GetPixelGreen(texture,p),q);
3372 SetPixelBlue(image,GetPixelBlue(texture,p),q);
3373 SetPixelAlpha(image,GetPixelAlpha(texture,p),q);
3374 if ((image->colorspace == CMYKColorspace) &&
3375 (texture->colorspace == CMYKColorspace))
3376 SetPixelBlack(image,GetPixelBlack(texture,p),q);
3377 p+=GetPixelChannels(texture);
3378 q+=GetPixelChannels(image);
3381 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3382 if (sync == MagickFalse)
3384 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3389 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3390 #pragma omp critical (MagickCore_TextureImage)
3392 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3394 if (proceed == MagickFalse)
3398 texture_view=DestroyCacheView(texture_view);
3399 image_view=DestroyCacheView(image_view);