2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
7 % C O O MM MM P P O O SS I T E %
8 % C O O M M M PPPP O O SSS I T EEE %
9 % C O O M M P O O SS I T E %
10 % CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
13 % MagickCore Image Composite Methods %
20 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/client.h"
48 #include "MagickCore/color.h"
49 #include "MagickCore/color-private.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/colorspace-private.h"
52 #include "MagickCore/composite.h"
53 #include "MagickCore/composite-private.h"
54 #include "MagickCore/constitute.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/fx.h"
57 #include "MagickCore/gem.h"
58 #include "MagickCore/geometry.h"
59 #include "MagickCore/image.h"
60 #include "MagickCore/image-private.h"
61 #include "MagickCore/list.h"
62 #include "MagickCore/log.h"
63 #include "MagickCore/monitor.h"
64 #include "MagickCore/monitor-private.h"
65 #include "MagickCore/memory_.h"
66 #include "MagickCore/option.h"
67 #include "MagickCore/pixel-accessor.h"
68 #include "MagickCore/property.h"
69 #include "MagickCore/quantum.h"
70 #include "MagickCore/resample.h"
71 #include "MagickCore/resource_.h"
72 #include "MagickCore/string_.h"
73 #include "MagickCore/thread-private.h"
74 #include "MagickCore/utility.h"
75 #include "MagickCore/version.h"
78 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 % C o m p o s i t e I m a g e C h a n n e l %
86 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88 % CompositeImageChannel() returns the second image composited onto the first
89 % at the specified offset, using the specified composite method.
91 % The format of the CompositeImageChannel method is:
93 % MagickBooleanType CompositeImage(Image *image,
94 % const CompositeOperator compose,Image *composite_image,
95 % const ssize_t x_offset,const ssize_t y_offset)
96 % MagickBooleanType CompositeImageChannel(Image *image,
97 % const ChannelType channel,const CompositeOperator compose,
98 % Image *composite_image,const ssize_t x_offset,const ssize_t y_offset)
100 % A description of each parameter follows:
102 % o image: the destination image, modified by he composition
104 % o channel: the channel.
106 % o compose: This operator affects how the composite is applied to
107 % the image. The operators and how they are utilized are listed here
108 % http://www.w3.org/TR/SVG12/#compositing.
110 % o composite_image: the composite (source) image.
112 % o x_offset: the column offset of the composited image.
114 % o y_offset: the row offset of the composited image.
116 % Extra Controls from Image meta-data in 'composite_image' (artifacts)
119 % A string containing extra numerical arguments for specific compose
120 % methods, generally expressed as a 'geometry' or a comma separated list
123 % Compose methods needing such arguments include "BlendCompositeOp" and
124 % "DisplaceCompositeOp".
126 % o "compose:outside-overlay"
127 % Modify how the composition is to effect areas not directly covered
128 % by the 'composite_image' at the offset given. Normally this is
129 % dependant on the 'compose' method, especially Duff-Porter methods.
131 % If set to "false" then disable all normal handling of pixels not
132 % covered by the composite_image. Typically used for repeated tiling
133 % of the composite_image by the calling API.
135 % Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
139 static inline double MagickMin(const double x,const double y)
145 static inline double MagickMax(const double x,const double y)
153 Programmers notes on SVG specification.
155 A Composition is defined by...
156 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
157 Blending areas : X = 1 for area of overlap ie: f(Sc,Dc)
158 Y = 1 for source preserved
159 Z = 1 for destination preserved
161 Conversion to transparency (then optimized)
162 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
163 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
166 Sca = Sc*Sa normalized Source color divided by Source alpha
167 Dca = Dc*Da normalized Dest color divided by Dest alpha
168 Dc' = Dca'/Da' the desired color value for this channel.
170 Da' in in the follow formula as 'gamma' The resulting alpla value.
172 Most functions use a blending mode of over (X=1,Y=1,Z=1)
173 this results in the following optimizations...
175 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
176 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
178 The above SVG definitions also definate that Mathematical Composition
179 methods should use a 'Over' blending mode for Alpha Channel.
180 It however was not applied for composition modes of 'Plus', 'Minus',
181 the modulus versions of 'Add' and 'Subtract'.
183 Mathematical operator changes to be applied from IM v6.7...
185 1/ Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
186 'ModulusAdd' and 'ModulusSubtract' for clarity.
188 2/ All mathematical compositions work as per the SVG specification
189 with regard to blending. This now includes 'ModulusAdd' and
192 3/ When the special channel flag 'sync' (syncronize channel updates)
193 is turned off (enabled by default) then mathematical compositions are
194 only performed on the channels specified, and are applied
195 independantally of each other. In other words the mathematics is
196 performed as 'pure' mathematical operations, rather than as image
200 static inline MagickRealType Atop(const MagickRealType p,
201 const MagickRealType Sa,const MagickRealType q,
202 const MagickRealType magick_unused(Da))
204 return(p*Sa+q*(1.0-Sa)); /* Da optimized out, Da/gamma => 1.0 */
207 static inline void CompositeAtop(const PixelInfo *p,const PixelInfo *q,
208 PixelInfo *composite)
213 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
214 composite->alpha=q->alpha; /* optimized Da = 1.0-Gamma */
215 composite->red=Atop(p->red,Sa,q->red,1.0);
216 composite->green=Atop(p->green,Sa,q->green,1.0);
217 composite->blue=Atop(p->blue,Sa,q->blue,1.0);
218 if (q->colorspace == CMYKColorspace)
219 composite->black=Atop(p->black,Sa,q->black,1.0);
223 What is this Composition method for? Can't find any specification!
224 WARNING this is not doing correct 'over' blend handling (Anthony Thyssen).
226 static inline void CompositeBumpmap(const PixelInfo *p,const PixelInfo *q,
227 PixelInfo *composite)
232 intensity=(MagickRealType) GetPixelInfoIntensity(p);
233 composite->red=QuantumScale*intensity*q->red;
234 composite->green=QuantumScale*intensity*q->green;
235 composite->blue=QuantumScale*intensity*q->blue;
236 composite->alpha=(MagickRealType) QuantumScale*intensity*p->alpha;
237 if (q->colorspace == CMYKColorspace)
238 composite->black=QuantumScale*intensity*q->black;
241 static inline void CompositeClear(const PixelInfo *q,PixelInfo *composite)
243 composite->alpha=(MagickRealType) TransparentAlpha;
245 composite->green=0.0;
247 if (q->colorspace == CMYKColorspace)
248 composite->black=0.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 PixelInfo *p,const PixelInfo *q,
337 const ChannelType channel,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 ((channel & SyncChannels) == 0)
349 Handle channels as separate grayscale channels.
351 if ((channel & RedChannel) != 0)
352 composite->red=MagickMin(p->red,q->red);
353 if ((channel & GreenChannel) != 0)
354 composite->green=MagickMin(p->green,q->green);
355 if ((channel & BlueChannel) != 0)
356 composite->blue=MagickMin(p->blue,q->blue);
357 if ((channel & BlackChannel) != 0 &&
358 (q->colorspace == CMYKColorspace))
359 composite->black=MagickMin(p->black,q->black);
360 if ((channel & AlphaChannel) != 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 PixelInfo *p,
375 const PixelInfo *q,const ChannelType channel,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 ((channel & SyncChannels) == 0)
391 from_p=GetPixelInfoIntensity(p) < GetPixelInfoIntensity(q) ? MagickTrue :
393 if ((channel & RedChannel) != 0)
394 composite->red=from_p != MagickFalse ? p->red : q->red;
395 if ((channel & GreenChannel) != 0)
396 composite->green=from_p != MagickFalse ? p->green : q->green;
397 if ((channel & BlueChannel) != 0)
398 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
399 if (((channel & BlackChannel) != 0) &&
400 (q->colorspace == CMYKColorspace))
401 composite->black=from_p != MagickFalse ? p->black : q->black;
402 if ((channel & AlphaChannel) != 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 PixelInfo *p,
422 const PixelInfo *q,const ChannelType channel,PixelInfo *composite)
429 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
430 Da=QuantumScale*q->alpha;
431 if ((channel & SyncChannels) == 0)
434 Handle channels as separate grayscale channels.
436 if ((channel & RedChannel) != 0)
437 composite->red=fabs((double) (p->red-q->red));
438 if ((channel & GreenChannel) != 0)
439 composite->green=fabs((double) (p->green-q->green));
440 if ((channel & BlueChannel) != 0)
441 composite->blue=fabs((double) (p->blue-q->blue));
442 if (((channel & BlackChannel) != 0) &&
443 (q->colorspace == CMYKColorspace))
444 composite->black=fabs((double) (p->black-q->black));
445 if ((channel & AlphaChannel) != 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 PixelInfo *p,const PixelInfo *q,
480 const ChannelType channel,PixelInfo *composite)
487 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
488 Da=QuantumScale*q->alpha;
489 if ((channel & SyncChannels) == 0)
492 Handle channels as separate grayscale channels.
494 if ((channel & RedChannel) != 0)
495 composite->red=QuantumRange*Divide(QuantumScale*p->red,1.0,
496 QuantumScale*q->red,1.0);
497 if ((channel & GreenChannel) != 0)
498 composite->green=QuantumRange*Divide(QuantumScale*p->green,1.0,
499 QuantumScale*q->green,1.0);
500 if ((channel & BlueChannel) != 0)
501 composite->blue=QuantumRange*Divide(QuantumScale*p->blue,1.0,
502 QuantumScale*q->blue,1.0);
503 if (((channel & BlackChannel) != 0) &&
504 (q->colorspace == CMYKColorspace))
505 composite->black=QuantumRange*Divide(QuantumScale*p->black,1.0,
506 QuantumScale*q->black,1.0);
507 if ((channel & AlphaChannel) != 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 PixelInfo *p,const PixelInfo *q,
532 const ChannelType channel,PixelInfo *composite)
539 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
540 Da=QuantumScale*q->alpha;
541 if ((channel & SyncChannels) == 0)
544 Handle channels as separate grayscale channels.
546 if ((channel & RedChannel) != 0)
547 composite->red=QuantumRange*Exclusion(QuantumScale*p->red,1.0,
548 QuantumScale*q->red,1.0);
549 if ((channel & GreenChannel) != 0)
550 composite->green=QuantumRange*Exclusion(QuantumScale*p->green,1.0,
551 QuantumScale*q->green,1.0);
552 if ((channel & BlueChannel) != 0)
553 composite->blue=QuantumRange*Exclusion(QuantumScale*p->blue,1.0,
554 QuantumScale*q->blue,1.0);
555 if (((channel & BlackChannel) != 0) &&
556 (q->colorspace == CMYKColorspace))
557 composite->black=QuantumRange*Exclusion(QuantumScale*p->black,1.0,
558 QuantumScale*q->black,1.0);
559 if ((channel & AlphaChannel) != 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);
634 *saturation=(double) (1.0-min/max);
639 *hue=(double) ((green-blue)/delta);
642 *hue=(double) (2.0+(blue-red)/delta);
645 *hue=(double) (4.0+(red-green)/delta);
651 static inline MagickRealType In(const MagickRealType p,const MagickRealType Sa,
652 const MagickRealType magick_unused(q),const MagickRealType Da)
657 static inline void CompositeIn(const PixelInfo *p,const PixelInfo *q,
658 PixelInfo *composite)
665 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
666 Da=QuantumScale*q->alpha;
668 composite->alpha=(MagickRealType) QuantumRange*gamma;
669 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
670 composite->red=gamma*In(p->red,Sa,q->red,Da);
671 composite->green=gamma*In(p->green,Sa,q->green,Da);
672 composite->blue=gamma*In(p->blue,Sa,q->blue,Da);
673 if (q->colorspace == CMYKColorspace)
674 composite->black=gamma*In(p->black,Sa,q->black,Da);
677 static inline MagickRealType Lighten(const MagickRealType p,
678 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
681 return(MagickOver_(p,alpha,q,beta)); /* src-over */
682 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
685 static inline void CompositeLighten(const PixelInfo *p,const PixelInfo *q,
686 const ChannelType channel,PixelInfo *composite)
692 Lighten is also equvalent to a 'Maximum' method OR a greyscale version of a
693 binary 'And' OR the 'Union' of pixel sets.
695 if ((channel & SyncChannels) == 0)
698 Handle channels as separate grayscale channels
700 if ((channel & RedChannel) != 0)
701 composite->red=MagickMax(p->red,q->red);
702 if ((channel & GreenChannel) != 0)
703 composite->green=MagickMax(p->green,q->green);
704 if ((channel & BlueChannel) != 0)
705 composite->blue=MagickMax(p->blue,q->blue);
706 if (((channel & BlackChannel) != 0) &&
707 (q->colorspace == CMYKColorspace))
708 composite->black=MagickMax(p->black,q->black);
709 if ((channel & AlphaChannel) != 0)
710 composite->alpha=MagickMin(p->alpha,q->alpha);
713 composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */
714 gamma=1.0-QuantumScale*composite->alpha;
715 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
716 composite->red=gamma*Lighten(p->red,p->alpha,q->red,q->alpha);
717 composite->green=gamma*Lighten(p->green,p->alpha,q->green,q->alpha);
718 composite->blue=gamma*Lighten(p->blue,p->alpha,q->blue,q->alpha);
719 if (q->colorspace == CMYKColorspace)
720 composite->black=gamma*Lighten(p->black,p->alpha,q->black,q->alpha);
723 static inline void CompositeLightenIntensity(const PixelInfo *p,
724 const PixelInfo *q,const ChannelType channel,PixelInfo *composite)
731 Select the pixel based on the intensity level.
732 If 'Sync' flag select whole pixel based on alpha weighted intensity.
733 Otherwise use Intenisty only, but restrict copy according to channel.
735 if ((channel & SyncChannels) == 0)
740 from_p=GetPixelInfoIntensity(p) > GetPixelInfoIntensity(q) ? MagickTrue :
742 if ((channel & RedChannel) != 0)
743 composite->red=from_p != MagickFalse ? p->red : q->red;
744 if ((channel & GreenChannel) != 0)
745 composite->green=from_p != MagickFalse ? p->green : q->green;
746 if ((channel & BlueChannel) != 0)
747 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
748 if (((channel & BlackChannel) != 0) &&
749 (q->colorspace == CMYKColorspace))
750 composite->black=from_p != MagickFalse ? p->black : q->black;
751 if ((channel & AlphaChannel) != 0)
752 composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha;
755 Sa=QuantumScale*p->alpha;
756 Da=QuantumScale*q->alpha;
757 *composite=(Sa*GetPixelInfoIntensity(p) > Da*GetPixelInfoIntensity(q)) ?
761 static inline void CompositeLinearDodge(const PixelInfo *p,const PixelInfo *q,
762 PixelInfo *composite)
769 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
770 Da=QuantumScale*q->alpha;
771 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
772 composite->alpha=(MagickRealType) QuantumRange*gamma;
773 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
774 composite->red=gamma*(p->red*Sa+q->red*Da);
775 composite->green=gamma*(p->green*Sa+q->green*Da);
776 composite->blue=gamma*(p->blue*Sa+q->blue*Da);
777 if (q->colorspace == CMYKColorspace)
778 composite->black=gamma*(p->black*Sa+q->black*Da);
782 static inline MagickRealType LinearBurn(const MagickRealType Sca,
783 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
786 LinearBurn: as defined by Abode Photoshop, according to
787 http://www.simplefilter.de/en/basics/mixmods.html is:
789 f(Sc,Dc) = Sc + Dc - 1
791 return(Sca+Dca-Sa*Da);
794 static inline void CompositeLinearBurn(const PixelInfo *p,const PixelInfo *q,
795 PixelInfo *composite)
802 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
803 Da=QuantumScale*q->alpha;
804 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
805 composite->alpha=(MagickRealType) QuantumRange*gamma;
806 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
807 composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
809 composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
811 composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
813 if (q->colorspace == CMYKColorspace)
814 composite->black=gamma*LinearBurn(QuantumScale*p->black*Sa,Sa,QuantumScale*
818 static inline MagickRealType LinearLight(const MagickRealType Sca,
819 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
822 LinearLight: as defined by Abode Photoshop, according to
823 http://www.simplefilter.de/en/basics/mixmods.html is:
825 f(Sc,Dc) = Dc + 2*Sc - 1
827 return((Sca-Sa)*Da+Sca+Dca);
830 static inline void CompositeLinearLight(const PixelInfo *p,const PixelInfo *q,
831 PixelInfo *composite)
838 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
839 Da=QuantumScale*q->alpha;
840 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
841 composite->alpha=(MagickRealType) QuantumRange*gamma;
842 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
843 composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
845 composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
847 composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
849 if (q->colorspace == CMYKColorspace)
850 composite->black=gamma*LinearLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
854 static inline MagickRealType Mathematics(const MagickRealType Sca,
855 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
856 const GeometryInfo *geometry_info)
862 'Mathematics' a free form user control mathematical composition is defined
865 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
867 Where the arguments A,B,C,D are (currently) passed to composite as
868 a command separated 'geometry' string in "compose:args" image artifact.
870 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
872 Applying the SVG transparency formula (see above), we get...
874 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
876 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
879 gamma=geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
880 geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
885 static inline void CompositeMathematics(const PixelInfo *p,const PixelInfo *q,
886 const ChannelType channel, const GeometryInfo *args, PixelInfo *composite)
893 Sa=QuantumScale*p->alpha; /* ??? - AT */
894 Da=QuantumScale*q->alpha;
895 if ((channel & SyncChannels) == 0)
898 Handle channels as separate grayscale channels.
900 if ((channel & RedChannel) != 0)
901 composite->red=QuantumRange*Mathematics(QuantumScale*p->red,1.0,
902 QuantumScale*q->red,1.0,args);
903 if ((channel & GreenChannel) != 0)
904 composite->green=QuantumRange*Mathematics(QuantumScale*p->green,1.0,
905 QuantumScale*q->green,1.0,args);
906 if ((channel & BlueChannel) != 0)
907 composite->blue=QuantumRange*Mathematics(QuantumScale*p->blue,1.0,
908 QuantumScale*q->blue,1.0,args);
909 if (((channel & BlackChannel) != 0) &&
910 (q->colorspace == CMYKColorspace))
911 composite->black=QuantumRange*Mathematics(QuantumScale*p->black,1.0,
912 QuantumScale*q->black,1.0,args);
913 if ((channel & AlphaChannel) != 0)
914 composite->alpha=QuantumRange*(1.0-Mathematics(Sa,1.0,Da,1.0,args));
917 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
918 composite->alpha=(MagickRealType) QuantumRange*gamma;
919 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
920 composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
922 composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,
923 QuantumScale*q->green*Da,Da,args);
924 composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
926 if (q->colorspace == CMYKColorspace)
927 composite->black=gamma*Mathematics(QuantumScale*p->black*Sa,Sa,
928 QuantumScale*q->black*Da,Da,args);
931 static inline void CompositePlus(const PixelInfo *p,const PixelInfo *q,
932 const ChannelType channel,PixelInfo *composite)
935 NOTE: "Plus" does not use 'over' alpha-blending but uses a special
936 'plus' form of alph-blending. It is the ONLY mathematical operator to
937 do this. this is what makes it different to the otherwise equivalent
938 "LinearDodge" composition method.
940 Note however that color channels are still effected by the alpha channel
941 as a result of the blending, making it just as useless for independant
942 channel maths, just like all other mathematical composition methods.
944 As such the removal of the 'sync' flag, is still a usful convention.
946 The CompositePixelInfoPlus() function is defined in
947 "composite-private.h" so it can also be used for Image Blending.
949 if ((channel & SyncChannels) == 0)
952 Handle channels as separate grayscale channels.
954 if ((channel & RedChannel) != 0)
955 composite->red=p->red+q->red;
956 if ((channel & GreenChannel) != 0)
957 composite->green=p->green+q->green;
958 if ((channel & BlueChannel) != 0)
959 composite->blue=p->blue+q->blue;
960 if (((channel & BlackChannel) != 0) &&
961 (q->colorspace == CMYKColorspace))
962 composite->black=p->black+q->black;
963 if ((channel & AlphaChannel) != 0)
964 composite->alpha=p->alpha+q->alpha-QuantumRange;
967 CompositePixelInfoPlus(p,p->alpha,q,q->alpha,composite);
970 static inline MagickRealType Minus(const MagickRealType Sca,
971 const MagickRealType Sa,const MagickRealType Dca,
972 const MagickRealType magick_unused(Da))
975 Minus Source from Destination
979 return(Sca+Dca-2.0*Dca*Sa);
982 static inline void CompositeMinus(const PixelInfo *p,const PixelInfo *q,
983 const ChannelType channel,PixelInfo *composite)
990 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
991 Da=QuantumScale*q->alpha;
992 if ((channel & SyncChannels) == 0)
995 Handle channels as separate grayscale channels.
997 if ((channel & RedChannel) != 0)
998 composite->red=p->red-q->red;
999 if ((channel & GreenChannel) != 0)
1000 composite->green=p->green-q->green;
1001 if ((channel & BlueChannel) != 0)
1002 composite->blue=p->blue-q->blue;
1003 if (((channel & BlackChannel) != 0) &&
1004 (q->colorspace == CMYKColorspace))
1005 composite->black=p->black-q->black;
1006 if ((channel & AlphaChannel) != 0)
1007 composite->alpha=QuantumRange*(1.0-(Sa-Da));
1010 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1011 composite->alpha=(MagickRealType) QuantumRange*gamma;
1012 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1013 composite->red=gamma*Minus(p->red*Sa,Sa,q->red*Da,Da);
1014 composite->green=gamma*Minus(p->green*Sa,Sa,q->green*Da,Da);
1015 composite->blue=gamma*Minus(p->blue*Sa,Sa,q->blue*Da,Da);
1016 if (q->colorspace == CMYKColorspace)
1017 composite->black=gamma*Minus(p->black*Sa,Sa,q->black*Da,Da);
1020 static inline MagickRealType ModulusAdd(const MagickRealType p,
1021 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1027 if (pixel > QuantumRange)
1028 pixel-=(QuantumRange+1.0);
1029 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1032 static inline void CompositeModulusAdd(const PixelInfo *p,const PixelInfo *q,
1033 const ChannelType channel,PixelInfo *composite)
1040 if ((channel & SyncChannels) == 0)
1043 Handle channels as separate grayscale channels.
1045 if ((channel & RedChannel) != 0)
1046 composite->red=ModulusAdd(p->red,1.0,q->red,1.0);
1047 if ((channel & GreenChannel) != 0)
1048 composite->green=ModulusAdd(p->green,1.0,q->green,1.0);
1049 if ((channel & BlueChannel) != 0)
1050 composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0);
1051 if (((channel & BlackChannel) != 0) &&
1052 (q->colorspace == CMYKColorspace))
1053 composite->black=ModulusAdd(p->black,1.0,q->black,1.0);
1054 if ((channel & AlphaChannel) != 0)
1055 composite->alpha=ModulusAdd(p->alpha,1.0,q->alpha,1.0);
1058 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1059 Da=QuantumScale*q->alpha;
1060 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1061 composite->alpha=(MagickRealType) QuantumRange*gamma;
1062 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1063 composite->red=ModulusAdd(p->red,Sa,q->red,Da);
1064 composite->green=ModulusAdd(p->green,Sa,q->green,Da);
1065 composite->blue=ModulusAdd(p->blue,Sa,q->blue,Da);
1066 if (q->colorspace == CMYKColorspace)
1067 composite->black=ModulusAdd(p->black,Sa,q->black,Da);
1070 static inline MagickRealType ModulusSubtract(const MagickRealType p,
1071 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1078 pixel+=(QuantumRange+1.0);
1079 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1082 static inline void CompositeModulusSubtract(const PixelInfo *p,
1083 const PixelInfo *q, const ChannelType channel,PixelInfo *composite)
1090 if ((channel & SyncChannels) == 0)
1093 Handle channels as separate grayscale channels,
1095 if ((channel & RedChannel) != 0)
1096 composite->red=ModulusSubtract(p->red,1.0,q->red,1.0);
1097 if ((channel & GreenChannel) != 0)
1098 composite->green=ModulusSubtract(p->green,1.0,q->green,1.0);
1099 if ((channel & BlueChannel) != 0)
1100 composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0);
1101 if (((channel & BlackChannel) != 0) &&
1102 (q->colorspace == CMYKColorspace))
1103 composite->black=ModulusSubtract(p->black,1.0,q->black,1.0);
1104 if ((channel & AlphaChannel) != 0)
1105 composite->alpha=ModulusSubtract(p->alpha,1.0,q->alpha,1.0);
1108 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1109 Da=QuantumScale*q->alpha;
1110 gamma = RoundToUnity(Sa+Da-Sa*Da);
1111 composite->alpha=(MagickRealType) QuantumRange*gamma;
1112 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1113 composite->red=ModulusSubtract(p->red,Sa,q->red,Da);
1114 composite->green=ModulusSubtract(p->green,Sa,q->green,Da);
1115 composite->blue=ModulusSubtract(p->blue,Sa,q->blue,Da);
1116 if (q->colorspace == CMYKColorspace)
1117 composite->black=ModulusSubtract(p->black,Sa,q->black,Da);
1120 static inline MagickRealType Multiply(const MagickRealType Sca,
1121 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1123 return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1126 static inline void CompositeMultiply(const PixelInfo *p,const PixelInfo *q,
1127 const ChannelType channel,PixelInfo *composite)
1134 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1135 Da=QuantumScale*q->alpha;
1136 if ((channel & SyncChannels) == 0)
1139 Handle channels as separate grayscale channels.
1141 if ((channel & RedChannel) != 0)
1142 composite->red=QuantumScale*p->red*q->red;
1143 if ((channel & GreenChannel) != 0)
1144 composite->green=QuantumScale*p->green*q->green;
1145 if ((channel & BlueChannel) != 0)
1146 composite->blue=QuantumScale*p->blue*q->blue;
1147 if (((channel & BlackChannel) != 0) &&
1148 (q->colorspace == CMYKColorspace))
1149 composite->black=QuantumScale*p->black*q->black;
1150 if ((channel & AlphaChannel) != 0)
1151 composite->alpha=QuantumRange*(1.0-Sa*Da);
1154 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1155 composite->alpha=(MagickRealType) QuantumRange*gamma;
1156 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1157 composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
1159 composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
1161 composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1163 if (q->colorspace == CMYKColorspace)
1164 composite->black=gamma*Multiply(QuantumScale*p->black*Sa,Sa,
1165 QuantumScale*q->black*Da,Da);
1168 static inline MagickRealType Out(const MagickRealType p,const MagickRealType Sa,
1169 const MagickRealType magick_unused(q),const MagickRealType Da)
1171 return(Sa*p*(1.0-Da));
1174 static inline void CompositeOut(const PixelInfo *p,const PixelInfo *q,
1175 PixelInfo *composite)
1182 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1183 Da=QuantumScale*q->alpha;
1185 composite->alpha=(MagickRealType) QuantumRange*gamma;
1186 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1187 composite->red=gamma*Out(p->red,Sa,q->red,Da);
1188 composite->green=gamma*Out(p->green,Sa,q->green,Da);
1189 composite->blue=gamma*Out(p->blue,Sa,q->blue,Da);
1190 if (q->colorspace == CMYKColorspace)
1191 composite->black=gamma*Out(p->black,Sa,q->black,Da);
1194 static MagickRealType PegtopLight(const MagickRealType Sca,
1195 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1198 PegTop: A Soft-Light alternative: A continuous version of the Softlight
1199 function, producing very similar results.
1201 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
1203 See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
1205 if (fabs(Da) < MagickEpsilon)
1207 return(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+Dca*(1.0-Sa));
1210 static inline void CompositePegtopLight(const PixelInfo *p,const PixelInfo *q,
1211 PixelInfo *composite)
1218 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1219 Da=QuantumScale*q->alpha;
1220 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1221 composite->alpha=(MagickRealType) QuantumRange*gamma;
1222 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1223 composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1225 composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1227 composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1229 if (q->colorspace == CMYKColorspace)
1230 composite->black=gamma*PegtopLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1234 static MagickRealType PinLight(const MagickRealType Sca,const MagickRealType Sa,
1235 const MagickRealType Dca,const MagickRealType Da)
1238 PinLight: A Photoshop 7 composition method
1239 http://www.simplefilter.de/en/basics/mixmods.html
1241 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
1243 if (Dca*Sa < Da*(2.0*Sca-Sa))
1244 return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
1245 if ((Dca*Sa) > (2.0*Sca*Da))
1246 return(Sca*Da+Sca+Dca*(1.0-Sa));
1247 return(Sca*(1.0-Da)+Dca);
1250 static inline void CompositePinLight(const PixelInfo *p,const PixelInfo *q,
1251 PixelInfo *composite)
1258 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1259 Da=QuantumScale*q->alpha;
1260 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1261 composite->alpha=(MagickRealType) QuantumRange*gamma;
1262 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1263 composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1265 composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1267 composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1269 if (q->colorspace == CMYKColorspace)
1270 composite->black=gamma*PinLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1274 static inline MagickRealType Screen(const MagickRealType Sca,
1275 const MagickRealType Dca)
1278 Screen: A negated multiply
1279 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
1281 return(Sca+Dca-Sca*Dca);
1284 static inline void CompositeScreen(const PixelInfo *p,const PixelInfo *q,
1285 const ChannelType channel,PixelInfo *composite)
1292 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1293 Da=QuantumScale*q->alpha;
1294 if ((channel & SyncChannels) == 0)
1297 Handle channels as separate grayscale channels.
1299 if ((channel & RedChannel) != 0)
1300 composite->red=QuantumRange*Screen(QuantumScale*p->red,
1301 QuantumScale*q->red);
1302 if ((channel & GreenChannel) != 0)
1303 composite->green=QuantumRange*Screen(QuantumScale*p->green,
1304 QuantumScale*q->green);
1305 if ((channel & BlueChannel) != 0)
1306 composite->blue=QuantumRange*Screen(QuantumScale*p->blue,
1307 QuantumScale*q->blue);
1308 if (((channel & BlackChannel) != 0) &&
1309 (q->colorspace == CMYKColorspace))
1310 composite->black=QuantumRange*Screen(QuantumScale*p->black,
1311 QuantumScale*q->black);
1312 if ((channel & AlphaChannel) != 0)
1313 composite->alpha=QuantumRange*(1.0-Screen(Sa,Da));
1316 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1317 composite->alpha=(MagickRealType) QuantumRange*gamma;
1318 Sa*=QuantumScale; Da*=QuantumScale; /* optimization */
1319 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1320 composite->red=gamma*Screen(p->red*Sa,q->red*Da);
1321 composite->green=gamma*Screen(p->green*Sa,q->green*Da);
1322 composite->blue=gamma*Screen(p->blue*Sa,q->blue*Da);
1323 if (q->colorspace == CMYKColorspace)
1324 composite->black=gamma*Screen(p->black*Sa,q->black*Da);
1327 static MagickRealType SoftLight(const MagickRealType Sca,
1328 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1335 New specification: March 2009 SVG specification.
1339 return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1340 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1342 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
1343 alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1346 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1350 static inline void CompositeSoftLight(const PixelInfo *p,const PixelInfo *q,
1351 PixelInfo *composite)
1358 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1359 Da=QuantumScale*q->alpha;
1360 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1361 composite->alpha=(MagickRealType) QuantumRange*gamma;
1362 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1363 composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1365 composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1367 composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1369 if (q->colorspace == CMYKColorspace)
1370 composite->black=gamma*SoftLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1374 static inline MagickRealType Threshold(const MagickRealType p,
1375 const MagickRealType q,const MagickRealType threshold,
1376 const MagickRealType amount)
1382 Multiply difference by amount, if differance larger than threshold???
1383 What use this is is completely unknown. The Opacity calculation appears to
1384 be inverted -- Anthony Thyssen
1389 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
1391 return(q+delta*amount);
1394 static inline void CompositeThreshold(const PixelInfo *p,const PixelInfo *q,
1395 const MagickRealType threshold,const MagickRealType amount,
1396 PixelInfo *composite)
1398 composite->red=Threshold(p->red,q->red,threshold,amount);
1399 composite->green=Threshold(p->green,q->green,threshold,amount);
1400 composite->blue=Threshold(p->blue,q->blue,threshold,amount);
1401 composite->alpha=Threshold(p->alpha,q->alpha,threshold,amount);
1402 if (q->colorspace == CMYKColorspace)
1403 composite->black=Threshold(p->black,q->black,threshold,amount);
1407 static MagickRealType VividLight(const MagickRealType Sca,
1408 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1411 VividLight: A Photoshop 7 composition method. See
1412 http://www.simplefilter.de/en/basics/mixmods.html.
1414 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
1416 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
1417 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1418 if ((2.0*Sca) <= Sa)
1419 return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1420 return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1423 static inline void CompositeVividLight(const PixelInfo *p,const PixelInfo *q,
1424 PixelInfo *composite)
1431 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1432 Da=QuantumScale*q->alpha;
1433 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1434 composite->alpha=(MagickRealType) QuantumRange*gamma;
1435 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1436 composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1438 composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1440 composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1442 if (q->colorspace == CMYKColorspace)
1443 composite->black=gamma*VividLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1447 static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
1448 const MagickRealType Dca,const MagickRealType Da)
1450 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
1453 static inline void CompositeXor(const PixelInfo *p,const PixelInfo *q,
1454 PixelInfo *composite)
1461 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1462 Da=QuantumScale*q->alpha;
1463 gamma=Sa+Da-2.0*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
1464 composite->alpha=(MagickRealType) QuantumRange*gamma;
1465 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1466 composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
1467 composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
1468 composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
1469 if (q->colorspace == CMYKColorspace)
1470 composite->black=gamma*Xor(p->black*Sa,Sa,q->black*Da,Da);
1473 static void HSBComposite(const double hue,const double saturation,
1474 const double brightness,MagickRealType *red,MagickRealType *green,
1475 MagickRealType *blue)
1485 Convert HSB to RGB colorspace.
1487 assert(red != (MagickRealType *) NULL);
1488 assert(green != (MagickRealType *) NULL);
1489 assert(blue != (MagickRealType *) NULL);
1490 if (saturation == 0.0)
1492 *red=(MagickRealType) QuantumRange*brightness;
1497 h=6.0*(hue-floor(hue));
1498 f=h-floor((double) h);
1499 p=brightness*(1.0-saturation);
1500 q=brightness*(1.0-saturation*f);
1501 t=brightness*(1.0-saturation*(1.0-f));
1507 *red=(MagickRealType) QuantumRange*brightness;
1508 *green=(MagickRealType) QuantumRange*t;
1509 *blue=(MagickRealType) QuantumRange*p;
1514 *red=(MagickRealType) QuantumRange*q;
1515 *green=(MagickRealType) QuantumRange*brightness;
1516 *blue=(MagickRealType) QuantumRange*p;
1521 *red=(MagickRealType) QuantumRange*p;
1522 *green=(MagickRealType) QuantumRange*brightness;
1523 *blue=(MagickRealType) QuantumRange*t;
1528 *red=(MagickRealType) QuantumRange*p;
1529 *green=(MagickRealType) QuantumRange*q;
1530 *blue=(MagickRealType) QuantumRange*brightness;
1535 *red=(MagickRealType) QuantumRange*t;
1536 *green=(MagickRealType) QuantumRange*p;
1537 *blue=(MagickRealType) QuantumRange*brightness;
1542 *red=(MagickRealType) QuantumRange*brightness;
1543 *green=(MagickRealType) QuantumRange*p;
1544 *blue=(MagickRealType) QuantumRange*q;
1550 MagickExport MagickBooleanType CompositeImage(Image *image,
1551 const CompositeOperator compose,const Image *composite_image,
1552 const ssize_t x_offset,const ssize_t y_offset)
1557 status=CompositeImageChannel(image,DefaultChannels,compose,composite_image,
1562 MagickExport MagickBooleanType CompositeImageChannel(Image *image,
1563 const ChannelType channel,const CompositeOperator compose,
1564 const Image *composite_image,const ssize_t x_offset,const ssize_t y_offset)
1566 #define CompositeImageTag "Composite/Image"
1588 modify_outside_overlay,
1599 destination_dissolve,
1613 Prepare composite image.
1615 assert(image != (Image *) NULL);
1616 assert(image->signature == MagickSignature);
1617 if (image->debug != MagickFalse)
1618 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1619 assert(composite_image != (Image *) NULL);
1620 assert(composite_image->signature == MagickSignature);
1621 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1622 return(MagickFalse);
1623 GetPixelInfo(image,&zero);
1624 destination_image=(Image *) NULL;
1626 destination_dissolve=1.0;
1627 modify_outside_overlay=MagickFalse;
1628 percent_brightness=100.0;
1629 percent_saturation=100.0;
1630 source_dissolve=1.0;
1634 case ClearCompositeOp:
1635 case SrcCompositeOp:
1637 case SrcInCompositeOp:
1638 case OutCompositeOp:
1639 case SrcOutCompositeOp:
1640 case DstInCompositeOp:
1641 case DstAtopCompositeOp:
1644 Modify destination outside the overlaid region.
1646 modify_outside_overlay=MagickTrue;
1649 case CopyCompositeOp:
1651 if ((x_offset < 0) || (y_offset < 0))
1653 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
1655 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
1658 exception=(&image->exception);
1659 image_view=AcquireCacheView(image);
1660 composite_view=AcquireCacheView(composite_image);
1661 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1662 #pragma omp parallel for schedule(dynamic,4) shared(status)
1664 for (y=0; y < (ssize_t) composite_image->rows; y++)
1669 register const Quantum
1678 if (status == MagickFalse)
1680 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1682 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1683 composite_image->columns,1,exception);
1684 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1689 for (x=0; x < (ssize_t) composite_image->columns; x++)
1691 SetPixelRed(image,GetPixelRed(composite_image,p),q);
1692 SetPixelGreen(image,GetPixelGreen(composite_image,p),q);
1693 SetPixelBlue(image,GetPixelBlue(composite_image,p),q);
1694 SetPixelAlpha(image,GetPixelAlpha(composite_image,p),q);
1695 if (image->colorspace == CMYKColorspace)
1696 SetPixelBlack(image,GetPixelBlack(composite_image,p),q);
1697 p+=GetPixelChannels(composite_image);
1698 q+=GetPixelChannels(image);
1700 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1701 if (sync == MagickFalse)
1703 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1708 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1709 #pragma omp critical (MagickCore_CompositeImage)
1711 proceed=SetImageProgress(image,CompositeImageTag,
1712 (MagickOffsetType) y,image->rows);
1713 if (proceed == MagickFalse)
1717 composite_view=DestroyCacheView(composite_view);
1718 image_view=DestroyCacheView(image_view);
1721 case CopyOpacityCompositeOp:
1722 case ChangeMaskCompositeOp:
1725 Modify destination outside the overlaid region and require an alpha
1726 channel to exist, to add transparency.
1728 if (image->matte == MagickFalse)
1729 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1730 modify_outside_overlay=MagickTrue;
1733 case BlurCompositeOp:
1755 Blur Image dictated by an overlay gradient map: X = red_channel;
1756 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
1758 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1760 if (destination_image == (Image *) NULL)
1761 return(MagickFalse);
1763 Determine the horizontal and vertical maximim blur.
1765 SetGeometryInfo(&geometry_info);
1767 value=GetImageArtifact(composite_image,"compose:args");
1768 if (value != (char *) NULL)
1769 flags=ParseGeometry(value,&geometry_info);
1770 if ((flags & WidthValue) == 0 )
1772 destination_image=DestroyImage(destination_image);
1773 return(MagickFalse);
1775 width=geometry_info.rho;
1776 height=geometry_info.sigma;
1777 blur.x1=geometry_info.rho;
1780 blur.y2=geometry_info.sigma;
1783 if ((flags & HeightValue) == 0)
1785 if ((flags & XValue) != 0 )
1790 angle=DegreesToRadians(geometry_info.xi);
1791 blur.x1=width*cos(angle);
1792 blur.x2=width*sin(angle);
1793 blur.y1=(-height*sin(angle));
1794 blur.y2=height*cos(angle);
1796 if ((flags & YValue) != 0 )
1798 angle_start=DegreesToRadians(geometry_info.xi);
1799 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1802 Blur Image by resampling.
1805 exception=(&image->exception);
1806 resample_filter=AcquireResampleFilter(image,&image->exception);
1807 SetResampleFilter(resample_filter,CubicFilter,2.0);
1808 destination_view=AcquireCacheView(destination_image);
1809 composite_view=AcquireCacheView(composite_image);
1810 for (y=0; y < (ssize_t) composite_image->rows; y++)
1815 register const Quantum
1824 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1826 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1828 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1829 destination_image->columns,1,&image->exception);
1830 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1832 for (x=0; x < (ssize_t) composite_image->columns; x++)
1834 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1836 p+=GetPixelChannels(composite_image);
1839 if (fabs(angle_range) > MagickEpsilon)
1844 angle=angle_start+angle_range*QuantumScale*
1845 GetPixelBlue(composite_image,p);
1846 blur.x1=width*cos(angle);
1847 blur.x2=width*sin(angle);
1848 blur.y1=(-height*sin(angle));
1849 blur.y2=height*cos(angle);
1851 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
1852 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
1853 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
1854 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
1855 GetPixelGreen(composite_image,p));
1856 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1857 (double) y_offset+y,&pixel);
1858 SetPixelPixelInfo(destination_image,&pixel,q);
1859 p+=GetPixelChannels(composite_image);
1860 q+=GetPixelChannels(destination_image);
1862 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1863 if (sync == MagickFalse)
1866 resample_filter=DestroyResampleFilter(resample_filter);
1867 composite_view=DestroyCacheView(composite_view);
1868 destination_view=DestroyCacheView(destination_view);
1869 composite_image=destination_image;
1872 case DisplaceCompositeOp:
1873 case DistortCompositeOp:
1892 Displace/Distort based on overlay gradient map:
1893 X = red_channel; Y = green_channel;
1894 compose:args = x_scale[,y_scale[,center.x,center.y]]
1896 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1898 if (destination_image == (Image *) NULL)
1899 return(MagickFalse);
1900 SetGeometryInfo(&geometry_info);
1902 value=GetImageArtifact(composite_image,"compose:args");
1903 if (value != (char *) NULL)
1904 flags=ParseGeometry(value,&geometry_info);
1905 if ((flags & (WidthValue|HeightValue)) == 0 )
1907 if ((flags & AspectValue) == 0)
1909 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1911 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1915 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1916 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1921 horizontal_scale=geometry_info.rho;
1922 vertical_scale=geometry_info.sigma;
1923 if ((flags & PercentValue) != 0)
1925 if ((flags & AspectValue) == 0)
1927 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1928 vertical_scale*=(composite_image->rows-1.0)/200.0;
1932 horizontal_scale*=(image->columns-1.0)/200.0;
1933 vertical_scale*=(image->rows-1.0)/200.0;
1936 if ((flags & HeightValue) == 0)
1937 vertical_scale=horizontal_scale;
1940 Determine fixed center point for absolute distortion map
1942 Displace offset relative to a fixed absolute point
1943 Select that point according to +X+Y user inputs.
1944 default = center of overlay image
1945 arg flag '!' = locations/percentage relative to background image
1947 center.x=(MagickRealType) x_offset;
1948 center.y=(MagickRealType) y_offset;
1949 if (compose == DistortCompositeOp)
1951 if ((flags & XValue) == 0)
1952 if ((flags & AspectValue) == 0)
1953 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
1956 center.x=((MagickRealType) image->columns-1)/2.0;
1958 if ((flags & AspectValue) == 0)
1959 center.x=(MagickRealType) x_offset+geometry_info.xi;
1961 center.x=geometry_info.xi;
1962 if ((flags & YValue) == 0)
1963 if ((flags & AspectValue) == 0)
1964 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
1966 center.y=((MagickRealType) image->rows-1)/2.0;
1968 if ((flags & AspectValue) == 0)
1969 center.y=(MagickRealType) y_offset+geometry_info.psi;
1971 center.y=geometry_info.psi;
1974 Shift the pixel offset point as defined by the provided,
1975 displacement/distortion map. -- Like a lens...
1978 exception=(&image->exception);
1979 image_view=AcquireCacheView(image);
1980 destination_view=AcquireCacheView(destination_image);
1981 composite_view=AcquireCacheView(composite_image);
1982 for (y=0; y < (ssize_t) composite_image->rows; y++)
1987 register const Quantum
1996 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1998 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
2000 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
2001 destination_image->columns,1,&image->exception);
2002 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2004 for (x=0; x < (ssize_t) composite_image->columns; x++)
2006 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
2008 p+=GetPixelChannels(composite_image);
2012 Displace the offset.
2014 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
2015 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2016 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
2018 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
2019 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2020 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
2022 (void) InterpolatePixelInfo(image,image_view,
2023 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2026 Mask with the 'invalid pixel mask' in alpha channel.
2028 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
2029 pixel.alpha)*(1.0-QuantumScale*
2030 GetPixelAlpha(composite_image,p)));
2031 SetPixelPixelInfo(destination_image,&pixel,q);
2032 p+=GetPixelChannels(composite_image);
2033 q+=GetPixelChannels(destination_image);
2035 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
2036 if (sync == MagickFalse)
2039 destination_view=DestroyCacheView(destination_view);
2040 composite_view=DestroyCacheView(composite_view);
2041 image_view=DestroyCacheView(image_view);
2042 composite_image=destination_image;
2045 case DissolveCompositeOp:
2048 Geometry arguments to dissolve factors.
2050 value=GetImageArtifact(composite_image,"compose:args");
2051 if (value != (char *) NULL)
2053 flags=ParseGeometry(value,&geometry_info);
2054 source_dissolve=geometry_info.rho/100.0;
2055 destination_dissolve=1.0;
2056 if ((source_dissolve-MagickEpsilon) < 0.0)
2057 source_dissolve=0.0;
2058 if ((source_dissolve+MagickEpsilon) > 1.0)
2060 destination_dissolve=2.0-source_dissolve;
2061 source_dissolve=1.0;
2063 if ((flags & SigmaValue) != 0)
2064 destination_dissolve=geometry_info.sigma/100.0;
2065 if ((destination_dissolve-MagickEpsilon) < 0.0)
2066 destination_dissolve=0.0;
2067 modify_outside_overlay=MagickTrue;
2068 if ((destination_dissolve+MagickEpsilon) > 1.0 )
2070 destination_dissolve=1.0;
2071 modify_outside_overlay=MagickFalse;
2076 case BlendCompositeOp:
2078 value=GetImageArtifact(composite_image,"compose:args");
2079 if (value != (char *) NULL)
2081 flags=ParseGeometry(value,&geometry_info);
2082 source_dissolve=geometry_info.rho/100.0;
2083 destination_dissolve=1.0-source_dissolve;
2084 if ((flags & SigmaValue) != 0)
2085 destination_dissolve=geometry_info.sigma/100.0;
2086 modify_outside_overlay=MagickTrue;
2087 if ((destination_dissolve+MagickEpsilon) > 1.0)
2088 modify_outside_overlay=MagickFalse;
2092 case MathematicsCompositeOp:
2095 Just collect the values from "compose:args", setting.
2096 Unused values are set to zero automagically.
2098 Arguments are normally a comma separated list, so this probably should
2099 be changed to some 'general comma list' parser, (with a minimum
2102 SetGeometryInfo(&geometry_info);
2103 value=GetImageArtifact(composite_image,"compose:args");
2104 if (value != (char *) NULL)
2105 (void) ParseGeometry(value,&geometry_info);
2108 case ModulateCompositeOp:
2111 Determine the brightness and saturation scale.
2113 value=GetImageArtifact(composite_image,"compose:args");
2114 if (value != (char *) NULL)
2116 flags=ParseGeometry(value,&geometry_info);
2117 percent_brightness=geometry_info.rho;
2118 if ((flags & SigmaValue) != 0)
2119 percent_saturation=geometry_info.sigma;
2123 case ThresholdCompositeOp:
2126 Determine the amount and threshold.
2128 value=GetImageArtifact(composite_image,"compose:args");
2129 if (value != (char *) NULL)
2131 flags=ParseGeometry(value,&geometry_info);
2132 amount=geometry_info.rho;
2133 threshold=geometry_info.sigma;
2134 if ((flags & SigmaValue) == 0)
2137 threshold*=QuantumRange;
2143 value=GetImageArtifact(composite_image,"compose:outside-overlay");
2144 if (value != (const char *) NULL)
2145 modify_outside_overlay=IsMagickTrue(value);
2151 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2152 GetPixelInfo(composite_image,&zero);
2153 exception=(&image->exception);
2154 image_view=AcquireCacheView(image);
2155 composite_view=AcquireCacheView(composite_image);
2156 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2157 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2159 for (y=0; y < (ssize_t) image->rows; y++)
2174 register const Quantum
2183 if (status == MagickFalse)
2185 if (modify_outside_overlay == MagickFalse)
2189 if ((y-y_offset) >= (ssize_t) composite_image->rows)
2193 If pixels is NULL, y is outside overlay region.
2195 pixels=(Quantum *) NULL;
2197 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
2199 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
2200 composite_image->columns,1,exception);
2201 if (p == (const Quantum *) NULL)
2208 p-=x_offset*GetPixelChannels(composite_image);
2210 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2211 if (q == (const Quantum *) NULL)
2221 for (x=0; x < (ssize_t) image->columns; x++)
2223 if (modify_outside_overlay == MagickFalse)
2227 q+=GetPixelChannels(image);
2230 if ((x-x_offset) >= (ssize_t) composite_image->columns)
2233 destination.red=(MagickRealType) GetPixelRed(image,q);
2234 destination.green=(MagickRealType) GetPixelGreen(image,q);
2235 destination.blue=(MagickRealType) GetPixelBlue(image,q);
2236 if (image->colorspace == CMYKColorspace)
2237 destination.black=(MagickRealType) GetPixelBlack(image,q);
2238 if (image->colorspace == CMYKColorspace)
2240 destination.red=(MagickRealType) QuantumRange-destination.red;
2241 destination.green=(MagickRealType) QuantumRange-destination.green;
2242 destination.blue=(MagickRealType) QuantumRange-destination.blue;
2243 destination.black=(MagickRealType) QuantumRange-destination.black;
2245 if (image->matte != MagickFalse)
2246 destination.alpha=(MagickRealType) GetPixelAlpha(image,q);
2248 Handle destination modifications outside overlaid region.
2250 composite=destination;
2251 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2252 ((x-x_offset) >= (ssize_t) composite_image->columns))
2256 case DissolveCompositeOp:
2257 case BlendCompositeOp:
2259 composite.alpha=destination_dissolve*(composite.alpha);
2262 case ClearCompositeOp:
2263 case SrcCompositeOp:
2265 CompositeClear(&destination,&composite);
2269 case SrcInCompositeOp:
2270 case OutCompositeOp:
2271 case SrcOutCompositeOp:
2272 case DstInCompositeOp:
2273 case DstAtopCompositeOp:
2274 case CopyOpacityCompositeOp:
2275 case ChangeMaskCompositeOp:
2277 composite.alpha=(MagickRealType) TransparentAlpha;
2282 (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,y-
2283 y_offset,&composite,exception);
2287 if (image->colorspace == CMYKColorspace)
2289 composite.red=(MagickRealType) QuantumRange-composite.red;
2290 composite.green=(MagickRealType) QuantumRange-composite.green;
2291 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2292 composite.black=(MagickRealType) QuantumRange-composite.black;
2294 SetPixelRed(image,ClampToQuantum(composite.red),q);
2295 SetPixelGreen(image,ClampToQuantum(composite.green),q);
2296 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2297 if (image->matte != MagickFalse)
2298 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2299 if (image->colorspace == CMYKColorspace)
2300 SetPixelBlack(image,ClampToQuantum(composite.black),q);
2301 q+=GetPixelChannels(image);
2305 Handle normal overlay of source onto destination.
2307 source.red=(MagickRealType) GetPixelRed(composite_image,p);
2308 source.green=(MagickRealType) GetPixelGreen(composite_image,p);
2309 source.blue=(MagickRealType) GetPixelBlue(composite_image,p);
2310 if (composite_image->colorspace == CMYKColorspace)
2311 source.black=(MagickRealType) GetPixelBlack(composite_image,p);
2312 if (composite_image->colorspace == CMYKColorspace)
2314 source.red=(MagickRealType) QuantumRange-source.red;
2315 source.green=(MagickRealType) QuantumRange-source.green;
2316 source.blue=(MagickRealType) QuantumRange-source.blue;
2317 source.black=(MagickRealType) QuantumRange-source.black;
2319 if (composite_image->matte != MagickFalse)
2320 source.alpha=(MagickRealType) GetPixelAlpha(composite_image,p);
2322 Porter-Duff compositions.
2326 case ClearCompositeOp:
2328 CompositeClear(&destination,&composite);
2331 case SrcCompositeOp:
2332 case CopyCompositeOp:
2333 case ReplaceCompositeOp:
2339 case DstCompositeOp:
2341 case OverCompositeOp:
2342 case SrcOverCompositeOp:
2344 CompositePixelInfoOver(&source,source.alpha,&destination,
2345 destination.alpha,&composite);
2348 case DstOverCompositeOp:
2350 CompositePixelInfoOver(&destination,destination.alpha,&source,
2351 source.alpha,&composite);
2354 case SrcInCompositeOp:
2357 CompositeIn(&source,&destination,&composite);
2360 case DstInCompositeOp:
2362 CompositeIn(&destination,&source,&composite);
2365 case OutCompositeOp:
2366 case SrcOutCompositeOp:
2368 CompositeOut(&source,&destination,&composite);
2371 case DstOutCompositeOp:
2373 CompositeOut(&destination,&source,&composite);
2376 case AtopCompositeOp:
2377 case SrcAtopCompositeOp:
2379 CompositeAtop(&source,&destination,&composite);
2382 case DstAtopCompositeOp:
2384 CompositeAtop(&destination,&source,&composite);
2387 case XorCompositeOp:
2389 CompositeXor(&source,&destination,&composite);
2392 case PlusCompositeOp:
2394 CompositePlus(&source,&destination,channel,&composite);
2397 case MinusDstCompositeOp:
2399 CompositeMinus(&source,&destination,channel,&composite);
2402 case MinusSrcCompositeOp:
2404 CompositeMinus(&destination,&source,channel,&composite);
2407 case ModulusAddCompositeOp:
2409 CompositeModulusAdd(&source,&destination,channel,&composite);
2412 case ModulusSubtractCompositeOp:
2414 CompositeModulusSubtract(&source,&destination,channel,&composite);
2417 case DifferenceCompositeOp:
2419 CompositeDifference(&source,&destination,channel,&composite);
2422 case ExclusionCompositeOp:
2424 CompositeExclusion(&source,&destination,channel,&composite);
2427 case MultiplyCompositeOp:
2429 CompositeMultiply(&source,&destination,channel,&composite);
2432 case ScreenCompositeOp:
2434 CompositeScreen(&source,&destination,channel,&composite);
2437 case DivideDstCompositeOp:
2439 CompositeDivide(&source,&destination,channel,&composite);
2442 case DivideSrcCompositeOp:
2444 CompositeDivide(&destination,&source,channel,&composite);
2447 case DarkenCompositeOp:
2449 CompositeDarken(&source,&destination,channel,&composite);
2452 case LightenCompositeOp:
2454 CompositeLighten(&source,&destination,channel,&composite);
2457 case DarkenIntensityCompositeOp:
2459 CompositeDarkenIntensity(&source,&destination,channel,&composite);
2462 case LightenIntensityCompositeOp:
2464 CompositeLightenIntensity(&source,&destination,channel,&composite);
2467 case MathematicsCompositeOp:
2469 CompositeMathematics(&source,&destination,channel,&geometry_info,
2473 case ColorDodgeCompositeOp:
2475 CompositeColorDodge(&source,&destination,&composite);
2478 case ColorBurnCompositeOp:
2480 CompositeColorBurn(&source,&destination,&composite);
2483 case LinearDodgeCompositeOp:
2485 CompositeLinearDodge(&source,&destination,&composite);
2488 case LinearBurnCompositeOp:
2490 CompositeLinearBurn(&source,&destination,&composite);
2493 case HardLightCompositeOp:
2495 CompositeHardLight(&source,&destination,&composite);
2498 case OverlayCompositeOp:
2500 CompositeHardLight(&destination,&source,&composite);
2503 case SoftLightCompositeOp:
2505 CompositeSoftLight(&source,&destination,&composite);
2508 case LinearLightCompositeOp:
2510 CompositeLinearLight(&source,&destination,&composite);
2513 case PegtopLightCompositeOp:
2515 CompositePegtopLight(&source,&destination,&composite);
2518 case VividLightCompositeOp:
2520 CompositeVividLight(&source,&destination,&composite);
2523 case PinLightCompositeOp:
2525 CompositePinLight(&source,&destination,&composite);
2528 case ChangeMaskCompositeOp:
2530 if ((composite.alpha > ((MagickRealType) QuantumRange/2.0)) ||
2531 (IsFuzzyEquivalencePixelInfo(&source,&destination) != MagickFalse))
2532 composite.alpha=(MagickRealType) TransparentAlpha;
2534 composite.alpha=(MagickRealType) OpaqueAlpha;
2537 case BumpmapCompositeOp:
2539 if (source.alpha == TransparentAlpha)
2541 CompositeBumpmap(&source,&destination,&composite);
2544 case DissolveCompositeOp:
2546 CompositePixelInfoOver(&source,source_dissolve*source.alpha,
2547 &destination,(MagickRealType) (destination_dissolve*
2548 destination.alpha),&composite);
2551 case BlendCompositeOp:
2553 CompositePixelInfoBlend(&source,source_dissolve,&destination,
2554 destination_dissolve,&composite);
2557 case ThresholdCompositeOp:
2559 CompositeThreshold(&source,&destination,threshold,amount,&composite);
2562 case ModulateCompositeOp:
2567 if (source.alpha == TransparentAlpha)
2569 offset=(ssize_t) (GetPixelInfoIntensity(&source)-midpoint);
2572 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2573 &saturation,&brightness);
2574 brightness+=(0.01*percent_brightness*offset)/midpoint;
2575 saturation*=0.01*percent_saturation;
2576 HSBComposite(hue,saturation,brightness,&composite.red,
2577 &composite.green,&composite.blue);
2580 case HueCompositeOp:
2582 if (source.alpha == TransparentAlpha)
2584 if (destination.alpha == TransparentAlpha)
2589 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2590 &saturation,&brightness);
2591 CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
2592 HSBComposite(hue,saturation,brightness,&composite.red,
2593 &composite.green,&composite.blue);
2594 if (source.alpha < destination.alpha)
2595 composite.alpha=source.alpha;
2598 case SaturateCompositeOp:
2600 if (source.alpha == TransparentAlpha)
2602 if (destination.alpha == TransparentAlpha)
2607 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2608 &saturation,&brightness);
2609 CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
2611 HSBComposite(hue,saturation,brightness,&composite.red,
2612 &composite.green,&composite.blue);
2613 if (source.alpha < destination.alpha)
2614 composite.alpha=source.alpha;
2617 case LuminizeCompositeOp:
2619 if (source.alpha == TransparentAlpha)
2621 if (destination.alpha == TransparentAlpha)
2626 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2627 &saturation,&brightness);
2628 CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
2630 HSBComposite(hue,saturation,brightness,&composite.red,
2631 &composite.green,&composite.blue);
2632 if (source.alpha < destination.alpha)
2633 composite.alpha=source.alpha;
2636 case ColorizeCompositeOp:
2638 if (source.alpha == TransparentAlpha)
2640 if (destination.alpha == TransparentAlpha)
2645 CompositeHSB(destination.red,destination.green,destination.blue,&sans,
2647 CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
2649 HSBComposite(hue,saturation,brightness,&composite.red,
2650 &composite.green,&composite.blue);
2651 if (source.alpha < destination.alpha)
2652 composite.alpha=source.alpha;
2655 case CopyRedCompositeOp:
2656 case CopyCyanCompositeOp:
2658 composite.red=source.red;
2661 case CopyGreenCompositeOp:
2662 case CopyMagentaCompositeOp:
2664 composite.green=source.green;
2667 case CopyBlueCompositeOp:
2668 case CopyYellowCompositeOp:
2670 composite.blue=source.blue;
2673 case CopyOpacityCompositeOp:
2675 if (source.matte == MagickFalse)
2677 composite.alpha=(MagickRealType) GetPixelInfoIntensity(&source);
2680 composite.alpha=source.alpha;
2683 case CopyBlackCompositeOp:
2685 if (source.colorspace != CMYKColorspace)
2686 ConvertRGBToCMYK(&source);
2687 composite.black=source.black;
2690 case BlurCompositeOp:
2691 case DisplaceCompositeOp:
2692 case DistortCompositeOp:
2700 if (image->colorspace == CMYKColorspace)
2702 composite.red=(MagickRealType) QuantumRange-composite.red;
2703 composite.green=(MagickRealType) QuantumRange-composite.green;
2704 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2705 composite.black=(MagickRealType) QuantumRange-composite.black;
2707 SetPixelRed(image,ClampToQuantum(composite.red),q);
2708 SetPixelGreen(image,ClampToQuantum(composite.green),q);
2709 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2710 if (image->colorspace == CMYKColorspace)
2711 SetPixelBlack(image,ClampToQuantum(composite.black),q);
2712 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2713 p+=GetPixelChannels(composite_image);
2714 if (p >= (pixels+composite_image->columns*GetPixelChannels(composite_image)))
2716 q+=GetPixelChannels(image);
2718 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2720 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2725 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2726 #pragma omp critical (MagickCore_CompositeImageChannel)
2728 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2730 if (proceed == MagickFalse)
2734 composite_view=DestroyCacheView(composite_view);
2735 image_view=DestroyCacheView(image_view);
2736 if (destination_image != (Image * ) NULL)
2737 destination_image=DestroyImage(destination_image);
2742 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2746 % T e x t u r e I m a g e %
2750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2752 % TextureImage() repeatedly tiles the texture image across and down the image
2755 % The format of the TextureImage method is:
2757 % MagickBooleanType TextureImage(Image *image,const Image *texture)
2759 % A description of each parameter follows:
2761 % o image: the image.
2763 % o texture: This image is the texture to layer on the background.
2766 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
2768 #define TextureImageTag "Texture/Image"
2783 assert(image != (Image *) NULL);
2784 if (image->debug != MagickFalse)
2785 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2786 assert(image->signature == MagickSignature);
2787 if (texture == (const Image *) NULL)
2788 return(MagickFalse);
2789 (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
2790 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2791 return(MagickFalse);
2793 if ((image->compose != CopyCompositeOp) &&
2794 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2795 (texture->matte != MagickFalse)))
2798 Tile texture onto the image background.
2800 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2801 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2803 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
2808 if (status == MagickFalse)
2810 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2815 thread_status=CompositeImage(image,image->compose,texture,x+
2816 texture->tile_offset.x,y+texture->tile_offset.y);
2817 if (thread_status == MagickFalse)
2819 status=thread_status;
2823 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2828 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2829 #pragma omp critical (MagickCore_TextureImage)
2831 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2833 if (proceed == MagickFalse)
2837 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2838 image->rows,image->rows);
2842 Tile texture onto the image background (optimized).
2845 exception=(&image->exception);
2846 image_view=AcquireCacheView(image);
2847 texture_view=AcquireCacheView(texture);
2848 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2849 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2851 for (y=0; y < (ssize_t) image->rows; y++)
2856 register const Quantum
2869 if (status == MagickFalse)
2871 pixels=GetCacheViewVirtualPixels(texture_view,texture->tile_offset.x,(y+
2872 texture->tile_offset.y) % texture->rows,texture->columns,1,exception);
2873 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2875 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2880 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2886 width=texture->columns;
2887 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2888 width=image->columns-x;
2889 for (i=0; i < (ssize_t) width; i++)
2891 SetPixelRed(image,GetPixelRed(texture,p),q);
2892 SetPixelGreen(image,GetPixelGreen(texture,p),q);
2893 SetPixelBlue(image,GetPixelBlue(texture,p),q);
2894 SetPixelAlpha(image,GetPixelAlpha(texture,p),q);
2895 if ((image->colorspace == CMYKColorspace) &&
2896 (texture->colorspace == CMYKColorspace))
2897 SetPixelBlack(image,GetPixelBlack(texture,p),q);
2898 p+=GetPixelChannels(texture);
2899 q+=GetPixelChannels(image);
2902 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2903 if (sync == MagickFalse)
2905 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2910 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2911 #pragma omp critical (MagickCore_TextureImage)
2913 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2915 if (proceed == MagickFalse)
2919 texture_view=DestroyCacheView(texture_view);
2920 image_view=DestroyCacheView(image_view);