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-2010 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 "magick/studio.h"
44 #include "magick/artifact.h"
45 #include "magick/cache-view.h"
46 #include "magick/client.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/colorspace.h"
50 #include "magick/colorspace-private.h"
51 #include "magick/composite.h"
52 #include "magick/composite-private.h"
53 #include "magick/constitute.h"
54 #include "magick/draw.h"
55 #include "magick/fx.h"
56 #include "magick/gem.h"
57 #include "magick/geometry.h"
58 #include "magick/image.h"
59 #include "magick/image-private.h"
60 #include "magick/list.h"
61 #include "magick/log.h"
62 #include "magick/monitor.h"
63 #include "magick/monitor-private.h"
64 #include "magick/memory_.h"
65 #include "magick/option.h"
66 #include "magick/pixel-private.h"
67 #include "magick/property.h"
68 #include "magick/quantum.h"
69 #include "magick/resample.h"
70 #include "magick/resource_.h"
71 #include "magick/string_.h"
72 #include "magick/utility.h"
73 #include "magick/version.h"
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80 % C o m p o s i t e I m a g e C h a n n e l %
84 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 % CompositeImageChannel() returns the second image composited onto the first
87 % at the specified offset, using the specified composite method.
89 % The format of the CompositeImageChannel method is:
91 % MagickBooleanType CompositeImage(Image *image,
92 % const CompositeOperator compose,Image *composite_image,
93 % const ssize_t x_offset,const ssize_t y_offset)
94 % MagickBooleanType CompositeImageChannel(Image *image,
95 % const ChannelType channel,const CompositeOperator compose,
96 % Image *composite_image,const ssize_t x_offset,const ssize_t y_offset)
98 % A description of each parameter follows:
100 % o image: the destination image, modified by he composition
102 % o channel: the channel.
104 % o compose: This operator affects how the composite is applied to
105 % the image. The operators and how they are utilized are listed here
106 % http://www.w3.org/TR/SVG12/#compositing.
108 % o composite_image: the composite (source) image.
110 % o x_offset: the column offset of the composited image.
112 % o y_offset: the row offset of the composited image.
114 % Extra Controls from Image meta-data in 'composite_image' (artifacts)
117 % A string containing extra numerical arguments for specific compose
118 % methods, generally expressed as a 'geometry' or a comma separated list
121 % Compose methods needing such arguments include "BlendCompositeOp" and
122 % "DisplaceCompositeOp".
124 % o "compose:outside-overlay"
125 % Modify how the composition is to effect areas not directly covered
126 % by the 'composite_image' at the offset given. Normally this is
127 % dependant on the 'compose' method, especially Duff-Porter methods.
129 % If set to "false" then disable all normal handling of pixels not
130 % covered by the composite_image. Typically used for repeated tiling
131 % of the composite_image by the calling API.
133 % Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
137 static inline double MagickMin(const double x,const double y)
143 static inline double MagickMax(const double x,const double y)
151 ** Programmers notes on SVG specification.
153 ** A Composition is defined by...
154 ** Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
155 ** Blending areas : X = 1 for area of overlap ie: f(Sc,Dc)
156 ** Y = 1 for source preserved
157 ** Z = 1 for destination preserved
159 ** Conversion to transparency (then optimized)
160 ** Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
161 ** Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
164 ** Sca = Sc*Sa normalized Source color divided by Source alpha
165 ** Dca = Dc*Da normalized Dest color divided by Dest alpha
166 ** Dc' = Dca'/Da' the desired color value for this channel.
168 ** Da' in in the follow formula as 'gamma' The resulting alpla value.
171 ** Most functions use a blending mode of over (X=1,Y=1,Z=1)
172 ** this results in the following optimizations...
173 ** gamma = Sa+Da-Sa*Da;
174 ** gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
175 ** opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
177 ** The above SVG definitions also definate that Mathematical Composition
178 ** methods should use a 'Over' blending mode for Alpha Channel.
179 ** It however was not applied for composition modes of 'Plus', 'Minus',
180 ** 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
190 ** 'ModulusSubtract'.
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 MagickPixelPacket *p,
208 const MagickPixelPacket *q,MagickPixelPacket *composite)
213 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
214 composite->opacity=q->opacity; /* 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->index=Atop(p->index,Sa,q->index,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 MagickPixelPacket *p,
227 const MagickPixelPacket *q,MagickPixelPacket *composite)
232 intensity=MagickPixelIntensity(p);
233 composite->red=QuantumScale*intensity*q->red;
234 composite->green=QuantumScale*intensity*q->green;
235 composite->blue=QuantumScale*intensity*q->blue;
236 composite->opacity=(MagickRealType) QuantumScale*intensity*
237 GetOpacityPixelComponent(p);
238 if (q->colorspace == CMYKColorspace)
239 composite->index=QuantumScale*intensity*q->index;
242 static inline void CompositeClear(const MagickPixelPacket *q,
243 MagickPixelPacket *composite)
245 composite->opacity=(MagickRealType) TransparentOpacity;
247 composite->green=0.0;
249 if (q->colorspace == CMYKColorspace)
250 composite->index=0.0;
253 static MagickRealType ColorBurn(const MagickRealType Sca,
254 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
258 Oct 2004 SVG specification.
260 if (Sca*Da + Dca*Sa <= Sa*Da)
261 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
262 return(Sa*(Sca*Da+Dca*Sa-Sa*Da)/Sca + Sca*(1.0-Da) + Dca*(1.0-Sa));
265 March 2009 SVG specification.
267 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
268 return(Sa*Da+Dca*(1.0-Sa));
269 if (Sca < MagickEpsilon)
270 return(Dca*(1.0-Sa));
271 return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
275 static inline void CompositeColorBurn(const MagickPixelPacket *p,
276 const MagickPixelPacket *q,MagickPixelPacket *composite)
283 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
284 Da=1.0-QuantumScale*q->opacity;
285 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
286 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
287 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
288 composite->red=gamma*ColorBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
290 composite->green=gamma*ColorBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
292 composite->blue=gamma*ColorBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
294 if (q->colorspace == CMYKColorspace)
295 composite->index=gamma*ColorBurn(QuantumScale*p->index*Sa,Sa,QuantumScale*
300 static MagickRealType ColorDodge(const MagickRealType Sca,
301 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
305 Oct 2004 SVG specification.
307 if ((Sca*Da+Dca*Sa) >= Sa*Da)
308 return( Sa*Da + Sca*(1.0-Da) + Dca*(1.0-Sa) );
309 return( Dca*Sa*Sa/(Sa-Sca) + Sca*(1.0-Da) + Dca*(1.0-Sa) );
313 New specification, March 2009 SVG specification. This specification was
314 also wrong of non-overlap cases.
316 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
317 return(Sca*(1.0-Da));
318 if (fabs(Sca-Sa) < MagickEpsilon)
319 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
320 return(Sa*MagickMin(Da,Dca*Sa/(Sa-Sca)));
323 Working from first principles using the original formula:
327 This works correctly! Looks like the 2004 model was right but just
328 required a extra condition for correct handling.
330 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
331 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
332 if (fabs(Sca-Sa) < MagickEpsilon)
333 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
334 return(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
337 static inline void CompositeColorDodge(const MagickPixelPacket *p,
338 const MagickPixelPacket *q,MagickPixelPacket *composite)
345 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
346 Da=1.0-QuantumScale*q->opacity;
347 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
348 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
349 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
350 composite->red=gamma*ColorDodge(QuantumScale*p->red*Sa,Sa,QuantumScale*
352 composite->green=gamma*ColorDodge(QuantumScale*p->green*Sa,Sa,QuantumScale*
354 composite->blue=gamma*ColorDodge(QuantumScale*p->blue*Sa,Sa,QuantumScale*
356 if (q->colorspace == CMYKColorspace)
357 composite->index=gamma*ColorDodge(QuantumScale*p->index*Sa,Sa,QuantumScale*
361 static inline MagickRealType Darken(const MagickRealType p,
362 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
365 return(MagickOver_(p,alpha,q,beta)); /* src-over */
366 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
369 static inline void CompositeDarken(const MagickPixelPacket *p,
370 const MagickPixelPacket *q,const ChannelType channel,
371 MagickPixelPacket *composite)
374 Darken is equivelent to a 'Minimum' method
375 OR a greyscale version of a binary 'Or'
376 OR the 'Intersection' of pixel sets.
381 if ( (channel & SyncChannels) != 0 ) {
382 composite->opacity=QuantumScale*p->opacity*q->opacity; /* Over Blend */
383 gamma=1.0-QuantumScale*composite->opacity;
384 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
385 composite->red=gamma*Darken(p->red,p->opacity,q->red,q->opacity);
386 composite->green=gamma*Darken(p->green,p->opacity,q->green,q->opacity);
387 composite->blue=gamma*Darken(p->blue,p->opacity,q->blue,q->opacity);
388 if (q->colorspace == CMYKColorspace)
389 composite->index=gamma*Darken(p->index,p->opacity,q->index,q->opacity);
391 else { /* handle channels as separate grayscale channels */
392 if ( (channel & AlphaChannel) != 0 )
393 composite->opacity=MagickMax(p->opacity,q->opacity);
394 if ( (channel & RedChannel) != 0 )
395 composite->red=MagickMin(p->red,q->red);
396 if ( (channel & GreenChannel) != 0 )
397 composite->green=MagickMin(p->green,q->green);
398 if ( (channel & BlueChannel) != 0 )
399 composite->blue=MagickMin(p->blue,q->blue);
400 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
401 composite->index=MagickMin(p->index,q->index);
405 static inline MagickRealType Difference(const MagickRealType p,
406 const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
408 /* Optimized by Multipling by QuantumRange (taken from gamma). */
409 return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q));
412 static inline void CompositeDifference(const MagickPixelPacket *p,
413 const MagickPixelPacket *q,const ChannelType channel,
414 MagickPixelPacket *composite)
421 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
422 Da=1.0-QuantumScale*q->opacity;
423 if ( (channel & SyncChannels) != 0 ) {
424 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
425 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
426 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
427 /* Values are not normalized as an optimization. */
428 composite->red=gamma*Difference(p->red,Sa,q->red,Da);
429 composite->green=gamma*Difference(p->green,Sa,q->green,Da);
430 composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da);
431 if (q->colorspace == CMYKColorspace)
432 composite->index=gamma*Difference(p->index,Sa,q->index,Da);
434 else { /* handle channels as separate grayscale channels */
435 if ( (channel & AlphaChannel) != 0 )
436 composite->opacity=QuantumRange-fabs(p->opacity - q->opacity);
437 if ( (channel & RedChannel) != 0 )
438 composite->red=fabs(p->red - q->red);
439 if ( (channel & GreenChannel) != 0 )
440 composite->green=fabs(p->green - q->green);
441 if ( (channel & BlueChannel) != 0 )
442 composite->blue=fabs(p->blue - q->blue);
443 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
444 composite->index=fabs(p->index - q->index);
448 static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
449 const MagickRealType Dca,const MagickRealType Da)
456 But with appropriate handling for special case of Dc == 0 specifically
457 so that f(Black,Black)=Black and f(non-Black,Black)=White.
458 It is however also important to correctly do 'over' alpha blending which
459 is why the formula becomes so complex looking.
461 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
462 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
463 if (fabs(Dca) < MagickEpsilon)
464 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
465 return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
468 static inline void CompositeDivide(const MagickPixelPacket *p,
469 const MagickPixelPacket *q,const ChannelType channel,
470 MagickPixelPacket *composite)
477 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
478 Da=1.0-QuantumScale*q->opacity;
479 if ( (channel & SyncChannels) != 0 ) {
480 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
481 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
482 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
483 composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale*
485 composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale*
487 composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale*
489 if (q->colorspace == CMYKColorspace)
490 composite->index=gamma*Divide(QuantumScale*p->index*Sa,Sa,QuantumScale*
493 else { /* handle channels as separate grayscale channels */
494 if ( (channel & AlphaChannel) != 0 )
495 composite->opacity=QuantumRange*(1.0-Divide(Sa,1.0,Da,1.0));
496 if ( (channel & RedChannel) != 0 )
497 composite->red=QuantumRange*
498 Divide(QuantumScale*p->red,1.0,QuantumScale*q->red,1.0);
499 if ( (channel & GreenChannel) != 0 )
500 composite->green=QuantumRange*
501 Divide(QuantumScale*p->green,1.0,QuantumScale*q->green,1.0);
502 if ( (channel & BlueChannel) != 0 )
503 composite->blue=QuantumRange*
504 Divide(QuantumScale*p->blue,1.0,QuantumScale*q->blue,1.0);
505 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
506 composite->index=QuantumRange*
507 Divide(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0);
511 static MagickRealType Exclusion(const MagickRealType Sca,
512 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
514 return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
517 static inline void CompositeExclusion(const MagickPixelPacket *p,
518 const MagickPixelPacket *q,const ChannelType channel,
519 MagickPixelPacket *composite)
526 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
527 Da=1.0-QuantumScale*q->opacity;
528 if ( (channel & SyncChannels) != 0 ) {
529 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
530 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
531 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
532 composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale*
534 composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale*
536 composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale*
538 if (q->colorspace == CMYKColorspace)
539 composite->index=gamma*Exclusion(QuantumScale*p->index*Sa,Sa,QuantumScale*
542 else { /* handle channels as separate grayscale channels */
543 if ( (channel & AlphaChannel) != 0 )
544 composite->opacity=QuantumRange*(1.0-Exclusion(Sa,1.0,Da,1.0));
545 if ( (channel & RedChannel) != 0 )
546 composite->red=QuantumRange*
547 Exclusion(QuantumScale*p->red,1.0,QuantumScale*q->red,1.0);
548 if ( (channel & GreenChannel) != 0 )
549 composite->green=QuantumRange*
550 Exclusion(QuantumScale*p->green,1.0,QuantumScale*q->green,1.0);
551 if ( (channel & BlueChannel) != 0 )
552 composite->blue=QuantumRange*
553 Exclusion(QuantumScale*p->blue,1.0,QuantumScale*q->blue,1.0);
554 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
555 composite->index=QuantumRange*
556 Exclusion(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0);
560 static MagickRealType HardLight(const MagickRealType Sca,
561 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
564 return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
565 return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
568 static inline void CompositeHardLight(const MagickPixelPacket *p,
569 const MagickPixelPacket *q,MagickPixelPacket *composite)
576 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
577 Da=1.0-QuantumScale*q->opacity;
578 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
579 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
580 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
581 composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
583 composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
585 composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
587 if (q->colorspace == CMYKColorspace)
588 composite->index=gamma*HardLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
592 static void CompositeHSB(const MagickRealType red,const MagickRealType green,
593 const MagickRealType blue,double *hue,double *saturation,double *brightness)
601 Convert RGB to HSB colorspace.
603 assert(hue != (double *) NULL);
604 assert(saturation != (double *) NULL);
605 assert(brightness != (double *) NULL);
606 max=(red > green ? red : green);
609 min=(red < green ? red : green);
614 *brightness=(double) (QuantumScale*max);
617 *saturation=(double) (1.0-min/max);
622 *hue=(double) ((green-blue)/delta);
625 *hue=(double) (2.0+(blue-red)/delta);
628 *hue=(double) (4.0+(red-green)/delta);
634 static inline MagickRealType In(const MagickRealType p,
635 const MagickRealType Sa,const MagickRealType magick_unused(q),
636 const MagickRealType Da)
641 static inline void CompositeIn(const MagickPixelPacket *p,
642 const MagickPixelPacket *q,MagickPixelPacket *composite)
649 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
650 Da=1.0-QuantumScale*q->opacity;
652 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
653 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
654 composite->red=gamma*In(p->red,Sa,q->red,Da);
655 composite->green=gamma*In(p->green,Sa,q->green,Da);
656 composite->blue=gamma*In(p->blue,Sa,q->blue,Da);
657 if (q->colorspace == CMYKColorspace)
658 composite->index=gamma*In(p->index,Sa,q->index,Da);
661 static inline MagickRealType Lighten(const MagickRealType p,
662 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
665 return(MagickOver_(p,alpha,q,beta)); /* src-over */
666 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
669 static inline void CompositeLighten(const MagickPixelPacket *p,
670 const MagickPixelPacket *q,const ChannelType channel,
671 MagickPixelPacket *composite)
674 Lighten is also equivelevt to a 'Maximum' method
675 OR a greyscale version of a binary 'And'
676 OR the 'Union' of pixel sets.
681 if ( (channel & SyncChannels) != 0 ) {
682 composite->opacity=QuantumScale*p->opacity*q->opacity; /* Over Blend */
683 gamma=1.0-QuantumScale*composite->opacity;
684 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
685 composite->red=gamma*Lighten(p->red,p->opacity,q->red,q->opacity);
686 composite->green=gamma*Lighten(p->green,p->opacity,q->green,q->opacity);
687 composite->blue=gamma*Lighten(p->blue,p->opacity,q->blue,q->opacity);
688 if (q->colorspace == CMYKColorspace)
689 composite->index=gamma*Lighten(p->index,p->opacity,q->index,q->opacity);
691 else { /* handle channels as separate grayscale channels */
692 if ( (channel & AlphaChannel) != 0 )
693 composite->opacity=MagickMin(p->opacity,q->opacity);
694 if ( (channel & RedChannel) != 0 )
695 composite->red=MagickMax(p->red,q->red);
696 if ( (channel & GreenChannel) != 0 )
697 composite->green=MagickMax(p->green,q->green);
698 if ( (channel & BlueChannel) != 0 )
699 composite->blue=MagickMax(p->blue,q->blue);
700 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
701 composite->index=MagickMax(p->index,q->index);
706 static inline MagickRealType LinearDodge(const MagickRealType Sca,
707 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
710 LinearDodge: simplifies to a trivial formula
718 static inline void CompositeLinearDodge(const MagickPixelPacket *p,
719 const MagickPixelPacket *q,MagickPixelPacket *composite)
726 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
727 Da=1.0-QuantumScale*q->opacity;
728 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
729 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
730 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
731 composite->red=gamma*(p->red*Sa+q->red*Da);
732 composite->green=gamma*(p->green*Sa+q->green*Da);
733 composite->blue=gamma*(p->blue*Sa+q->blue*Da);
734 if (q->colorspace == CMYKColorspace)
735 composite->index=gamma*(p->index*Sa+q->index*Da);
739 static inline MagickRealType LinearBurn(const MagickRealType Sca,
740 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
743 LinearBurn: as defined by Abode Photoshop, according to
744 http://www.simplefilter.de/en/basics/mixmods.html is:
746 f(Sc,Dc) = Sc + Dc - 1
748 return(Sca+Dca-Sa*Da);
751 static inline void CompositeLinearBurn(const MagickPixelPacket *p,
752 const MagickPixelPacket *q,MagickPixelPacket *composite)
759 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
760 Da=1.0-QuantumScale*q->opacity;
761 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
762 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
763 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
764 composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
766 composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
768 composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
770 if (q->colorspace == CMYKColorspace)
771 composite->index=gamma*LinearBurn(QuantumScale*p->index*Sa,Sa,QuantumScale*
775 static inline MagickRealType LinearLight(const MagickRealType Sca,
776 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
780 Previous formula, was only valid for fully-opaque images.
782 return(Dca+2*Sca-1.0);
785 LinearLight: as defined by Abode Photoshop, according to
786 http://www.simplefilter.de/en/basics/mixmods.html is:
788 f(Sc,Dc) = Dc + 2*Sc - 1
790 return((Sca-Sa)*Da+Sca+Dca);
794 static inline void CompositeLinearLight(const MagickPixelPacket *p,
795 const MagickPixelPacket *q,MagickPixelPacket *composite)
802 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
803 Da=1.0-QuantumScale*q->opacity;
804 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
805 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
806 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
807 composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
809 composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
811 composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
813 if (q->colorspace == CMYKColorspace)
814 composite->index=gamma*LinearLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
818 static inline MagickRealType Mathematics(const MagickRealType Sca,
819 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
820 const GeometryInfo *geometry_info)
823 'Mathematics' a free form user control mathematical composition is defined
826 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
828 Where the arguments A,B,C,D are (currently) passed to composite as
829 a command separated 'geometry' string in "compose:args" image artifact.
831 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
833 Applying the SVG transparency formula (see above), we get...
835 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
837 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
840 return(geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
841 geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
845 static inline void CompositeMathematics(const MagickPixelPacket *p,
846 const MagickPixelPacket *q,const ChannelType channel, const GeometryInfo
847 *args, MagickPixelPacket *composite)
854 Sa=1.0-QuantumScale*GetOpacityPixelComponent(p); /* ??? - AT */
855 Da=1.0-QuantumScale*q->opacity;
856 if ( (channel & SyncChannels) != 0 ) {
857 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
858 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
859 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
860 composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
862 composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,QuantumScale*
863 q->green*Da,Da,args);
864 composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
866 if (q->colorspace == CMYKColorspace)
867 composite->index=gamma*Mathematics(QuantumScale*p->index*Sa,Sa,QuantumScale*
868 q->index*Da,Da,args);
870 else { /* handle channels as separate grayscale channels */
871 if ( (channel & AlphaChannel) != 0 )
872 composite->opacity=QuantumRange*(1.0-Mathematics(Sa,1.0,Da,1.0,args));
873 if ( (channel & RedChannel) != 0 )
874 composite->red=QuantumRange*
875 Mathematics(QuantumScale*p->red,1.0,QuantumScale*q->red,1.0,args);
876 if ( (channel & GreenChannel) != 0 )
877 composite->green=QuantumRange*
878 Mathematics(QuantumScale*p->green,1.0,QuantumScale*q->green,1.0,args);
879 if ( (channel & BlueChannel) != 0 )
880 composite->blue=QuantumRange*
881 Mathematics(QuantumScale*p->blue,1.0,QuantumScale*q->blue,1.0,args);
882 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
883 composite->index=QuantumRange*
884 Mathematics(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0,args);
889 static inline void CompositePlus(const MagickPixelPacket *p,
890 const MagickPixelPacket *q,const ChannelType channel,
891 MagickPixelPacket *composite)
893 if ( (channel & SyncChannels) != 0 ) {
895 NOTE: "Plus" does not use 'over' alpha-blending but uses a
896 special 'plus' form of alph-blending. It is the ONLY mathematical
897 operator to do this. this is what makes it different to the
898 otherwise equivelent "LinearDodge" composition method.
900 Note however that color channels are still effected by the alpha channel
901 as a result of the blending, making it just as useless for independant
902 channel maths, just like all other mathematical composition methods.
904 As such the removal of the 'sync' flag, is still a usful convention.
906 The MagickPixelCompositePlus() function is defined in
907 "composite-private.h" so it can also be used for Image Blending.
909 MagickPixelCompositePlus(p,p->opacity,q,q->opacity,composite);
911 else { /* handle channels as separate grayscale channels */
912 if ( (channel & AlphaChannel) != 0 )
913 composite->opacity=p->opacity+q->opacity-QuantumRange;
914 if ( (channel & RedChannel) != 0 )
915 composite->red=p->red+q->red;
916 if ( (channel & GreenChannel) != 0 )
917 composite->green=p->green+q->green;
918 if ( (channel & BlueChannel) != 0 )
919 composite->blue=p->blue+q->blue;
920 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
921 composite->index=p->index+q->index;
925 static inline MagickRealType Minus(const MagickRealType Sca,
926 const MagickRealType Sa,const MagickRealType Dca,
927 const MagickRealType magick_unused(Da))
929 return(Sca + Dca - 2*Dca*Sa);
932 static inline void CompositeMinus(const MagickPixelPacket *p,
933 const MagickPixelPacket *q,const ChannelType channel,
934 MagickPixelPacket *composite)
941 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
942 Da=1.0-QuantumScale*q->opacity;
943 if ( (channel & SyncChannels) != 0 ) {
944 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
945 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
946 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
947 composite->red=gamma*Minus(p->red*Sa,Sa,q->red*Da,Da);
948 composite->green=gamma*Minus(p->green*Sa,Sa,q->green*Da,Da);
949 composite->blue=gamma*Minus(p->blue*Sa,Sa,q->blue*Da,Da);
950 if (q->colorspace == CMYKColorspace)
951 composite->index=gamma*Minus(p->index*Sa,Sa,q->index*Da,Da);
953 else { /* handle channels as separate grayscale channels */
954 if ( (channel & AlphaChannel) != 0 )
955 composite->opacity=QuantumRange*(1.0-(Sa-Da));
956 if ( (channel & RedChannel) != 0 )
957 composite->red=p->red-q->red;
958 if ( (channel & GreenChannel) != 0 )
959 composite->green=p->green-q->green;
960 if ( (channel & BlueChannel) != 0 )
961 composite->blue=p->blue-q->blue;
962 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
963 composite->index=p->index-q->index;
967 static inline MagickRealType ModulusAdd(const MagickRealType p,
968 const MagickRealType Sa, const MagickRealType q, const MagickRealType Da)
974 if (pixel > QuantumRange)
975 pixel-=(QuantumRange+1.0);
976 return(pixel*Sa*Da + p*Sa*(1-Da) + q*Da*(1-Sa));
979 static inline void CompositeModulusAdd(const MagickPixelPacket *p,
980 const MagickPixelPacket *q, const ChannelType channel,
981 MagickPixelPacket *composite)
983 if ( (channel & SyncChannels) != 0 ) {
989 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
990 Da=1.0-QuantumScale*q->opacity;
991 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
992 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
993 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
994 composite->red=ModulusAdd(p->red,Sa,q->red,Da);
995 composite->green=ModulusAdd(p->green,Sa,q->green,Da);
996 composite->blue=ModulusAdd(p->blue,Sa,q->blue,Da);
997 if (q->colorspace == CMYKColorspace)
998 composite->index=ModulusAdd(p->index,Sa,q->index,Da);
1000 else { /* handle channels as separate grayscale channels */
1001 if ( (channel & AlphaChannel) != 0 )
1002 composite->opacity=QuantumRange-ModulusAdd(QuantumRange-p->opacity,
1003 1.0,QuantumRange-q->opacity,1.0);
1004 if ( (channel & RedChannel) != 0 )
1005 composite->red=ModulusAdd(p->red,1.0,q->red,1.0);
1006 if ( (channel & GreenChannel) != 0 )
1007 composite->green=ModulusAdd(p->green,1.0,q->green,1.0);
1008 if ( (channel & BlueChannel) != 0 )
1009 composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0);
1010 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1011 composite->index=ModulusAdd(p->index,1.0,q->index,1.0);
1015 static inline MagickRealType ModulusSubtract(const MagickRealType p,
1016 const MagickRealType Sa, const MagickRealType q, const MagickRealType Da)
1023 pixel+=(QuantumRange+1.0);
1024 return(pixel*Sa*Da + p*Sa*(1-Da) + q*Da*(1-Sa));
1027 static inline void CompositeModulusSubtract(const MagickPixelPacket *p,
1028 const MagickPixelPacket *q, const ChannelType channel,
1029 MagickPixelPacket *composite)
1031 if ( (channel & SyncChannels) != 0 ) {
1037 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1038 Da=1.0-QuantumScale*q->opacity;
1039 gamma = RoundToUnity(Sa+Da-Sa*Da);
1040 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1041 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1042 composite->red=ModulusSubtract(p->red,Sa,q->red,Da);
1043 composite->green=ModulusSubtract(p->green,Sa,q->green,Da);
1044 composite->blue=ModulusSubtract(p->blue,Sa,q->blue,Da);
1045 if (q->colorspace == CMYKColorspace)
1046 composite->index=ModulusSubtract(p->index,Sa,q->index,Da);
1048 else { /* handle channels as separate grayscale channels */
1049 if ( (channel & AlphaChannel) != 0 )
1050 composite->opacity=QuantumRange-ModulusSubtract(QuantumRange-p->opacity,
1051 1.0,QuantumRange-q->opacity,1.0);
1052 if ( (channel & RedChannel) != 0 )
1053 composite->red=ModulusSubtract(p->red,1.0,q->red,1.0);
1054 if ( (channel & GreenChannel) != 0 )
1055 composite->green=ModulusSubtract(p->green,1.0,q->green,1.0);
1056 if ( (channel & BlueChannel) != 0 )
1057 composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0);
1058 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1059 composite->index=ModulusSubtract(p->index,1.0,q->index,1.0);
1063 static inline MagickRealType Multiply(const MagickRealType Sca,
1064 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1066 return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1069 static inline void CompositeMultiply(const MagickPixelPacket *p,
1070 const MagickPixelPacket *q,const ChannelType channel,
1071 MagickPixelPacket *composite)
1078 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1079 Da=1.0-QuantumScale*q->opacity;
1080 if ( (channel & SyncChannels) != 0 ) {
1081 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1082 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1083 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1084 composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
1086 composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
1088 composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1090 if (q->colorspace == CMYKColorspace)
1091 composite->index=gamma*Multiply(QuantumScale*p->index*Sa,Sa,QuantumScale*
1094 else { /* handle channels as separate grayscale channels */
1095 if ( (channel & AlphaChannel) != 0 )
1096 composite->opacity=QuantumRange*(1.0-Sa*Da);
1097 if ( (channel & RedChannel) != 0 )
1098 composite->red=QuantumScale*p->red*q->red;
1099 if ( (channel & GreenChannel) != 0 )
1100 composite->green=QuantumScale*p->green*q->green;
1101 if ( (channel & BlueChannel) != 0 )
1102 composite->blue=QuantumScale*p->blue*q->blue;
1103 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1104 composite->index=QuantumScale*p->index*q->index;
1108 static inline MagickRealType Out(const MagickRealType p,
1109 const MagickRealType Sa,const MagickRealType magick_unused(q),
1110 const MagickRealType Da)
1112 return(Sa*p*(1.0-Da));
1115 static inline void CompositeOut(const MagickPixelPacket *p,
1116 const MagickPixelPacket *q,MagickPixelPacket *composite)
1123 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1124 Da=1.0-QuantumScale*q->opacity;
1126 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1127 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1128 composite->red=gamma*Out(p->red,Sa,q->red,Da);
1129 composite->green=gamma*Out(p->green,Sa,q->green,Da);
1130 composite->blue=gamma*Out(p->blue,Sa,q->blue,Da);
1131 if (q->colorspace == CMYKColorspace)
1132 composite->index=gamma*Out(p->index,Sa,q->index,Da);
1135 static MagickRealType PegtopLight(const MagickRealType Sca,
1136 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1139 PegTop: A Soft-Light alternative: A continuous version of the Softlight
1140 function, producing very similar results.
1142 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
1144 See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
1146 if (fabs(Da) < MagickEpsilon)
1148 return(Dca*Dca*(Sa-2*Sca)/Da+Sca*(2*Dca+1-Da)+Dca*(1-Sa));
1151 static inline void CompositePegtopLight(const MagickPixelPacket *p,
1152 const MagickPixelPacket *q,MagickPixelPacket *composite)
1159 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1160 Da=1.0-QuantumScale*q->opacity;
1161 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1162 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1163 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1164 composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1166 composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1168 composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1170 if (q->colorspace == CMYKColorspace)
1171 composite->index=gamma*PegtopLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1175 static MagickRealType PinLight(const MagickRealType Sca,
1176 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1179 PinLight: A Photoshop 7 composition method
1180 http://www.simplefilter.de/en/basics/mixmods.html
1182 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
1184 if (Dca*Sa < Da*(2*Sca-Sa))
1185 return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
1186 if ((Dca*Sa) > (2*Sca*Da))
1187 return(Sca*Da+Sca+Dca*(1.0-Sa));
1188 return(Sca*(1.0-Da)+Dca);
1191 static inline void CompositePinLight(const MagickPixelPacket *p,
1192 const MagickPixelPacket *q,MagickPixelPacket *composite)
1199 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1200 Da=1.0-QuantumScale*q->opacity;
1201 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1202 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1203 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1204 composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1206 composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1208 composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1210 if (q->colorspace == CMYKColorspace)
1211 composite->index=gamma*PinLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1215 static inline MagickRealType Screen(const MagickRealType Sca,
1216 const MagickRealType Dca)
1218 /* Screen: A negated multiply
1219 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
1221 return(Sca+Dca-Sca*Dca);
1224 static inline void CompositeScreen(const MagickPixelPacket *p,
1225 const MagickPixelPacket *q,const ChannelType channel,
1226 MagickPixelPacket *composite)
1233 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1234 Da=1.0-QuantumScale*q->opacity;
1235 if ( (channel & SyncChannels) != 0 ) {
1236 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1237 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1238 Sa*=QuantumScale; Da*=QuantumScale; /* optimization */
1239 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1240 composite->red=gamma*Screen(p->red*Sa,q->red*Da);
1241 composite->green=gamma*Screen(p->green*Sa,q->green*Da);
1242 composite->blue=gamma*Screen(p->blue*Sa,q->blue*Da);
1243 if (q->colorspace == CMYKColorspace)
1244 composite->index=gamma*Screen(p->index*Sa,q->index*Da);
1246 else { /* handle channels as separate grayscale channels */
1247 if ( (channel & AlphaChannel) != 0 )
1248 composite->opacity=QuantumRange*(1.0-Screen(Sa,Da));
1249 if ( (channel & RedChannel) != 0 )
1250 composite->red=QuantumRange*Screen(QuantumScale*p->red,
1251 QuantumScale*q->red);
1252 if ( (channel & GreenChannel) != 0 )
1253 composite->green=QuantumRange*Screen(QuantumScale*p->green,
1254 QuantumScale*q->green);
1255 if ( (channel & BlueChannel) != 0 )
1256 composite->blue=QuantumRange*Screen(QuantumScale*p->blue,
1257 QuantumScale*q->blue);
1258 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1259 composite->index=QuantumRange*Screen(QuantumScale*p->index,
1260 QuantumScale*q->index);
1264 static MagickRealType SoftLight(const MagickRealType Sca,
1265 const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
1269 Oct 2004 SVG specification -- was found to be incorrect
1270 See http://lists.w3.org/Archives/Public/www-svg/2009Feb/0014.html.
1273 return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1275 return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa)*(3.0-8.0*Dca/Da))+
1276 Sca*(1.0-Da)+Dca*(1.0-Sa));
1277 return((Dca*Sa+(pow(Dca/Da,0.5)*Da-Dca)*(2.0*Sca-Sa))+Sca*(1.0-Da)+
1285 New specification: March 2009 SVG specification.
1289 return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1290 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1292 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
1293 alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1296 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1301 static inline void CompositeSoftLight(const MagickPixelPacket *p,
1302 const MagickPixelPacket *q,MagickPixelPacket *composite)
1309 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1310 Da=1.0-QuantumScale*q->opacity;
1311 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1312 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1313 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1314 composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1316 composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1318 composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1320 if (q->colorspace == CMYKColorspace)
1321 composite->index=gamma*SoftLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1327 Multiply difference by amount, if differance larger than threshold???
1328 What use this is is completely unknown
1329 The Opacity calculation appears to be inverted -- Anthony Thyssen
1331 static inline MagickRealType Threshold(const MagickRealType p,
1332 const MagickRealType q,const MagickRealType threshold,
1333 const MagickRealType amount)
1339 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
1341 return(q+delta*amount);
1344 static inline void CompositeThreshold(const MagickPixelPacket *p,
1345 const MagickPixelPacket *q,const MagickRealType threshold,
1346 const MagickRealType amount,MagickPixelPacket *composite)
1348 composite->red=Threshold(p->red,q->red,threshold,amount);
1349 composite->green=Threshold(p->green,q->green,threshold,amount);
1350 composite->blue=Threshold(p->blue,q->blue,threshold,amount);
1351 composite->opacity=QuantumRange-Threshold(p->opacity,q->opacity,
1353 if (q->colorspace == CMYKColorspace)
1354 composite->index=Threshold(p->index,q->index,threshold,amount);
1358 static MagickRealType VividLight(const MagickRealType Sca,
1359 const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
1362 VividLight: A Photoshop 7 composition method. See
1363 http://www.simplefilter.de/en/basics/mixmods.html.
1365 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
1367 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
1368 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1370 return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1371 return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1374 static inline void CompositeVividLight(const MagickPixelPacket *p,
1375 const MagickPixelPacket *q,MagickPixelPacket *composite)
1382 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1383 Da=1.0-QuantumScale*q->opacity;
1384 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1385 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1386 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1387 composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1389 composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1391 composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1393 if (q->colorspace == CMYKColorspace)
1394 composite->index=gamma*VividLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1398 static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
1399 const MagickRealType Dca,const MagickRealType Da)
1401 return(Sca*(1-Da)+Dca*(1-Sa));
1404 static inline void CompositeXor(const MagickPixelPacket *p,
1405 const MagickPixelPacket *q,MagickPixelPacket *composite)
1412 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1413 Da=1.0-QuantumScale*q->opacity;
1414 gamma=Sa+Da-2*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
1415 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1416 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1417 composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
1418 composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
1419 composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
1420 if (q->colorspace == CMYKColorspace)
1421 composite->index=gamma*Xor(p->index*Sa,Sa,q->index*Da,Da);
1424 static void HSBComposite(const double hue,const double saturation,
1425 const double brightness,MagickRealType *red,MagickRealType *green,
1426 MagickRealType *blue)
1436 Convert HSB to RGB colorspace.
1438 assert(red != (MagickRealType *) NULL);
1439 assert(green != (MagickRealType *) NULL);
1440 assert(blue != (MagickRealType *) NULL);
1441 if (saturation == 0.0)
1443 *red=(MagickRealType) QuantumRange*brightness;
1448 h=6.0*(hue-floor(hue));
1449 f=h-floor((double) h);
1450 p=brightness*(1.0-saturation);
1451 q=brightness*(1.0-saturation*f);
1452 t=brightness*(1.0-saturation*(1.0-f));
1458 *red=(MagickRealType) QuantumRange*brightness;
1459 *green=(MagickRealType) QuantumRange*t;
1460 *blue=(MagickRealType) QuantumRange*p;
1465 *red=(MagickRealType) QuantumRange*q;
1466 *green=(MagickRealType) QuantumRange*brightness;
1467 *blue=(MagickRealType) QuantumRange*p;
1472 *red=(MagickRealType) QuantumRange*p;
1473 *green=(MagickRealType) QuantumRange*brightness;
1474 *blue=(MagickRealType) QuantumRange*t;
1479 *red=(MagickRealType) QuantumRange*p;
1480 *green=(MagickRealType) QuantumRange*q;
1481 *blue=(MagickRealType) QuantumRange*brightness;
1486 *red=(MagickRealType) QuantumRange*t;
1487 *green=(MagickRealType) QuantumRange*p;
1488 *blue=(MagickRealType) QuantumRange*brightness;
1493 *red=(MagickRealType) QuantumRange*brightness;
1494 *green=(MagickRealType) QuantumRange*p;
1495 *blue=(MagickRealType) QuantumRange*q;
1501 MagickExport MagickBooleanType CompositeImage(Image *image,
1502 const CompositeOperator compose,const Image *composite_image,
1503 const ssize_t x_offset,const ssize_t y_offset)
1508 status=CompositeImageChannel(image,DefaultChannels,compose,composite_image,
1513 MagickExport MagickBooleanType CompositeImageChannel(Image *image,
1514 const ChannelType channel,const CompositeOperator compose,
1515 const Image *composite_image,const ssize_t x_offset,const ssize_t y_offset)
1517 #define CompositeImageTag "Composite/Image"
1539 modify_outside_overlay,
1550 destination_dissolve,
1564 Prepare composite image.
1566 assert(image != (Image *) NULL);
1567 assert(image->signature == MagickSignature);
1568 if (image->debug != MagickFalse)
1569 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1570 assert(composite_image != (Image *) NULL);
1571 assert(composite_image->signature == MagickSignature);
1572 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1573 return(MagickFalse);
1574 GetMagickPixelPacket(image,&zero);
1575 destination_image=(Image *) NULL;
1577 destination_dissolve=1.0;
1578 modify_outside_overlay=MagickFalse;
1579 percent_brightness=100.0;
1580 percent_saturation=100.0;
1581 source_dissolve=1.0;
1585 case ClearCompositeOp:
1586 case SrcCompositeOp:
1588 case SrcInCompositeOp:
1589 case OutCompositeOp:
1590 case SrcOutCompositeOp:
1591 case DstInCompositeOp:
1592 case DstAtopCompositeOp:
1595 Modify destination outside the overlaid region.
1597 modify_outside_overlay=MagickTrue;
1600 case OverCompositeOp:
1602 if (image->matte != MagickFalse)
1604 if (composite_image->matte != MagickFalse)
1607 case CopyCompositeOp:
1609 if ((x_offset < 0) || (y_offset < 0))
1611 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
1613 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
1616 exception=(&image->exception);
1617 image_view=AcquireCacheView(image);
1618 composite_view=AcquireCacheView(composite_image);
1619 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1620 #pragma omp parallel for schedule(dynamic,4) shared(status)
1622 for (y=0; y < (ssize_t) composite_image->rows; y++)
1627 register const IndexPacket
1630 register const PixelPacket
1633 register IndexPacket
1636 register PixelPacket
1639 if (status == MagickFalse)
1641 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1643 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1644 composite_image->columns,1,exception);
1645 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1650 composite_indexes=GetCacheViewVirtualIndexQueue(composite_view);
1651 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1652 (void) CopyMagickMemory(q,p,composite_image->columns*sizeof(*p));
1653 if ((indexes != (IndexPacket *) NULL) &&
1654 (composite_indexes != (const IndexPacket *) NULL))
1655 (void) CopyMagickMemory(indexes,composite_indexes,
1656 composite_image->columns*sizeof(*indexes));
1657 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1658 if (sync == MagickFalse)
1660 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1665 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1666 #pragma omp critical (MagickCore_CompositeImage)
1668 proceed=SetImageProgress(image,CompositeImageTag,
1669 (MagickOffsetType) y,image->rows);
1670 if (proceed == MagickFalse)
1674 composite_view=DestroyCacheView(composite_view);
1675 image_view=DestroyCacheView(image_view);
1678 case CopyOpacityCompositeOp:
1679 case ChangeMaskCompositeOp:
1682 Modify destination outside the overlaid region and require an alpha
1683 channel to exist, to add transparency.
1685 if (image->matte == MagickFalse)
1686 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1687 modify_outside_overlay=MagickTrue;
1690 case BlurCompositeOp:
1712 Blur Image dictated by an overlay gradient map: X = red_channel;
1713 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
1715 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1717 if (destination_image == (Image *) NULL)
1718 return(MagickFalse);
1720 Determine the horizontal and vertical maximim blur.
1722 SetGeometryInfo(&geometry_info);
1724 value=GetImageArtifact(composite_image,"compose:args");
1725 if (value != (char *) NULL)
1726 flags=ParseGeometry(value,&geometry_info);
1727 if ((flags & WidthValue) == 0 )
1729 destination_image=DestroyImage(destination_image);
1730 return(MagickFalse);
1732 width=geometry_info.rho;
1733 height=geometry_info.sigma;
1734 blur.x1=geometry_info.rho;
1737 blur.y2=geometry_info.sigma;
1740 if ((flags & HeightValue) == 0)
1742 if ((flags & XValue) != 0 )
1747 angle=DegreesToRadians(geometry_info.xi);
1748 blur.x1=width*cos(angle);
1749 blur.x2=width*sin(angle);
1750 blur.y1=(-height*sin(angle));
1751 blur.y2=height*cos(angle);
1753 if ((flags & YValue) != 0 )
1755 angle_start=DegreesToRadians(geometry_info.xi);
1756 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1759 Blur Image by resampling;
1762 exception=(&image->exception);
1763 resample_filter=AcquireResampleFilter(image,&image->exception);
1764 SetResampleFilter(resample_filter,GaussianFilter,sqrt(2.0));
1765 destination_view=AcquireCacheView(destination_image);
1766 composite_view=AcquireCacheView(composite_image);
1767 for (y=0; y < (ssize_t) composite_image->rows; y++)
1772 register const PixelPacket
1775 register PixelPacket
1778 register IndexPacket
1779 *restrict destination_indexes;
1784 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1786 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1788 r=QueueCacheViewAuthenticPixels(destination_view,0,y,
1789 destination_image->columns,1,&image->exception);
1790 if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
1792 destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
1793 for (x=0; x < (ssize_t) composite_image->columns; x++)
1795 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1800 if (fabs(angle_range) > MagickEpsilon)
1805 angle=angle_start+angle_range*QuantumScale*
1806 GetBluePixelComponent(p);
1807 blur.x1=width*cos(angle);
1808 blur.x2=width*sin(angle);
1809 blur.y1=(-height*sin(angle));
1810 blur.y2=height*cos(angle);
1812 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*p->red,
1813 blur.y1*QuantumScale*p->green,blur.x2*QuantumScale*p->red,
1814 blur.y2*QuantumScale*GetGreenPixelComponent(p));
1815 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1816 (double) y_offset+y,&pixel);
1817 SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
1821 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1822 if (sync == MagickFalse)
1825 resample_filter=DestroyResampleFilter(resample_filter);
1826 composite_view=DestroyCacheView(composite_view);
1827 destination_view=DestroyCacheView(destination_view);
1828 composite_image=destination_image;
1831 case DisplaceCompositeOp:
1832 case DistortCompositeOp:
1849 register IndexPacket
1850 *restrict destination_indexes;
1852 register PixelPacket
1859 Displace/Distort based on overlay gradient map:
1860 X = red_channel; Y = green_channel;
1861 compose:args = x_scale[,y_scale[,center.x,center.y]]
1863 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1865 if (destination_image == (Image *) NULL)
1866 return(MagickFalse);
1867 SetGeometryInfo(&geometry_info);
1869 value=GetImageArtifact(composite_image,"compose:args");
1870 if (value != (char *) NULL)
1871 flags=ParseGeometry(value,&geometry_info);
1872 if ((flags & (WidthValue|HeightValue)) == 0 )
1874 if ((flags & AspectValue) == 0)
1876 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1878 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1882 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1883 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1888 horizontal_scale=geometry_info.rho;
1889 vertical_scale=geometry_info.sigma;
1890 if ((flags & PercentValue) != 0)
1892 if ((flags & AspectValue) == 0)
1894 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1895 vertical_scale*=(composite_image->rows-1.0)/200.0;
1899 horizontal_scale*=(image->columns-1.0)/200.0;
1900 vertical_scale*=(image->rows-1.0)/200.0;
1903 if ((flags & HeightValue) == 0)
1904 vertical_scale=horizontal_scale;
1907 Determine fixed center point for absolute distortion map
1909 Displace offset relative to a fixed absolute point
1910 Select that point according to +X+Y user inputs.
1911 default = center of overlay image
1912 arg flag '!' = locations/percentage relative to background image
1914 center.x=(MagickRealType) x_offset;
1915 center.y=(MagickRealType) y_offset;
1916 if (compose == DistortCompositeOp)
1918 if ((flags & XValue) == 0)
1919 if ((flags & AspectValue) == 0)
1920 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
1923 center.x=((MagickRealType) image->columns-1)/2.0;
1925 if ((flags & AspectValue) == 0)
1926 center.x=(MagickRealType) x_offset+geometry_info.xi;
1928 center.x=geometry_info.xi;
1929 if ((flags & YValue) == 0)
1930 if ((flags & AspectValue) == 0)
1931 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
1933 center.y=((MagickRealType) image->rows-1)/2.0;
1935 if ((flags & AspectValue) == 0)
1936 center.y=(MagickRealType) y_offset+geometry_info.psi;
1938 center.y=geometry_info.psi;
1941 Shift the pixel offset point as defined by the provided,
1942 displacement/distortion map. -- Like a lens...
1945 exception=(&image->exception);
1946 resample_filter=AcquireResampleFilter(image,&image->exception);
1947 destination_view=AcquireCacheView(destination_image);
1948 composite_view=AcquireCacheView(composite_image);
1949 for (y=0; y < (ssize_t) composite_image->rows; y++)
1954 register const PixelPacket
1960 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1962 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1964 r=QueueCacheViewAuthenticPixels(destination_view,0,y,
1965 destination_image->columns,1,&image->exception);
1966 if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
1968 destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
1969 for (x=0; x < (ssize_t) composite_image->columns; x++)
1971 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1977 Displace the offset.
1979 offset.x=(horizontal_scale*(p->red-(((MagickRealType) QuantumRange+
1980 1.0)/2.0)))/(((MagickRealType) QuantumRange+1.0)/2.0)+
1981 center.x+((compose == DisplaceCompositeOp) ? x : 0);
1982 offset.y=(vertical_scale*(p->green-(((MagickRealType) QuantumRange+
1983 1.0)/2.0)))/(((MagickRealType) QuantumRange+1.0)/2.0)+
1984 center.y+((compose == DisplaceCompositeOp) ? y : 0);
1985 (void) ResamplePixelColor(resample_filter,(double) offset.x,
1986 (double) offset.y,&pixel);
1988 Mask with the 'invalid pixel mask' in alpha channel.
1990 pixel.opacity=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1991 pixel.opacity)*(1.0-QuantumScale*GetOpacityPixelComponent(p)));
1992 SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
1996 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1997 if (sync == MagickFalse)
2000 resample_filter=DestroyResampleFilter(resample_filter);
2001 composite_view=DestroyCacheView(composite_view);
2002 destination_view=DestroyCacheView(destination_view);
2003 composite_image=destination_image;
2006 case DissolveCompositeOp:
2009 Geometry arguments to dissolve factors.
2011 value=GetImageArtifact(composite_image,"compose:args");
2012 if (value != (char *) NULL)
2014 flags=ParseGeometry(value,&geometry_info);
2015 source_dissolve=geometry_info.rho/100.0;
2016 destination_dissolve=1.0;
2017 if ((source_dissolve-MagickEpsilon) < 0.0)
2018 source_dissolve=0.0;
2019 if ((source_dissolve+MagickEpsilon) > 1.0)
2021 destination_dissolve=2.0-source_dissolve;
2022 source_dissolve=1.0;
2024 if ((flags & SigmaValue) != 0)
2025 destination_dissolve=geometry_info.sigma/100.0;
2026 if ((destination_dissolve-MagickEpsilon) < 0.0)
2027 destination_dissolve=0.0;
2028 modify_outside_overlay=MagickTrue;
2029 if ((destination_dissolve+MagickEpsilon) > 1.0 )
2031 destination_dissolve=1.0;
2032 modify_outside_overlay=MagickFalse;
2037 case BlendCompositeOp:
2039 value=GetImageArtifact(composite_image,"compose:args");
2040 if (value != (char *) NULL)
2042 flags=ParseGeometry(value,&geometry_info);
2043 source_dissolve=geometry_info.rho/100.0;
2044 destination_dissolve=1.0-source_dissolve;
2045 if ((flags & SigmaValue) != 0)
2046 destination_dissolve=geometry_info.sigma/100.0;
2047 modify_outside_overlay=MagickTrue;
2048 if ((destination_dissolve+MagickEpsilon) > 1.0)
2049 modify_outside_overlay=MagickFalse;
2053 case MathematicsCompositeOp:
2056 Just collect the values from "compose:args", setting.
2057 Unused values are set to zero automagically.
2059 Arguments are normally a comma separated list, so this probably should
2060 be changed to some 'general comma list' parser, (with a minimum
2063 SetGeometryInfo(&geometry_info);
2064 value=GetImageArtifact(composite_image,"compose:args");
2065 if (value != (char *) NULL)
2066 (void) ParseGeometry(value,&geometry_info);
2069 case ModulateCompositeOp:
2072 Determine the brightness and saturation scale.
2074 value=GetImageArtifact(composite_image,"compose:args");
2075 if (value != (char *) NULL)
2077 flags=ParseGeometry(value,&geometry_info);
2078 percent_brightness=geometry_info.rho;
2079 if ((flags & SigmaValue) != 0)
2080 percent_saturation=geometry_info.sigma;
2084 case ThresholdCompositeOp:
2087 Determine the amount and threshold.
2088 This Composition method is depreciated
2090 value=GetImageArtifact(composite_image,"compose:args");
2091 if (value != (char *) NULL)
2093 flags=ParseGeometry(value,&geometry_info);
2094 amount=geometry_info.rho;
2095 threshold=geometry_info.sigma;
2096 if ((flags & SigmaValue) == 0)
2099 threshold*=QuantumRange;
2105 value=GetImageArtifact(composite_image,"compose:outside-overlay");
2106 if (value != (const char *) NULL)
2107 modify_outside_overlay=IsMagickTrue(value);
2113 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2114 GetMagickPixelPacket(composite_image,&zero);
2115 exception=(&image->exception);
2116 image_view=AcquireCacheView(image);
2117 composite_view=AcquireCacheView(composite_image);
2118 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2119 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2121 for (y=0; y < (ssize_t) image->rows; y++)
2136 register const IndexPacket
2137 *restrict composite_indexes;
2139 register const PixelPacket
2142 register IndexPacket
2148 register PixelPacket
2151 if (status == MagickFalse)
2153 if (modify_outside_overlay == MagickFalse)
2157 if ((y-y_offset) >= (ssize_t) composite_image->rows)
2161 If pixels is NULL, y is outside overlay region.
2163 pixels=(PixelPacket *) NULL;
2164 p=(PixelPacket *) NULL;
2165 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
2167 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
2168 composite_image->columns,1,exception);
2169 if (p == (const PixelPacket *) NULL)
2178 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2180 if (q == (PixelPacket *) NULL)
2185 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2186 composite_indexes=GetCacheViewVirtualIndexQueue(composite_view);
2192 for (x=0; x < (ssize_t) image->columns; x++)
2194 if (modify_outside_overlay == MagickFalse)
2201 if ((x-x_offset) >= (ssize_t) composite_image->columns)
2204 destination.red=(MagickRealType) q->red;
2205 destination.green=(MagickRealType) q->green;
2206 destination.blue=(MagickRealType) q->blue;
2207 if (image->matte != MagickFalse)
2208 destination.opacity=(MagickRealType) q->opacity;
2209 if (image->colorspace == CMYKColorspace)
2211 destination.red=(MagickRealType) QuantumRange-destination.red;
2212 destination.green=(MagickRealType) QuantumRange-destination.green;
2213 destination.blue=(MagickRealType) QuantumRange-destination.blue;
2214 destination.index=(MagickRealType) (QuantumRange-indexes[x]);
2217 Handle destination modifications outside overlaid region.
2219 composite=destination;
2220 if ((pixels == (PixelPacket *) NULL) || (x < x_offset) ||
2221 ((x-x_offset) >= (ssize_t) composite_image->columns))
2225 case DissolveCompositeOp:
2226 case BlendCompositeOp:
2228 composite.opacity=(MagickRealType) (QuantumRange-
2229 destination_dissolve*(QuantumRange-composite.opacity));
2232 case ClearCompositeOp:
2233 case SrcCompositeOp:
2235 CompositeClear(&destination,&composite);
2239 case SrcInCompositeOp:
2240 case OutCompositeOp:
2241 case SrcOutCompositeOp:
2242 case DstInCompositeOp:
2243 case DstAtopCompositeOp:
2244 case CopyOpacityCompositeOp:
2245 case ChangeMaskCompositeOp:
2247 composite.opacity=(MagickRealType) TransparentOpacity;
2252 (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,
2253 y-y_offset,&composite,exception);
2257 if (image->colorspace == CMYKColorspace)
2259 composite.red=(MagickRealType) QuantumRange-composite.red;
2260 composite.green=(MagickRealType) QuantumRange-composite.green;
2261 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2262 composite.index=(MagickRealType) QuantumRange-composite.index;
2264 q->red=ClampToQuantum(composite.red);
2265 q->green=ClampToQuantum(composite.green);
2266 q->blue=ClampToQuantum(composite.blue);
2267 if (image->matte != MagickFalse)
2268 q->opacity=ClampToQuantum(composite.opacity);
2269 if (image->colorspace == CMYKColorspace)
2270 indexes[x]=ClampToQuantum(composite.index);
2275 Handle normal overlay of source onto destination.
2277 source.red=(MagickRealType) GetRedPixelComponent(p);
2278 source.green=(MagickRealType) GetGreenPixelComponent(p);
2279 source.blue=(MagickRealType) GetBluePixelComponent(p);
2280 if (composite_image->matte != MagickFalse)
2281 source.opacity=(MagickRealType) GetOpacityPixelComponent(p);
2282 if (composite_image->colorspace == CMYKColorspace)
2284 source.red=(MagickRealType) QuantumRange-source.red;
2285 source.green=(MagickRealType) QuantumRange-source.green;
2286 source.blue=(MagickRealType) QuantumRange-source.blue;
2287 source.index=(MagickRealType) QuantumRange-(MagickRealType)
2288 composite_indexes[x-x_offset];
2292 /* Duff-Porter Compositions */
2293 case ClearCompositeOp:
2295 CompositeClear(&destination,&composite);
2298 case SrcCompositeOp:
2299 case CopyCompositeOp:
2300 case ReplaceCompositeOp:
2306 case DstCompositeOp:
2308 case OverCompositeOp:
2309 case SrcOverCompositeOp:
2311 MagickPixelCompositeOver(&source,source.opacity,
2312 &destination,destination.opacity,&composite);
2315 case DstOverCompositeOp:
2317 MagickPixelCompositeOver(&destination,destination.opacity,
2318 &source,source.opacity,&composite);
2321 case SrcInCompositeOp:
2324 CompositeIn(&source,&destination,&composite);
2327 case DstInCompositeOp:
2329 CompositeIn(&destination,&source,&composite);
2332 case OutCompositeOp:
2333 case SrcOutCompositeOp:
2335 CompositeOut(&source,&destination,&composite);
2338 case DstOutCompositeOp:
2340 CompositeOut(&destination,&source,&composite);
2343 case AtopCompositeOp:
2344 case SrcAtopCompositeOp:
2346 CompositeAtop(&source,&destination,&composite);
2349 case DstAtopCompositeOp:
2351 CompositeAtop(&destination,&source,&composite);
2354 case XorCompositeOp:
2356 CompositeXor(&source,&destination,&composite);
2359 /* Mathematical Compositions */
2360 case PlusCompositeOp:
2362 CompositePlus(&source,&destination,channel,&composite);
2365 case MinusCompositeOp:
2367 CompositeMinus(&source,&destination,
2368 channel,&composite);
2371 case ModulusAddCompositeOp:
2373 CompositeModulusAdd(&source,&destination,channel,&composite);
2376 case ModulusSubtractCompositeOp:
2378 CompositeModulusSubtract(&source,&destination,channel,&composite);
2381 case DifferenceCompositeOp:
2383 CompositeDifference(&source,&destination,channel,&composite);
2386 case ExclusionCompositeOp:
2388 CompositeExclusion(&source,&destination,channel,&composite);
2391 case MultiplyCompositeOp:
2393 CompositeMultiply(&source,&destination,channel,&composite);
2396 case ScreenCompositeOp:
2398 CompositeScreen(&source,&destination,channel,&composite);
2401 case DivideCompositeOp:
2403 CompositeDivide(&source,&destination,channel,&composite);
2406 case DarkenCompositeOp:
2408 CompositeDarken(&source,&destination,channel,&composite);
2411 case LightenCompositeOp:
2413 CompositeLighten(&source,&destination,channel,&composite);
2416 case MathematicsCompositeOp:
2418 CompositeMathematics(&source,&destination,channel,&geometry_info,
2422 /* Lighting Compositions */
2423 case ColorDodgeCompositeOp:
2425 CompositeColorDodge(&source,&destination,&composite);
2428 case ColorBurnCompositeOp:
2430 CompositeColorBurn(&source,&destination,&composite);
2433 case LinearDodgeCompositeOp:
2435 CompositeLinearDodge(&source,&destination,&composite);
2438 case LinearBurnCompositeOp:
2440 CompositeLinearBurn(&source,&destination,&composite);
2443 case HardLightCompositeOp:
2445 CompositeHardLight(&source,&destination,&composite);
2448 case OverlayCompositeOp:
2450 /* Overlay = Reversed HardLight. */
2451 CompositeHardLight(&destination,&source,&composite);
2454 case SoftLightCompositeOp:
2456 CompositeSoftLight(&source,&destination,&composite);
2459 case LinearLightCompositeOp:
2461 CompositeLinearLight(&source,&destination,&composite);
2464 case PegtopLightCompositeOp:
2466 CompositePegtopLight(&source,&destination,&composite);
2469 case VividLightCompositeOp:
2471 CompositeVividLight(&source,&destination,&composite);
2474 case PinLightCompositeOp:
2476 CompositePinLight(&source,&destination,&composite);
2479 /* Other Composition */
2480 case ChangeMaskCompositeOp:
2482 if ((composite.opacity > ((MagickRealType) QuantumRange/2.0)) ||
2483 (IsMagickColorSimilar(&source,&destination) != MagickFalse))
2484 composite.opacity=(MagickRealType) TransparentOpacity;
2486 composite.opacity=(MagickRealType) OpaqueOpacity;
2489 case BumpmapCompositeOp:
2491 if (source.opacity == TransparentOpacity)
2493 CompositeBumpmap(&source,&destination,&composite);
2496 case DissolveCompositeOp:
2498 MagickPixelCompositeOver(&source,(MagickRealType) (QuantumRange-source_dissolve*
2499 (QuantumRange-source.opacity)),&destination,(MagickRealType)
2500 (QuantumRange-destination_dissolve*(QuantumRange-
2501 destination.opacity)),&composite);
2504 case BlendCompositeOp:
2506 MagickPixelCompositeBlend(&source,source_dissolve,&destination,
2507 destination_dissolve,&composite);
2510 case ThresholdCompositeOp:
2512 CompositeThreshold(&source,&destination,threshold,amount,&composite);
2515 case ModulateCompositeOp:
2520 if (source.opacity == TransparentOpacity)
2522 offset=(ssize_t) (MagickPixelIntensityToQuantum(&source)-midpoint);
2525 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2526 &saturation,&brightness);
2527 brightness+=(0.01*percent_brightness*offset)/midpoint;
2528 saturation*=0.01*percent_saturation;
2529 HSBComposite(hue,saturation,brightness,&composite.red,
2530 &composite.green,&composite.blue);
2533 case HueCompositeOp:
2535 if (source.opacity == TransparentOpacity)
2537 if (destination.opacity == TransparentOpacity)
2542 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2543 &saturation,&brightness);
2544 CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
2545 HSBComposite(hue,saturation,brightness,&composite.red,
2546 &composite.green,&composite.blue);
2547 if (source.opacity < destination.opacity)
2548 composite.opacity=source.opacity;
2551 case SaturateCompositeOp:
2553 if (source.opacity == TransparentOpacity)
2555 if (destination.opacity == TransparentOpacity)
2560 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2561 &saturation,&brightness);
2562 CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
2564 HSBComposite(hue,saturation,brightness,&composite.red,
2565 &composite.green,&composite.blue);
2566 if (source.opacity < destination.opacity)
2567 composite.opacity=source.opacity;
2570 case LuminizeCompositeOp:
2572 if (source.opacity == TransparentOpacity)
2574 if (destination.opacity == TransparentOpacity)
2579 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2580 &saturation,&brightness);
2581 CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
2583 HSBComposite(hue,saturation,brightness,&composite.red,
2584 &composite.green,&composite.blue);
2585 if (source.opacity < destination.opacity)
2586 composite.opacity=source.opacity;
2589 case ColorizeCompositeOp:
2591 if (source.opacity == TransparentOpacity)
2593 if (destination.opacity == TransparentOpacity)
2598 CompositeHSB(destination.red,destination.green,destination.blue,&sans,
2600 CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
2602 HSBComposite(hue,saturation,brightness,&composite.red,
2603 &composite.green,&composite.blue);
2604 if (source.opacity < destination.opacity)
2605 composite.opacity=source.opacity;
2608 case CopyRedCompositeOp:
2609 case CopyCyanCompositeOp:
2611 composite.red=source.red;
2614 case CopyGreenCompositeOp:
2615 case CopyMagentaCompositeOp:
2617 composite.green=source.green;
2620 case CopyBlueCompositeOp:
2621 case CopyYellowCompositeOp:
2623 composite.blue=source.blue;
2626 case CopyOpacityCompositeOp:
2628 if (source.matte == MagickFalse)
2630 composite.opacity=(MagickRealType) (QuantumRange-
2631 MagickPixelIntensityToQuantum(&source));
2634 composite.opacity=source.opacity;
2637 case CopyBlackCompositeOp:
2639 if (source.colorspace != CMYKColorspace)
2640 ConvertRGBToCMYK(&source);
2641 composite.index=source.index;
2644 /* compose methods that are already handled */
2645 case BlurCompositeOp:
2646 case DisplaceCompositeOp:
2647 case DistortCompositeOp:
2655 if (image->colorspace == CMYKColorspace)
2657 composite.red=(MagickRealType) QuantumRange-composite.red;
2658 composite.green=(MagickRealType) QuantumRange-composite.green;
2659 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2660 composite.index=(MagickRealType) QuantumRange-composite.index;
2662 q->red=ClampToQuantum(composite.red);
2663 q->green=ClampToQuantum(composite.green);
2664 q->blue=ClampToQuantum(composite.blue);
2665 q->opacity=ClampToQuantum(composite.opacity);
2666 if (image->colorspace == CMYKColorspace)
2667 indexes[x]=ClampToQuantum(composite.index);
2669 if (p >= (pixels+composite_image->columns))
2673 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2675 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2680 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2681 #pragma omp critical (MagickCore_CompositeImageChannel)
2683 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2685 if (proceed == MagickFalse)
2689 composite_view=DestroyCacheView(composite_view);
2690 image_view=DestroyCacheView(image_view);
2691 if (destination_image != (Image * ) NULL)
2692 destination_image=DestroyImage(destination_image);
2697 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2701 % T e x t u r e I m a g e %
2705 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2707 % TextureImage() repeatedly tiles the texture image across and down the image
2710 % The format of the TextureImage method is:
2712 % MagickBooleanType TextureImage(Image *image,const Image *texture)
2714 % A description of each parameter follows:
2716 % o image: the image.
2718 % o texture: This image is the texture to layer on the background.
2721 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
2723 #define TextureImageTag "Texture/Image"
2738 assert(image != (Image *) NULL);
2739 if (image->debug != MagickFalse)
2740 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2741 assert(image->signature == MagickSignature);
2742 if (texture == (const Image *) NULL)
2743 return(MagickFalse);
2744 (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
2745 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2746 return(MagickFalse);
2748 if ((image->compose != CopyCompositeOp) &&
2749 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2750 (texture->matte != MagickFalse)))
2753 Tile texture onto the image background.
2755 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2756 #pragma omp parallel for schedule(dynamic,4) shared(status)
2758 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
2763 if (status == MagickFalse)
2765 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2770 thread_status=CompositeImage(image,image->compose,texture,x+
2771 texture->tile_offset.x,y+texture->tile_offset.y);
2772 if (thread_status == MagickFalse)
2774 status=thread_status;
2778 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2783 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2784 #pragma omp critical (MagickCore_TextureImage)
2786 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2788 if (proceed == MagickFalse)
2792 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2793 image->rows,image->rows);
2797 Tile texture onto the image background (optimized).
2800 exception=(&image->exception);
2801 image_view=AcquireCacheView(image);
2802 texture_view=AcquireCacheView(texture);
2803 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2804 #pragma omp parallel for schedule(dynamic,4) shared(status)
2806 for (y=0; y < (ssize_t) image->rows; y++)
2811 register const IndexPacket
2814 register const PixelPacket
2817 register IndexPacket
2823 register PixelPacket
2829 if (status == MagickFalse)
2831 p=GetCacheViewVirtualPixels(texture_view,texture->tile_offset.x,(y+
2832 texture->tile_offset.y) % texture->rows,texture->columns,1,exception);
2833 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2835 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2840 texture_indexes=GetCacheViewVirtualIndexQueue(texture_view);
2841 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2842 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2844 width=texture->columns;
2845 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2846 width=image->columns-x;
2847 (void) CopyMagickMemory(q,p,width*sizeof(*p));
2848 if ((image->colorspace == CMYKColorspace) &&
2849 (texture->colorspace == CMYKColorspace))
2851 (void) CopyMagickMemory(indexes,texture_indexes,width*
2857 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2858 if (sync == MagickFalse)
2860 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2865 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2866 #pragma omp critical (MagickCore_TextureImage)
2868 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2870 if (proceed == MagickFalse)
2874 texture_view=DestroyCacheView(texture_view);
2875 image_view=DestroyCacheView(image_view);