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 "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/thread-private.h"
73 #include "magick/utility.h"
74 #include "magick/version.h"
77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81 % C o m p o s i t e I m a g e C h a n n e l %
85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 % CompositeImageChannel() returns the second image composited onto the first
88 % at the specified offset, using the specified composite method.
90 % The format of the CompositeImageChannel method is:
92 % MagickBooleanType CompositeImage(Image *image,
93 % const CompositeOperator compose,Image *composite_image,
94 % const ssize_t x_offset,const ssize_t y_offset)
95 % MagickBooleanType CompositeImageChannel(Image *image,
96 % const ChannelType channel,const CompositeOperator compose,
97 % Image *composite_image,const ssize_t x_offset,const ssize_t y_offset)
99 % A description of each parameter follows:
101 % o image: the destination image, modified by he composition
103 % o channel: the channel.
105 % o compose: This operator affects how the composite is applied to
106 % the image. The operators and how they are utilized are listed here
107 % http://www.w3.org/TR/SVG12/#compositing.
109 % o composite_image: the composite (source) image.
111 % o x_offset: the column offset of the composited image.
113 % o y_offset: the row offset of the composited image.
115 % Extra Controls from Image meta-data in 'composite_image' (artifacts)
118 % A string containing extra numerical arguments for specific compose
119 % methods, generally expressed as a 'geometry' or a comma separated list
122 % Compose methods needing such arguments include "BlendCompositeOp" and
123 % "DisplaceCompositeOp".
125 % o "compose:outside-overlay"
126 % Modify how the composition is to effect areas not directly covered
127 % by the 'composite_image' at the offset given. Normally this is
128 % dependant on the 'compose' method, especially Duff-Porter methods.
130 % If set to "false" then disable all normal handling of pixels not
131 % covered by the composite_image. Typically used for repeated tiling
132 % of the composite_image by the calling API.
134 % Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
138 static inline double MagickMin(const double x,const double y)
144 static inline double MagickMax(const double x,const double y)
152 ** Programmers notes on SVG specification.
154 ** A Composition is defined by...
155 ** Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
156 ** Blending areas : X = 1 for area of overlap ie: f(Sc,Dc)
157 ** Y = 1 for source preserved
158 ** Z = 1 for destination preserved
160 ** Conversion to transparency (then optimized)
161 ** Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
162 ** Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
165 ** Sca = Sc*Sa normalized Source color divided by Source alpha
166 ** Dca = Dc*Da normalized Dest color divided by Dest alpha
167 ** Dc' = Dca'/Da' the desired color value for this channel.
169 ** Da' 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...
174 ** gamma = Sa+Da-Sa*Da;
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'.
184 ** Mathematical operator changes to be applied from IM v6.7...
186 ** 1/ Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
187 ** 'ModulusAdd' and 'ModulusSubtract' for clarity.
189 ** 2/ All mathematical compositions work as per the SVG specification
190 ** with regard to blending. This now includes 'ModulusAdd' and
191 ** 'ModulusSubtract'.
193 ** 3/ When the special channel flag 'sync' (syncronize channel updates)
194 ** is turned off (enabled by default) then mathematical compositions are
195 ** only performed on the channels specified, and are applied
196 ** independantally of each other. In other words the mathematics is
197 ** performed as 'pure' mathematical operations, rather than as image
201 static inline MagickRealType Atop(const MagickRealType p,
202 const MagickRealType Sa,const MagickRealType q,
203 const MagickRealType magick_unused(Da))
205 return(p*Sa+q*(1.0-Sa)); /* Da optimized out, Da/gamma => 1.0 */
208 static inline void CompositeAtop(const MagickPixelPacket *p,
209 const MagickPixelPacket *q,MagickPixelPacket *composite)
214 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
215 composite->opacity=q->opacity; /* optimized Da = 1.0-Gamma */
216 composite->red=Atop(p->red,Sa,q->red,1.0);
217 composite->green=Atop(p->green,Sa,q->green,1.0);
218 composite->blue=Atop(p->blue,Sa,q->blue,1.0);
219 if (q->colorspace == CMYKColorspace)
220 composite->index=Atop(p->index,Sa,q->index,1.0);
224 What is this Composition method for? Can't find any specification!
225 WARNING this is not doing correct 'over' blend handling (Anthony Thyssen).
227 static inline void CompositeBumpmap(const MagickPixelPacket *p,
228 const MagickPixelPacket *q,MagickPixelPacket *composite)
233 intensity=MagickPixelIntensity(p);
234 composite->red=QuantumScale*intensity*q->red;
235 composite->green=QuantumScale*intensity*q->green;
236 composite->blue=QuantumScale*intensity*q->blue;
237 composite->opacity=(MagickRealType) QuantumScale*intensity*
238 GetOpacityPixelComponent(p);
239 if (q->colorspace == CMYKColorspace)
240 composite->index=QuantumScale*intensity*q->index;
243 static inline void CompositeClear(const MagickPixelPacket *q,
244 MagickPixelPacket *composite)
246 composite->opacity=(MagickRealType) TransparentOpacity;
248 composite->green=0.0;
250 if (q->colorspace == CMYKColorspace)
251 composite->index=0.0;
254 static MagickRealType ColorBurn(const MagickRealType Sca,
255 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
259 Oct 2004 SVG specification.
261 if (Sca*Da + Dca*Sa <= Sa*Da)
262 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
263 return(Sa*(Sca*Da+Dca*Sa-Sa*Da)/Sca + Sca*(1.0-Da) + Dca*(1.0-Sa));
266 March 2009 SVG specification.
268 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
269 return(Sa*Da+Dca*(1.0-Sa));
270 if (Sca < MagickEpsilon)
271 return(Dca*(1.0-Sa));
272 return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
276 static inline void CompositeColorBurn(const MagickPixelPacket *p,
277 const MagickPixelPacket *q,MagickPixelPacket *composite)
284 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
285 Da=1.0-QuantumScale*q->opacity;
286 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
287 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
288 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
289 composite->red=gamma*ColorBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
291 composite->green=gamma*ColorBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
293 composite->blue=gamma*ColorBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
295 if (q->colorspace == CMYKColorspace)
296 composite->index=gamma*ColorBurn(QuantumScale*p->index*Sa,Sa,QuantumScale*
301 static MagickRealType ColorDodge(const MagickRealType Sca,
302 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
306 Oct 2004 SVG specification.
308 if ((Sca*Da+Dca*Sa) >= Sa*Da)
309 return( Sa*Da + Sca*(1.0-Da) + Dca*(1.0-Sa) );
310 return( Dca*Sa*Sa/(Sa-Sca) + Sca*(1.0-Da) + Dca*(1.0-Sa) );
314 New specification, March 2009 SVG specification. This specification was
315 also wrong of non-overlap cases.
317 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
318 return(Sca*(1.0-Da));
319 if (fabs(Sca-Sa) < MagickEpsilon)
320 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
321 return(Sa*MagickMin(Da,Dca*Sa/(Sa-Sca)));
324 Working from first principles using the original formula:
328 This works correctly! Looks like the 2004 model was right but just
329 required a extra condition for correct handling.
331 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
332 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
333 if (fabs(Sca-Sa) < MagickEpsilon)
334 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
335 return(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
338 static inline void CompositeColorDodge(const MagickPixelPacket *p,
339 const MagickPixelPacket *q,MagickPixelPacket *composite)
346 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
347 Da=1.0-QuantumScale*q->opacity;
348 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
349 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
350 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
351 composite->red=gamma*ColorDodge(QuantumScale*p->red*Sa,Sa,QuantumScale*
353 composite->green=gamma*ColorDodge(QuantumScale*p->green*Sa,Sa,QuantumScale*
355 composite->blue=gamma*ColorDodge(QuantumScale*p->blue*Sa,Sa,QuantumScale*
357 if (q->colorspace == CMYKColorspace)
358 composite->index=gamma*ColorDodge(QuantumScale*p->index*Sa,Sa,QuantumScale*
362 static inline MagickRealType Darken(const MagickRealType p,
363 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
366 return(MagickOver_(p,alpha,q,beta)); /* src-over */
367 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
370 static inline void CompositeDarken(const MagickPixelPacket *p,
371 const MagickPixelPacket *q,const ChannelType channel,
372 MagickPixelPacket *composite)
375 Darken is equivelent to a 'Minimum' method
376 OR a greyscale version of a binary 'Or'
377 OR the 'Intersection' of pixel sets.
382 if ( (channel & SyncChannels) != 0 ) {
383 composite->opacity=QuantumScale*p->opacity*q->opacity; /* Over Blend */
384 gamma=1.0-QuantumScale*composite->opacity;
385 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
386 composite->red=gamma*Darken(p->red,p->opacity,q->red,q->opacity);
387 composite->green=gamma*Darken(p->green,p->opacity,q->green,q->opacity);
388 composite->blue=gamma*Darken(p->blue,p->opacity,q->blue,q->opacity);
389 if (q->colorspace == CMYKColorspace)
390 composite->index=gamma*Darken(p->index,p->opacity,q->index,q->opacity);
392 else { /* handle channels as separate grayscale channels */
393 if ( (channel & AlphaChannel) != 0 )
394 composite->opacity=MagickMax(p->opacity,q->opacity);
395 if ( (channel & RedChannel) != 0 )
396 composite->red=MagickMin(p->red,q->red);
397 if ( (channel & GreenChannel) != 0 )
398 composite->green=MagickMin(p->green,q->green);
399 if ( (channel & BlueChannel) != 0 )
400 composite->blue=MagickMin(p->blue,q->blue);
401 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
402 composite->index=MagickMin(p->index,q->index);
406 static inline MagickRealType Difference(const MagickRealType p,
407 const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
409 /* Optimized by Multipling by QuantumRange (taken from gamma). */
410 return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q));
413 static inline void CompositeDifference(const MagickPixelPacket *p,
414 const MagickPixelPacket *q,const ChannelType channel,
415 MagickPixelPacket *composite)
422 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
423 Da=1.0-QuantumScale*q->opacity;
424 if ( (channel & SyncChannels) != 0 ) {
425 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
426 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
427 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
428 /* Values are not normalized as an optimization. */
429 composite->red=gamma*Difference(p->red,Sa,q->red,Da);
430 composite->green=gamma*Difference(p->green,Sa,q->green,Da);
431 composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da);
432 if (q->colorspace == CMYKColorspace)
433 composite->index=gamma*Difference(p->index,Sa,q->index,Da);
435 else { /* handle channels as separate grayscale channels */
436 if ( (channel & AlphaChannel) != 0 )
437 composite->opacity=QuantumRange-fabs(p->opacity - q->opacity);
438 if ( (channel & RedChannel) != 0 )
439 composite->red=fabs(p->red - q->red);
440 if ( (channel & GreenChannel) != 0 )
441 composite->green=fabs(p->green - q->green);
442 if ( (channel & BlueChannel) != 0 )
443 composite->blue=fabs(p->blue - q->blue);
444 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
445 composite->index=fabs(p->index - q->index);
449 static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
450 const MagickRealType Dca,const MagickRealType Da)
457 But with appropriate handling for special case of Dc == 0 specifically
458 so that f(Black,Black)=Black and f(non-Black,Black)=White.
459 It is however also important to correctly do 'over' alpha blending which
460 is why the formula becomes so complex looking.
462 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
463 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
464 if (fabs(Dca) < MagickEpsilon)
465 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
466 return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
469 static inline void CompositeDivide(const MagickPixelPacket *p,
470 const MagickPixelPacket *q,const ChannelType channel,
471 MagickPixelPacket *composite)
478 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
479 Da=1.0-QuantumScale*q->opacity;
480 if ( (channel & SyncChannels) != 0 ) {
481 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
482 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
483 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
484 composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale*
486 composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale*
488 composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale*
490 if (q->colorspace == CMYKColorspace)
491 composite->index=gamma*Divide(QuantumScale*p->index*Sa,Sa,QuantumScale*
494 else { /* handle channels as separate grayscale channels */
495 if ( (channel & AlphaChannel) != 0 )
496 composite->opacity=QuantumRange*(1.0-Divide(Sa,1.0,Da,1.0));
497 if ( (channel & RedChannel) != 0 )
498 composite->red=QuantumRange*
499 Divide(QuantumScale*p->red,1.0,QuantumScale*q->red,1.0);
500 if ( (channel & GreenChannel) != 0 )
501 composite->green=QuantumRange*
502 Divide(QuantumScale*p->green,1.0,QuantumScale*q->green,1.0);
503 if ( (channel & BlueChannel) != 0 )
504 composite->blue=QuantumRange*
505 Divide(QuantumScale*p->blue,1.0,QuantumScale*q->blue,1.0);
506 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
507 composite->index=QuantumRange*
508 Divide(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0);
512 static MagickRealType Exclusion(const MagickRealType Sca,
513 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
515 return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
518 static inline void CompositeExclusion(const MagickPixelPacket *p,
519 const MagickPixelPacket *q,const ChannelType channel,
520 MagickPixelPacket *composite)
527 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
528 Da=1.0-QuantumScale*q->opacity;
529 if ( (channel & SyncChannels) != 0 ) {
530 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
531 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
532 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
533 composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale*
535 composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale*
537 composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale*
539 if (q->colorspace == CMYKColorspace)
540 composite->index=gamma*Exclusion(QuantumScale*p->index*Sa,Sa,QuantumScale*
543 else { /* handle channels as separate grayscale channels */
544 if ( (channel & AlphaChannel) != 0 )
545 composite->opacity=QuantumRange*(1.0-Exclusion(Sa,1.0,Da,1.0));
546 if ( (channel & RedChannel) != 0 )
547 composite->red=QuantumRange*
548 Exclusion(QuantumScale*p->red,1.0,QuantumScale*q->red,1.0);
549 if ( (channel & GreenChannel) != 0 )
550 composite->green=QuantumRange*
551 Exclusion(QuantumScale*p->green,1.0,QuantumScale*q->green,1.0);
552 if ( (channel & BlueChannel) != 0 )
553 composite->blue=QuantumRange*
554 Exclusion(QuantumScale*p->blue,1.0,QuantumScale*q->blue,1.0);
555 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
556 composite->index=QuantumRange*
557 Exclusion(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0);
561 static MagickRealType HardLight(const MagickRealType Sca,
562 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
565 return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
566 return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
569 static inline void CompositeHardLight(const MagickPixelPacket *p,
570 const MagickPixelPacket *q,MagickPixelPacket *composite)
577 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
578 Da=1.0-QuantumScale*q->opacity;
579 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
580 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
581 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
582 composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
584 composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
586 composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
588 if (q->colorspace == CMYKColorspace)
589 composite->index=gamma*HardLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
593 static void CompositeHSB(const MagickRealType red,const MagickRealType green,
594 const MagickRealType blue,double *hue,double *saturation,double *brightness)
602 Convert RGB to HSB colorspace.
604 assert(hue != (double *) NULL);
605 assert(saturation != (double *) NULL);
606 assert(brightness != (double *) NULL);
607 max=(red > green ? red : green);
610 min=(red < green ? red : green);
615 *brightness=(double) (QuantumScale*max);
618 *saturation=(double) (1.0-min/max);
623 *hue=(double) ((green-blue)/delta);
626 *hue=(double) (2.0+(blue-red)/delta);
629 *hue=(double) (4.0+(red-green)/delta);
635 static inline MagickRealType In(const MagickRealType p,
636 const MagickRealType Sa,const MagickRealType magick_unused(q),
637 const MagickRealType Da)
642 static inline void CompositeIn(const MagickPixelPacket *p,
643 const MagickPixelPacket *q,MagickPixelPacket *composite)
650 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
651 Da=1.0-QuantumScale*q->opacity;
653 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
654 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
655 composite->red=gamma*In(p->red,Sa,q->red,Da);
656 composite->green=gamma*In(p->green,Sa,q->green,Da);
657 composite->blue=gamma*In(p->blue,Sa,q->blue,Da);
658 if (q->colorspace == CMYKColorspace)
659 composite->index=gamma*In(p->index,Sa,q->index,Da);
662 static inline MagickRealType Lighten(const MagickRealType p,
663 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
666 return(MagickOver_(p,alpha,q,beta)); /* src-over */
667 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
670 static inline void CompositeLighten(const MagickPixelPacket *p,
671 const MagickPixelPacket *q,const ChannelType channel,
672 MagickPixelPacket *composite)
675 Lighten is also equivelevt to a 'Maximum' method
676 OR a greyscale version of a binary 'And'
677 OR the 'Union' of pixel sets.
682 if ( (channel & SyncChannels) != 0 ) {
683 composite->opacity=QuantumScale*p->opacity*q->opacity; /* Over Blend */
684 gamma=1.0-QuantumScale*composite->opacity;
685 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
686 composite->red=gamma*Lighten(p->red,p->opacity,q->red,q->opacity);
687 composite->green=gamma*Lighten(p->green,p->opacity,q->green,q->opacity);
688 composite->blue=gamma*Lighten(p->blue,p->opacity,q->blue,q->opacity);
689 if (q->colorspace == CMYKColorspace)
690 composite->index=gamma*Lighten(p->index,p->opacity,q->index,q->opacity);
692 else { /* handle channels as separate grayscale channels */
693 if ( (channel & AlphaChannel) != 0 )
694 composite->opacity=MagickMin(p->opacity,q->opacity);
695 if ( (channel & RedChannel) != 0 )
696 composite->red=MagickMax(p->red,q->red);
697 if ( (channel & GreenChannel) != 0 )
698 composite->green=MagickMax(p->green,q->green);
699 if ( (channel & BlueChannel) != 0 )
700 composite->blue=MagickMax(p->blue,q->blue);
701 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
702 composite->index=MagickMax(p->index,q->index);
707 static inline MagickRealType LinearDodge(const MagickRealType Sca,
708 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
711 LinearDodge: simplifies to a trivial formula
719 static inline void CompositeLinearDodge(const MagickPixelPacket *p,
720 const MagickPixelPacket *q,MagickPixelPacket *composite)
727 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
728 Da=1.0-QuantumScale*q->opacity;
729 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
730 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
731 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
732 composite->red=gamma*(p->red*Sa+q->red*Da);
733 composite->green=gamma*(p->green*Sa+q->green*Da);
734 composite->blue=gamma*(p->blue*Sa+q->blue*Da);
735 if (q->colorspace == CMYKColorspace)
736 composite->index=gamma*(p->index*Sa+q->index*Da);
740 static inline MagickRealType LinearBurn(const MagickRealType Sca,
741 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
744 LinearBurn: as defined by Abode Photoshop, according to
745 http://www.simplefilter.de/en/basics/mixmods.html is:
747 f(Sc,Dc) = Sc + Dc - 1
749 return(Sca+Dca-Sa*Da);
752 static inline void CompositeLinearBurn(const MagickPixelPacket *p,
753 const MagickPixelPacket *q,MagickPixelPacket *composite)
760 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
761 Da=1.0-QuantumScale*q->opacity;
762 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
763 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
764 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
765 composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
767 composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
769 composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
771 if (q->colorspace == CMYKColorspace)
772 composite->index=gamma*LinearBurn(QuantumScale*p->index*Sa,Sa,QuantumScale*
776 static inline MagickRealType LinearLight(const MagickRealType Sca,
777 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
781 Previous formula, was only valid for fully-opaque images.
783 return(Dca+2*Sca-1.0);
786 LinearLight: as defined by Abode Photoshop, according to
787 http://www.simplefilter.de/en/basics/mixmods.html is:
789 f(Sc,Dc) = Dc + 2*Sc - 1
791 return((Sca-Sa)*Da+Sca+Dca);
795 static inline void CompositeLinearLight(const MagickPixelPacket *p,
796 const MagickPixelPacket *q,MagickPixelPacket *composite)
803 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
804 Da=1.0-QuantumScale*q->opacity;
805 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
806 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
807 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
808 composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
810 composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
812 composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
814 if (q->colorspace == CMYKColorspace)
815 composite->index=gamma*LinearLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
819 static inline MagickRealType Mathematics(const MagickRealType Sca,
820 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
821 const GeometryInfo *geometry_info)
824 'Mathematics' a free form user control mathematical composition is defined
827 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
829 Where the arguments A,B,C,D are (currently) passed to composite as
830 a command separated 'geometry' string in "compose:args" image artifact.
832 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
834 Applying the SVG transparency formula (see above), we get...
836 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
838 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
841 return(geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
842 geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
846 static inline void CompositeMathematics(const MagickPixelPacket *p,
847 const MagickPixelPacket *q,const ChannelType channel, const GeometryInfo
848 *args, MagickPixelPacket *composite)
855 Sa=1.0-QuantumScale*GetOpacityPixelComponent(p); /* ??? - AT */
856 Da=1.0-QuantumScale*q->opacity;
857 if ( (channel & SyncChannels) != 0 ) {
858 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
859 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
860 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
861 composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
863 composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,QuantumScale*
864 q->green*Da,Da,args);
865 composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
867 if (q->colorspace == CMYKColorspace)
868 composite->index=gamma*Mathematics(QuantumScale*p->index*Sa,Sa,QuantumScale*
869 q->index*Da,Da,args);
871 else { /* handle channels as separate grayscale channels */
872 if ( (channel & AlphaChannel) != 0 )
873 composite->opacity=QuantumRange*(1.0-Mathematics(Sa,1.0,Da,1.0,args));
874 if ( (channel & RedChannel) != 0 )
875 composite->red=QuantumRange*
876 Mathematics(QuantumScale*p->red,1.0,QuantumScale*q->red,1.0,args);
877 if ( (channel & GreenChannel) != 0 )
878 composite->green=QuantumRange*
879 Mathematics(QuantumScale*p->green,1.0,QuantumScale*q->green,1.0,args);
880 if ( (channel & BlueChannel) != 0 )
881 composite->blue=QuantumRange*
882 Mathematics(QuantumScale*p->blue,1.0,QuantumScale*q->blue,1.0,args);
883 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
884 composite->index=QuantumRange*
885 Mathematics(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0,args);
890 static inline void CompositePlus(const MagickPixelPacket *p,
891 const MagickPixelPacket *q,const ChannelType channel,
892 MagickPixelPacket *composite)
894 if ( (channel & SyncChannels) != 0 ) {
896 NOTE: "Plus" does not use 'over' alpha-blending but uses a
897 special 'plus' form of alph-blending. It is the ONLY mathematical
898 operator to do this. this is what makes it different to the
899 otherwise equivelent "LinearDodge" composition method.
901 Note however that color channels are still effected by the alpha channel
902 as a result of the blending, making it just as useless for independant
903 channel maths, just like all other mathematical composition methods.
905 As such the removal of the 'sync' flag, is still a usful convention.
907 The MagickPixelCompositePlus() function is defined in
908 "composite-private.h" so it can also be used for Image Blending.
910 MagickPixelCompositePlus(p,p->opacity,q,q->opacity,composite);
912 else { /* handle channels as separate grayscale channels */
913 if ( (channel & AlphaChannel) != 0 )
914 composite->opacity=p->opacity+q->opacity-QuantumRange;
915 if ( (channel & RedChannel) != 0 )
916 composite->red=p->red+q->red;
917 if ( (channel & GreenChannel) != 0 )
918 composite->green=p->green+q->green;
919 if ( (channel & BlueChannel) != 0 )
920 composite->blue=p->blue+q->blue;
921 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
922 composite->index=p->index+q->index;
926 static inline MagickRealType Minus(const MagickRealType Sca,
927 const MagickRealType Sa,const MagickRealType Dca,
928 const MagickRealType magick_unused(Da))
930 return(Sca + Dca - 2*Dca*Sa);
933 static inline void CompositeMinus(const MagickPixelPacket *p,
934 const MagickPixelPacket *q,const ChannelType channel,
935 MagickPixelPacket *composite)
942 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
943 Da=1.0-QuantumScale*q->opacity;
944 if ( (channel & SyncChannels) != 0 ) {
945 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
946 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
947 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
948 composite->red=gamma*Minus(p->red*Sa,Sa,q->red*Da,Da);
949 composite->green=gamma*Minus(p->green*Sa,Sa,q->green*Da,Da);
950 composite->blue=gamma*Minus(p->blue*Sa,Sa,q->blue*Da,Da);
951 if (q->colorspace == CMYKColorspace)
952 composite->index=gamma*Minus(p->index*Sa,Sa,q->index*Da,Da);
954 else { /* handle channels as separate grayscale channels */
955 if ( (channel & AlphaChannel) != 0 )
956 composite->opacity=QuantumRange*(1.0-(Sa-Da));
957 if ( (channel & RedChannel) != 0 )
958 composite->red=p->red-q->red;
959 if ( (channel & GreenChannel) != 0 )
960 composite->green=p->green-q->green;
961 if ( (channel & BlueChannel) != 0 )
962 composite->blue=p->blue-q->blue;
963 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
964 composite->index=p->index-q->index;
968 static inline MagickRealType ModulusAdd(const MagickRealType p,
969 const MagickRealType Sa, const MagickRealType q, const MagickRealType Da)
975 if (pixel > QuantumRange)
976 pixel-=(QuantumRange+1.0);
977 return(pixel*Sa*Da + p*Sa*(1-Da) + q*Da*(1-Sa));
980 static inline void CompositeModulusAdd(const MagickPixelPacket *p,
981 const MagickPixelPacket *q, const ChannelType channel,
982 MagickPixelPacket *composite)
984 if ( (channel & SyncChannels) != 0 ) {
990 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
991 Da=1.0-QuantumScale*q->opacity;
992 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
993 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
994 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
995 composite->red=ModulusAdd(p->red,Sa,q->red,Da);
996 composite->green=ModulusAdd(p->green,Sa,q->green,Da);
997 composite->blue=ModulusAdd(p->blue,Sa,q->blue,Da);
998 if (q->colorspace == CMYKColorspace)
999 composite->index=ModulusAdd(p->index,Sa,q->index,Da);
1001 else { /* handle channels as separate grayscale channels */
1002 if ( (channel & AlphaChannel) != 0 )
1003 composite->opacity=QuantumRange-ModulusAdd(QuantumRange-p->opacity,
1004 1.0,QuantumRange-q->opacity,1.0);
1005 if ( (channel & RedChannel) != 0 )
1006 composite->red=ModulusAdd(p->red,1.0,q->red,1.0);
1007 if ( (channel & GreenChannel) != 0 )
1008 composite->green=ModulusAdd(p->green,1.0,q->green,1.0);
1009 if ( (channel & BlueChannel) != 0 )
1010 composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0);
1011 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1012 composite->index=ModulusAdd(p->index,1.0,q->index,1.0);
1016 static inline MagickRealType ModulusSubtract(const MagickRealType p,
1017 const MagickRealType Sa, const MagickRealType q, const MagickRealType Da)
1024 pixel+=(QuantumRange+1.0);
1025 return(pixel*Sa*Da + p*Sa*(1-Da) + q*Da*(1-Sa));
1028 static inline void CompositeModulusSubtract(const MagickPixelPacket *p,
1029 const MagickPixelPacket *q, const ChannelType channel,
1030 MagickPixelPacket *composite)
1032 if ( (channel & SyncChannels) != 0 ) {
1038 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1039 Da=1.0-QuantumScale*q->opacity;
1040 gamma = RoundToUnity(Sa+Da-Sa*Da);
1041 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1042 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1043 composite->red=ModulusSubtract(p->red,Sa,q->red,Da);
1044 composite->green=ModulusSubtract(p->green,Sa,q->green,Da);
1045 composite->blue=ModulusSubtract(p->blue,Sa,q->blue,Da);
1046 if (q->colorspace == CMYKColorspace)
1047 composite->index=ModulusSubtract(p->index,Sa,q->index,Da);
1049 else { /* handle channels as separate grayscale channels */
1050 if ( (channel & AlphaChannel) != 0 )
1051 composite->opacity=QuantumRange-ModulusSubtract(QuantumRange-p->opacity,
1052 1.0,QuantumRange-q->opacity,1.0);
1053 if ( (channel & RedChannel) != 0 )
1054 composite->red=ModulusSubtract(p->red,1.0,q->red,1.0);
1055 if ( (channel & GreenChannel) != 0 )
1056 composite->green=ModulusSubtract(p->green,1.0,q->green,1.0);
1057 if ( (channel & BlueChannel) != 0 )
1058 composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0);
1059 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1060 composite->index=ModulusSubtract(p->index,1.0,q->index,1.0);
1064 static inline MagickRealType Multiply(const MagickRealType Sca,
1065 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1067 return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1070 static inline void CompositeMultiply(const MagickPixelPacket *p,
1071 const MagickPixelPacket *q,const ChannelType channel,
1072 MagickPixelPacket *composite)
1079 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1080 Da=1.0-QuantumScale*q->opacity;
1081 if ( (channel & SyncChannels) != 0 ) {
1082 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1083 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1084 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1085 composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
1087 composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
1089 composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1091 if (q->colorspace == CMYKColorspace)
1092 composite->index=gamma*Multiply(QuantumScale*p->index*Sa,Sa,QuantumScale*
1095 else { /* handle channels as separate grayscale channels */
1096 if ( (channel & AlphaChannel) != 0 )
1097 composite->opacity=QuantumRange*(1.0-Sa*Da);
1098 if ( (channel & RedChannel) != 0 )
1099 composite->red=QuantumScale*p->red*q->red;
1100 if ( (channel & GreenChannel) != 0 )
1101 composite->green=QuantumScale*p->green*q->green;
1102 if ( (channel & BlueChannel) != 0 )
1103 composite->blue=QuantumScale*p->blue*q->blue;
1104 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1105 composite->index=QuantumScale*p->index*q->index;
1109 static inline MagickRealType Out(const MagickRealType p,
1110 const MagickRealType Sa,const MagickRealType magick_unused(q),
1111 const MagickRealType Da)
1113 return(Sa*p*(1.0-Da));
1116 static inline void CompositeOut(const MagickPixelPacket *p,
1117 const MagickPixelPacket *q,MagickPixelPacket *composite)
1124 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1125 Da=1.0-QuantumScale*q->opacity;
1127 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1128 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1129 composite->red=gamma*Out(p->red,Sa,q->red,Da);
1130 composite->green=gamma*Out(p->green,Sa,q->green,Da);
1131 composite->blue=gamma*Out(p->blue,Sa,q->blue,Da);
1132 if (q->colorspace == CMYKColorspace)
1133 composite->index=gamma*Out(p->index,Sa,q->index,Da);
1136 static MagickRealType PegtopLight(const MagickRealType Sca,
1137 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1140 PegTop: A Soft-Light alternative: A continuous version of the Softlight
1141 function, producing very similar results.
1143 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
1145 See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
1147 if (fabs(Da) < MagickEpsilon)
1149 return(Dca*Dca*(Sa-2*Sca)/Da+Sca*(2*Dca+1-Da)+Dca*(1-Sa));
1152 static inline void CompositePegtopLight(const MagickPixelPacket *p,
1153 const MagickPixelPacket *q,MagickPixelPacket *composite)
1160 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1161 Da=1.0-QuantumScale*q->opacity;
1162 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1163 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1164 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1165 composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1167 composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1169 composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1171 if (q->colorspace == CMYKColorspace)
1172 composite->index=gamma*PegtopLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1176 static MagickRealType PinLight(const MagickRealType Sca,
1177 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1180 PinLight: A Photoshop 7 composition method
1181 http://www.simplefilter.de/en/basics/mixmods.html
1183 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
1185 if (Dca*Sa < Da*(2*Sca-Sa))
1186 return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
1187 if ((Dca*Sa) > (2*Sca*Da))
1188 return(Sca*Da+Sca+Dca*(1.0-Sa));
1189 return(Sca*(1.0-Da)+Dca);
1192 static inline void CompositePinLight(const MagickPixelPacket *p,
1193 const MagickPixelPacket *q,MagickPixelPacket *composite)
1200 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1201 Da=1.0-QuantumScale*q->opacity;
1202 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1203 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1204 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1205 composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1207 composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1209 composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1211 if (q->colorspace == CMYKColorspace)
1212 composite->index=gamma*PinLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1216 static inline MagickRealType Screen(const MagickRealType Sca,
1217 const MagickRealType Dca)
1219 /* Screen: A negated multiply
1220 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
1222 return(Sca+Dca-Sca*Dca);
1225 static inline void CompositeScreen(const MagickPixelPacket *p,
1226 const MagickPixelPacket *q,const ChannelType channel,
1227 MagickPixelPacket *composite)
1234 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1235 Da=1.0-QuantumScale*q->opacity;
1236 if ( (channel & SyncChannels) != 0 ) {
1237 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1238 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1239 Sa*=QuantumScale; Da*=QuantumScale; /* optimization */
1240 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1241 composite->red=gamma*Screen(p->red*Sa,q->red*Da);
1242 composite->green=gamma*Screen(p->green*Sa,q->green*Da);
1243 composite->blue=gamma*Screen(p->blue*Sa,q->blue*Da);
1244 if (q->colorspace == CMYKColorspace)
1245 composite->index=gamma*Screen(p->index*Sa,q->index*Da);
1247 else { /* handle channels as separate grayscale channels */
1248 if ( (channel & AlphaChannel) != 0 )
1249 composite->opacity=QuantumRange*(1.0-Screen(Sa,Da));
1250 if ( (channel & RedChannel) != 0 )
1251 composite->red=QuantumRange*Screen(QuantumScale*p->red,
1252 QuantumScale*q->red);
1253 if ( (channel & GreenChannel) != 0 )
1254 composite->green=QuantumRange*Screen(QuantumScale*p->green,
1255 QuantumScale*q->green);
1256 if ( (channel & BlueChannel) != 0 )
1257 composite->blue=QuantumRange*Screen(QuantumScale*p->blue,
1258 QuantumScale*q->blue);
1259 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1260 composite->index=QuantumRange*Screen(QuantumScale*p->index,
1261 QuantumScale*q->index);
1265 static MagickRealType SoftLight(const MagickRealType Sca,
1266 const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
1270 Oct 2004 SVG specification -- was found to be incorrect
1271 See http://lists.w3.org/Archives/Public/www-svg/2009Feb/0014.html.
1274 return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1276 return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa)*(3.0-8.0*Dca/Da))+
1277 Sca*(1.0-Da)+Dca*(1.0-Sa));
1278 return((Dca*Sa+(pow(Dca/Da,0.5)*Da-Dca)*(2.0*Sca-Sa))+Sca*(1.0-Da)+
1286 New specification: March 2009 SVG specification.
1290 return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1291 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1293 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
1294 alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1297 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1302 static inline void CompositeSoftLight(const MagickPixelPacket *p,
1303 const MagickPixelPacket *q,MagickPixelPacket *composite)
1310 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1311 Da=1.0-QuantumScale*q->opacity;
1312 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1313 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1314 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1315 composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1317 composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1319 composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1321 if (q->colorspace == CMYKColorspace)
1322 composite->index=gamma*SoftLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1328 Multiply difference by amount, if differance larger than threshold???
1329 What use this is is completely unknown
1330 The Opacity calculation appears to be inverted -- Anthony Thyssen
1332 static inline MagickRealType Threshold(const MagickRealType p,
1333 const MagickRealType q,const MagickRealType threshold,
1334 const MagickRealType amount)
1340 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
1342 return(q+delta*amount);
1345 static inline void CompositeThreshold(const MagickPixelPacket *p,
1346 const MagickPixelPacket *q,const MagickRealType threshold,
1347 const MagickRealType amount,MagickPixelPacket *composite)
1349 composite->red=Threshold(p->red,q->red,threshold,amount);
1350 composite->green=Threshold(p->green,q->green,threshold,amount);
1351 composite->blue=Threshold(p->blue,q->blue,threshold,amount);
1352 composite->opacity=QuantumRange-Threshold(p->opacity,q->opacity,
1354 if (q->colorspace == CMYKColorspace)
1355 composite->index=Threshold(p->index,q->index,threshold,amount);
1359 static MagickRealType VividLight(const MagickRealType Sca,
1360 const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
1363 VividLight: A Photoshop 7 composition method. See
1364 http://www.simplefilter.de/en/basics/mixmods.html.
1366 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
1368 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
1369 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1371 return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1372 return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1375 static inline void CompositeVividLight(const MagickPixelPacket *p,
1376 const MagickPixelPacket *q,MagickPixelPacket *composite)
1383 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1384 Da=1.0-QuantumScale*q->opacity;
1385 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1386 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1387 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1388 composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1390 composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1392 composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1394 if (q->colorspace == CMYKColorspace)
1395 composite->index=gamma*VividLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1399 static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
1400 const MagickRealType Dca,const MagickRealType Da)
1402 return(Sca*(1-Da)+Dca*(1-Sa));
1405 static inline void CompositeXor(const MagickPixelPacket *p,
1406 const MagickPixelPacket *q,MagickPixelPacket *composite)
1413 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1414 Da=1.0-QuantumScale*q->opacity;
1415 gamma=Sa+Da-2*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
1416 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1417 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1418 composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
1419 composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
1420 composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
1421 if (q->colorspace == CMYKColorspace)
1422 composite->index=gamma*Xor(p->index*Sa,Sa,q->index*Da,Da);
1425 static void HSBComposite(const double hue,const double saturation,
1426 const double brightness,MagickRealType *red,MagickRealType *green,
1427 MagickRealType *blue)
1437 Convert HSB to RGB colorspace.
1439 assert(red != (MagickRealType *) NULL);
1440 assert(green != (MagickRealType *) NULL);
1441 assert(blue != (MagickRealType *) NULL);
1442 if (saturation == 0.0)
1444 *red=(MagickRealType) QuantumRange*brightness;
1449 h=6.0*(hue-floor(hue));
1450 f=h-floor((double) h);
1451 p=brightness*(1.0-saturation);
1452 q=brightness*(1.0-saturation*f);
1453 t=brightness*(1.0-saturation*(1.0-f));
1459 *red=(MagickRealType) QuantumRange*brightness;
1460 *green=(MagickRealType) QuantumRange*t;
1461 *blue=(MagickRealType) QuantumRange*p;
1466 *red=(MagickRealType) QuantumRange*q;
1467 *green=(MagickRealType) QuantumRange*brightness;
1468 *blue=(MagickRealType) QuantumRange*p;
1473 *red=(MagickRealType) QuantumRange*p;
1474 *green=(MagickRealType) QuantumRange*brightness;
1475 *blue=(MagickRealType) QuantumRange*t;
1480 *red=(MagickRealType) QuantumRange*p;
1481 *green=(MagickRealType) QuantumRange*q;
1482 *blue=(MagickRealType) QuantumRange*brightness;
1487 *red=(MagickRealType) QuantumRange*t;
1488 *green=(MagickRealType) QuantumRange*p;
1489 *blue=(MagickRealType) QuantumRange*brightness;
1494 *red=(MagickRealType) QuantumRange*brightness;
1495 *green=(MagickRealType) QuantumRange*p;
1496 *blue=(MagickRealType) QuantumRange*q;
1502 MagickExport MagickBooleanType CompositeImage(Image *image,
1503 const CompositeOperator compose,const Image *composite_image,
1504 const ssize_t x_offset,const ssize_t y_offset)
1509 status=CompositeImageChannel(image,DefaultChannels,compose,composite_image,
1514 MagickExport MagickBooleanType CompositeImageChannel(Image *image,
1515 const ChannelType channel,const CompositeOperator compose,
1516 const Image *composite_image,const ssize_t x_offset,const ssize_t y_offset)
1518 #define CompositeImageTag "Composite/Image"
1540 modify_outside_overlay,
1551 destination_dissolve,
1565 Prepare composite image.
1567 assert(image != (Image *) NULL);
1568 assert(image->signature == MagickSignature);
1569 if (image->debug != MagickFalse)
1570 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1571 assert(composite_image != (Image *) NULL);
1572 assert(composite_image->signature == MagickSignature);
1573 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1574 return(MagickFalse);
1575 GetMagickPixelPacket(image,&zero);
1576 destination_image=(Image *) NULL;
1578 destination_dissolve=1.0;
1579 modify_outside_overlay=MagickFalse;
1580 percent_brightness=100.0;
1581 percent_saturation=100.0;
1582 source_dissolve=1.0;
1586 case ClearCompositeOp:
1587 case SrcCompositeOp:
1589 case SrcInCompositeOp:
1590 case OutCompositeOp:
1591 case SrcOutCompositeOp:
1592 case DstInCompositeOp:
1593 case DstAtopCompositeOp:
1596 Modify destination outside the overlaid region.
1598 modify_outside_overlay=MagickTrue;
1601 case OverCompositeOp:
1603 if (image->matte != MagickFalse)
1605 if (composite_image->matte != MagickFalse)
1608 case CopyCompositeOp:
1610 if ((x_offset < 0) || (y_offset < 0))
1612 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
1614 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
1617 exception=(&image->exception);
1618 image_view=AcquireCacheView(image);
1619 composite_view=AcquireCacheView(composite_image);
1620 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1621 #pragma omp parallel for schedule(dynamic,4) shared(status)
1623 for (y=0; y < (ssize_t) composite_image->rows; y++)
1628 register const IndexPacket
1631 register const PixelPacket
1634 register IndexPacket
1637 register PixelPacket
1640 if (status == MagickFalse)
1642 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1644 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1645 composite_image->columns,1,exception);
1646 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1651 composite_indexes=GetCacheViewVirtualIndexQueue(composite_view);
1652 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1653 (void) CopyMagickMemory(q,p,composite_image->columns*sizeof(*p));
1654 if ((indexes != (IndexPacket *) NULL) &&
1655 (composite_indexes != (const IndexPacket *) NULL))
1656 (void) CopyMagickMemory(indexes,composite_indexes,
1657 composite_image->columns*sizeof(*indexes));
1658 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1659 if (sync == MagickFalse)
1661 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1666 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1667 #pragma omp critical (MagickCore_CompositeImage)
1669 proceed=SetImageProgress(image,CompositeImageTag,
1670 (MagickOffsetType) y,image->rows);
1671 if (proceed == MagickFalse)
1675 composite_view=DestroyCacheView(composite_view);
1676 image_view=DestroyCacheView(image_view);
1679 case CopyOpacityCompositeOp:
1680 case ChangeMaskCompositeOp:
1683 Modify destination outside the overlaid region and require an alpha
1684 channel to exist, to add transparency.
1686 if (image->matte == MagickFalse)
1687 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1688 modify_outside_overlay=MagickTrue;
1691 case BlurCompositeOp:
1713 Blur Image dictated by an overlay gradient map: X = red_channel;
1714 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
1716 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1718 if (destination_image == (Image *) NULL)
1719 return(MagickFalse);
1721 Determine the horizontal and vertical maximim blur.
1723 SetGeometryInfo(&geometry_info);
1725 value=GetImageArtifact(composite_image,"compose:args");
1726 if (value != (char *) NULL)
1727 flags=ParseGeometry(value,&geometry_info);
1728 if ((flags & WidthValue) == 0 )
1730 destination_image=DestroyImage(destination_image);
1731 return(MagickFalse);
1733 width=geometry_info.rho;
1734 height=geometry_info.sigma;
1735 blur.x1=geometry_info.rho;
1738 blur.y2=geometry_info.sigma;
1741 if ((flags & HeightValue) == 0)
1743 if ((flags & XValue) != 0 )
1748 angle=DegreesToRadians(geometry_info.xi);
1749 blur.x1=width*cos(angle);
1750 blur.x2=width*sin(angle);
1751 blur.y1=(-height*sin(angle));
1752 blur.y2=height*cos(angle);
1754 if ((flags & YValue) != 0 )
1756 angle_start=DegreesToRadians(geometry_info.xi);
1757 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1760 Blur Image by resampling;
1763 exception=(&image->exception);
1764 resample_filter=AcquireResampleFilter(image,&image->exception);
1765 SetResampleFilter(resample_filter,GaussianFilter,sqrt(2.0));
1766 destination_view=AcquireCacheView(destination_image);
1767 composite_view=AcquireCacheView(composite_image);
1768 for (y=0; y < (ssize_t) composite_image->rows; y++)
1773 register const PixelPacket
1776 register PixelPacket
1779 register IndexPacket
1780 *restrict destination_indexes;
1785 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1787 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1789 r=QueueCacheViewAuthenticPixels(destination_view,0,y,
1790 destination_image->columns,1,&image->exception);
1791 if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
1793 destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
1794 for (x=0; x < (ssize_t) composite_image->columns; x++)
1796 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1801 if (fabs(angle_range) > MagickEpsilon)
1806 angle=angle_start+angle_range*QuantumScale*
1807 GetBluePixelComponent(p);
1808 blur.x1=width*cos(angle);
1809 blur.x2=width*sin(angle);
1810 blur.y1=(-height*sin(angle));
1811 blur.y2=height*cos(angle);
1813 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*p->red,
1814 blur.y1*QuantumScale*p->green,blur.x2*QuantumScale*p->red,
1815 blur.y2*QuantumScale*GetGreenPixelComponent(p));
1816 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1817 (double) y_offset+y,&pixel);
1818 SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
1822 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1823 if (sync == MagickFalse)
1826 resample_filter=DestroyResampleFilter(resample_filter);
1827 composite_view=DestroyCacheView(composite_view);
1828 destination_view=DestroyCacheView(destination_view);
1829 composite_image=destination_image;
1832 case DisplaceCompositeOp:
1833 case DistortCompositeOp:
1850 register IndexPacket
1851 *restrict destination_indexes;
1853 register PixelPacket
1860 Displace/Distort based on overlay gradient map:
1861 X = red_channel; Y = green_channel;
1862 compose:args = x_scale[,y_scale[,center.x,center.y]]
1864 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1866 if (destination_image == (Image *) NULL)
1867 return(MagickFalse);
1868 SetGeometryInfo(&geometry_info);
1870 value=GetImageArtifact(composite_image,"compose:args");
1871 if (value != (char *) NULL)
1872 flags=ParseGeometry(value,&geometry_info);
1873 if ((flags & (WidthValue|HeightValue)) == 0 )
1875 if ((flags & AspectValue) == 0)
1877 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1879 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1883 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1884 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1889 horizontal_scale=geometry_info.rho;
1890 vertical_scale=geometry_info.sigma;
1891 if ((flags & PercentValue) != 0)
1893 if ((flags & AspectValue) == 0)
1895 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1896 vertical_scale*=(composite_image->rows-1.0)/200.0;
1900 horizontal_scale*=(image->columns-1.0)/200.0;
1901 vertical_scale*=(image->rows-1.0)/200.0;
1904 if ((flags & HeightValue) == 0)
1905 vertical_scale=horizontal_scale;
1908 Determine fixed center point for absolute distortion map
1910 Displace offset relative to a fixed absolute point
1911 Select that point according to +X+Y user inputs.
1912 default = center of overlay image
1913 arg flag '!' = locations/percentage relative to background image
1915 center.x=(MagickRealType) x_offset;
1916 center.y=(MagickRealType) y_offset;
1917 if (compose == DistortCompositeOp)
1919 if ((flags & XValue) == 0)
1920 if ((flags & AspectValue) == 0)
1921 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
1924 center.x=((MagickRealType) image->columns-1)/2.0;
1926 if ((flags & AspectValue) == 0)
1927 center.x=(MagickRealType) x_offset+geometry_info.xi;
1929 center.x=geometry_info.xi;
1930 if ((flags & YValue) == 0)
1931 if ((flags & AspectValue) == 0)
1932 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
1934 center.y=((MagickRealType) image->rows-1)/2.0;
1936 if ((flags & AspectValue) == 0)
1937 center.y=(MagickRealType) y_offset+geometry_info.psi;
1939 center.y=geometry_info.psi;
1942 Shift the pixel offset point as defined by the provided,
1943 displacement/distortion map. -- Like a lens...
1946 exception=(&image->exception);
1947 resample_filter=AcquireResampleFilter(image,&image->exception);
1948 destination_view=AcquireCacheView(destination_image);
1949 composite_view=AcquireCacheView(composite_image);
1950 for (y=0; y < (ssize_t) composite_image->rows; y++)
1955 register const PixelPacket
1961 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1963 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1965 r=QueueCacheViewAuthenticPixels(destination_view,0,y,
1966 destination_image->columns,1,&image->exception);
1967 if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
1969 destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
1970 for (x=0; x < (ssize_t) composite_image->columns; x++)
1972 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1978 Displace the offset.
1980 offset.x=(horizontal_scale*(p->red-(((MagickRealType) QuantumRange+
1981 1.0)/2.0)))/(((MagickRealType) QuantumRange+1.0)/2.0)+
1982 center.x+((compose == DisplaceCompositeOp) ? x : 0);
1983 offset.y=(vertical_scale*(p->green-(((MagickRealType) QuantumRange+
1984 1.0)/2.0)))/(((MagickRealType) QuantumRange+1.0)/2.0)+
1985 center.y+((compose == DisplaceCompositeOp) ? y : 0);
1986 (void) ResamplePixelColor(resample_filter,(double) offset.x,
1987 (double) offset.y,&pixel);
1989 Mask with the 'invalid pixel mask' in alpha channel.
1991 pixel.opacity=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1992 pixel.opacity)*(1.0-QuantumScale*GetOpacityPixelComponent(p)));
1993 SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
1997 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1998 if (sync == MagickFalse)
2001 resample_filter=DestroyResampleFilter(resample_filter);
2002 composite_view=DestroyCacheView(composite_view);
2003 destination_view=DestroyCacheView(destination_view);
2004 composite_image=destination_image;
2007 case DissolveCompositeOp:
2010 Geometry arguments to dissolve factors.
2012 value=GetImageArtifact(composite_image,"compose:args");
2013 if (value != (char *) NULL)
2015 flags=ParseGeometry(value,&geometry_info);
2016 source_dissolve=geometry_info.rho/100.0;
2017 destination_dissolve=1.0;
2018 if ((source_dissolve-MagickEpsilon) < 0.0)
2019 source_dissolve=0.0;
2020 if ((source_dissolve+MagickEpsilon) > 1.0)
2022 destination_dissolve=2.0-source_dissolve;
2023 source_dissolve=1.0;
2025 if ((flags & SigmaValue) != 0)
2026 destination_dissolve=geometry_info.sigma/100.0;
2027 if ((destination_dissolve-MagickEpsilon) < 0.0)
2028 destination_dissolve=0.0;
2029 modify_outside_overlay=MagickTrue;
2030 if ((destination_dissolve+MagickEpsilon) > 1.0 )
2032 destination_dissolve=1.0;
2033 modify_outside_overlay=MagickFalse;
2038 case BlendCompositeOp:
2040 value=GetImageArtifact(composite_image,"compose:args");
2041 if (value != (char *) NULL)
2043 flags=ParseGeometry(value,&geometry_info);
2044 source_dissolve=geometry_info.rho/100.0;
2045 destination_dissolve=1.0-source_dissolve;
2046 if ((flags & SigmaValue) != 0)
2047 destination_dissolve=geometry_info.sigma/100.0;
2048 modify_outside_overlay=MagickTrue;
2049 if ((destination_dissolve+MagickEpsilon) > 1.0)
2050 modify_outside_overlay=MagickFalse;
2054 case MathematicsCompositeOp:
2057 Just collect the values from "compose:args", setting.
2058 Unused values are set to zero automagically.
2060 Arguments are normally a comma separated list, so this probably should
2061 be changed to some 'general comma list' parser, (with a minimum
2064 SetGeometryInfo(&geometry_info);
2065 value=GetImageArtifact(composite_image,"compose:args");
2066 if (value != (char *) NULL)
2067 (void) ParseGeometry(value,&geometry_info);
2070 case ModulateCompositeOp:
2073 Determine the brightness and saturation scale.
2075 value=GetImageArtifact(composite_image,"compose:args");
2076 if (value != (char *) NULL)
2078 flags=ParseGeometry(value,&geometry_info);
2079 percent_brightness=geometry_info.rho;
2080 if ((flags & SigmaValue) != 0)
2081 percent_saturation=geometry_info.sigma;
2085 case ThresholdCompositeOp:
2088 Determine the amount and threshold.
2089 This Composition method is depreciated
2091 value=GetImageArtifact(composite_image,"compose:args");
2092 if (value != (char *) NULL)
2094 flags=ParseGeometry(value,&geometry_info);
2095 amount=geometry_info.rho;
2096 threshold=geometry_info.sigma;
2097 if ((flags & SigmaValue) == 0)
2100 threshold*=QuantumRange;
2106 value=GetImageArtifact(composite_image,"compose:outside-overlay");
2107 if (value != (const char *) NULL)
2108 modify_outside_overlay=IsMagickTrue(value);
2114 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2115 GetMagickPixelPacket(composite_image,&zero);
2116 exception=(&image->exception);
2117 image_view=AcquireCacheView(image);
2118 composite_view=AcquireCacheView(composite_image);
2119 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2120 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2122 for (y=0; y < (ssize_t) image->rows; y++)
2137 register const IndexPacket
2138 *restrict composite_indexes;
2140 register const PixelPacket
2143 register IndexPacket
2149 register PixelPacket
2152 if (status == MagickFalse)
2154 if (modify_outside_overlay == MagickFalse)
2158 if ((y-y_offset) >= (ssize_t) composite_image->rows)
2162 If pixels is NULL, y is outside overlay region.
2164 pixels=(PixelPacket *) NULL;
2165 p=(PixelPacket *) NULL;
2166 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
2168 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
2169 composite_image->columns,1,exception);
2170 if (p == (const PixelPacket *) NULL)
2179 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2181 if (q == (PixelPacket *) NULL)
2186 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2187 composite_indexes=GetCacheViewVirtualIndexQueue(composite_view);
2193 for (x=0; x < (ssize_t) image->columns; x++)
2195 if (modify_outside_overlay == MagickFalse)
2202 if ((x-x_offset) >= (ssize_t) composite_image->columns)
2205 destination.red=(MagickRealType) q->red;
2206 destination.green=(MagickRealType) q->green;
2207 destination.blue=(MagickRealType) q->blue;
2208 if (image->matte != MagickFalse)
2209 destination.opacity=(MagickRealType) q->opacity;
2210 if (image->colorspace == CMYKColorspace)
2212 destination.red=(MagickRealType) QuantumRange-destination.red;
2213 destination.green=(MagickRealType) QuantumRange-destination.green;
2214 destination.blue=(MagickRealType) QuantumRange-destination.blue;
2215 destination.index=(MagickRealType) (QuantumRange-indexes[x]);
2218 Handle destination modifications outside overlaid region.
2220 composite=destination;
2221 if ((pixels == (PixelPacket *) NULL) || (x < x_offset) ||
2222 ((x-x_offset) >= (ssize_t) composite_image->columns))
2226 case DissolveCompositeOp:
2227 case BlendCompositeOp:
2229 composite.opacity=(MagickRealType) (QuantumRange-
2230 destination_dissolve*(QuantumRange-composite.opacity));
2233 case ClearCompositeOp:
2234 case SrcCompositeOp:
2236 CompositeClear(&destination,&composite);
2240 case SrcInCompositeOp:
2241 case OutCompositeOp:
2242 case SrcOutCompositeOp:
2243 case DstInCompositeOp:
2244 case DstAtopCompositeOp:
2245 case CopyOpacityCompositeOp:
2246 case ChangeMaskCompositeOp:
2248 composite.opacity=(MagickRealType) TransparentOpacity;
2253 (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,
2254 y-y_offset,&composite,exception);
2258 if (image->colorspace == CMYKColorspace)
2260 composite.red=(MagickRealType) QuantumRange-composite.red;
2261 composite.green=(MagickRealType) QuantumRange-composite.green;
2262 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2263 composite.index=(MagickRealType) QuantumRange-composite.index;
2265 q->red=ClampToQuantum(composite.red);
2266 q->green=ClampToQuantum(composite.green);
2267 q->blue=ClampToQuantum(composite.blue);
2268 if (image->matte != MagickFalse)
2269 q->opacity=ClampToQuantum(composite.opacity);
2270 if (image->colorspace == CMYKColorspace)
2271 indexes[x]=ClampToQuantum(composite.index);
2276 Handle normal overlay of source onto destination.
2278 source.red=(MagickRealType) GetRedPixelComponent(p);
2279 source.green=(MagickRealType) GetGreenPixelComponent(p);
2280 source.blue=(MagickRealType) GetBluePixelComponent(p);
2281 if (composite_image->matte != MagickFalse)
2282 source.opacity=(MagickRealType) GetOpacityPixelComponent(p);
2283 if (composite_image->colorspace == CMYKColorspace)
2285 source.red=(MagickRealType) QuantumRange-source.red;
2286 source.green=(MagickRealType) QuantumRange-source.green;
2287 source.blue=(MagickRealType) QuantumRange-source.blue;
2288 source.index=(MagickRealType) QuantumRange-(MagickRealType)
2289 composite_indexes[x-x_offset];
2293 /* Duff-Porter Compositions */
2294 case ClearCompositeOp:
2296 CompositeClear(&destination,&composite);
2299 case SrcCompositeOp:
2300 case CopyCompositeOp:
2301 case ReplaceCompositeOp:
2307 case DstCompositeOp:
2309 case OverCompositeOp:
2310 case SrcOverCompositeOp:
2312 MagickPixelCompositeOver(&source,source.opacity,
2313 &destination,destination.opacity,&composite);
2316 case DstOverCompositeOp:
2318 MagickPixelCompositeOver(&destination,destination.opacity,
2319 &source,source.opacity,&composite);
2322 case SrcInCompositeOp:
2325 CompositeIn(&source,&destination,&composite);
2328 case DstInCompositeOp:
2330 CompositeIn(&destination,&source,&composite);
2333 case OutCompositeOp:
2334 case SrcOutCompositeOp:
2336 CompositeOut(&source,&destination,&composite);
2339 case DstOutCompositeOp:
2341 CompositeOut(&destination,&source,&composite);
2344 case AtopCompositeOp:
2345 case SrcAtopCompositeOp:
2347 CompositeAtop(&source,&destination,&composite);
2350 case DstAtopCompositeOp:
2352 CompositeAtop(&destination,&source,&composite);
2355 case XorCompositeOp:
2357 CompositeXor(&source,&destination,&composite);
2360 /* Mathematical Compositions */
2361 case PlusCompositeOp:
2363 CompositePlus(&source,&destination,channel,&composite);
2366 case MinusCompositeOp:
2368 CompositeMinus(&source,&destination,
2369 channel,&composite);
2372 case ModulusAddCompositeOp:
2374 CompositeModulusAdd(&source,&destination,channel,&composite);
2377 case ModulusSubtractCompositeOp:
2379 CompositeModulusSubtract(&source,&destination,channel,&composite);
2382 case DifferenceCompositeOp:
2384 CompositeDifference(&source,&destination,channel,&composite);
2387 case ExclusionCompositeOp:
2389 CompositeExclusion(&source,&destination,channel,&composite);
2392 case MultiplyCompositeOp:
2394 CompositeMultiply(&source,&destination,channel,&composite);
2397 case ScreenCompositeOp:
2399 CompositeScreen(&source,&destination,channel,&composite);
2402 case DivideCompositeOp:
2404 CompositeDivide(&source,&destination,channel,&composite);
2407 case DarkenCompositeOp:
2409 CompositeDarken(&source,&destination,channel,&composite);
2412 case LightenCompositeOp:
2414 CompositeLighten(&source,&destination,channel,&composite);
2417 case MathematicsCompositeOp:
2419 CompositeMathematics(&source,&destination,channel,&geometry_info,
2423 /* Lighting Compositions */
2424 case ColorDodgeCompositeOp:
2426 CompositeColorDodge(&source,&destination,&composite);
2429 case ColorBurnCompositeOp:
2431 CompositeColorBurn(&source,&destination,&composite);
2434 case LinearDodgeCompositeOp:
2436 CompositeLinearDodge(&source,&destination,&composite);
2439 case LinearBurnCompositeOp:
2441 CompositeLinearBurn(&source,&destination,&composite);
2444 case HardLightCompositeOp:
2446 CompositeHardLight(&source,&destination,&composite);
2449 case OverlayCompositeOp:
2451 /* Overlay = Reversed HardLight. */
2452 CompositeHardLight(&destination,&source,&composite);
2455 case SoftLightCompositeOp:
2457 CompositeSoftLight(&source,&destination,&composite);
2460 case LinearLightCompositeOp:
2462 CompositeLinearLight(&source,&destination,&composite);
2465 case PegtopLightCompositeOp:
2467 CompositePegtopLight(&source,&destination,&composite);
2470 case VividLightCompositeOp:
2472 CompositeVividLight(&source,&destination,&composite);
2475 case PinLightCompositeOp:
2477 CompositePinLight(&source,&destination,&composite);
2480 /* Other Composition */
2481 case ChangeMaskCompositeOp:
2483 if ((composite.opacity > ((MagickRealType) QuantumRange/2.0)) ||
2484 (IsMagickColorSimilar(&source,&destination) != MagickFalse))
2485 composite.opacity=(MagickRealType) TransparentOpacity;
2487 composite.opacity=(MagickRealType) OpaqueOpacity;
2490 case BumpmapCompositeOp:
2492 if (source.opacity == TransparentOpacity)
2494 CompositeBumpmap(&source,&destination,&composite);
2497 case DissolveCompositeOp:
2499 MagickPixelCompositeOver(&source,(MagickRealType) (QuantumRange-source_dissolve*
2500 (QuantumRange-source.opacity)),&destination,(MagickRealType)
2501 (QuantumRange-destination_dissolve*(QuantumRange-
2502 destination.opacity)),&composite);
2505 case BlendCompositeOp:
2507 MagickPixelCompositeBlend(&source,source_dissolve,&destination,
2508 destination_dissolve,&composite);
2511 case ThresholdCompositeOp:
2513 CompositeThreshold(&source,&destination,threshold,amount,&composite);
2516 case ModulateCompositeOp:
2521 if (source.opacity == TransparentOpacity)
2523 offset=(ssize_t) (MagickPixelIntensityToQuantum(&source)-midpoint);
2526 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2527 &saturation,&brightness);
2528 brightness+=(0.01*percent_brightness*offset)/midpoint;
2529 saturation*=0.01*percent_saturation;
2530 HSBComposite(hue,saturation,brightness,&composite.red,
2531 &composite.green,&composite.blue);
2534 case HueCompositeOp:
2536 if (source.opacity == TransparentOpacity)
2538 if (destination.opacity == TransparentOpacity)
2543 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2544 &saturation,&brightness);
2545 CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
2546 HSBComposite(hue,saturation,brightness,&composite.red,
2547 &composite.green,&composite.blue);
2548 if (source.opacity < destination.opacity)
2549 composite.opacity=source.opacity;
2552 case SaturateCompositeOp:
2554 if (source.opacity == TransparentOpacity)
2556 if (destination.opacity == TransparentOpacity)
2561 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2562 &saturation,&brightness);
2563 CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
2565 HSBComposite(hue,saturation,brightness,&composite.red,
2566 &composite.green,&composite.blue);
2567 if (source.opacity < destination.opacity)
2568 composite.opacity=source.opacity;
2571 case LuminizeCompositeOp:
2573 if (source.opacity == TransparentOpacity)
2575 if (destination.opacity == TransparentOpacity)
2580 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2581 &saturation,&brightness);
2582 CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
2584 HSBComposite(hue,saturation,brightness,&composite.red,
2585 &composite.green,&composite.blue);
2586 if (source.opacity < destination.opacity)
2587 composite.opacity=source.opacity;
2590 case ColorizeCompositeOp:
2592 if (source.opacity == TransparentOpacity)
2594 if (destination.opacity == TransparentOpacity)
2599 CompositeHSB(destination.red,destination.green,destination.blue,&sans,
2601 CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
2603 HSBComposite(hue,saturation,brightness,&composite.red,
2604 &composite.green,&composite.blue);
2605 if (source.opacity < destination.opacity)
2606 composite.opacity=source.opacity;
2609 case CopyRedCompositeOp:
2610 case CopyCyanCompositeOp:
2612 composite.red=source.red;
2615 case CopyGreenCompositeOp:
2616 case CopyMagentaCompositeOp:
2618 composite.green=source.green;
2621 case CopyBlueCompositeOp:
2622 case CopyYellowCompositeOp:
2624 composite.blue=source.blue;
2627 case CopyOpacityCompositeOp:
2629 if (source.matte == MagickFalse)
2631 composite.opacity=(MagickRealType) (QuantumRange-
2632 MagickPixelIntensityToQuantum(&source));
2635 composite.opacity=source.opacity;
2638 case CopyBlackCompositeOp:
2640 if (source.colorspace != CMYKColorspace)
2641 ConvertRGBToCMYK(&source);
2642 composite.index=source.index;
2645 /* compose methods that are already handled */
2646 case BlurCompositeOp:
2647 case DisplaceCompositeOp:
2648 case DistortCompositeOp:
2656 if (image->colorspace == CMYKColorspace)
2658 composite.red=(MagickRealType) QuantumRange-composite.red;
2659 composite.green=(MagickRealType) QuantumRange-composite.green;
2660 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2661 composite.index=(MagickRealType) QuantumRange-composite.index;
2663 q->red=ClampToQuantum(composite.red);
2664 q->green=ClampToQuantum(composite.green);
2665 q->blue=ClampToQuantum(composite.blue);
2666 q->opacity=ClampToQuantum(composite.opacity);
2667 if (image->colorspace == CMYKColorspace)
2668 indexes[x]=ClampToQuantum(composite.index);
2670 if (p >= (pixels+composite_image->columns))
2674 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2676 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2681 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2682 #pragma omp critical (MagickCore_CompositeImageChannel)
2684 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2686 if (proceed == MagickFalse)
2690 composite_view=DestroyCacheView(composite_view);
2691 image_view=DestroyCacheView(image_view);
2692 if (destination_image != (Image * ) NULL)
2693 destination_image=DestroyImage(destination_image);
2698 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2702 % T e x t u r e I m a g e %
2706 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2708 % TextureImage() repeatedly tiles the texture image across and down the image
2711 % The format of the TextureImage method is:
2713 % MagickBooleanType TextureImage(Image *image,const Image *texture)
2715 % A description of each parameter follows:
2717 % o image: the image.
2719 % o texture: This image is the texture to layer on the background.
2722 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
2724 #define TextureImageTag "Texture/Image"
2739 assert(image != (Image *) NULL);
2740 if (image->debug != MagickFalse)
2741 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2742 assert(image->signature == MagickSignature);
2743 if (texture == (const Image *) NULL)
2744 return(MagickFalse);
2745 (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
2746 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2747 return(MagickFalse);
2749 if ((image->compose != CopyCompositeOp) &&
2750 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2751 (texture->matte != MagickFalse)))
2754 Tile texture onto the image background.
2756 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2757 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2759 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
2764 if (status == MagickFalse)
2766 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2771 thread_status=CompositeImage(image,image->compose,texture,x+
2772 texture->tile_offset.x,y+texture->tile_offset.y);
2773 if (thread_status == MagickFalse)
2775 status=thread_status;
2779 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2784 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2785 #pragma omp critical (MagickCore_TextureImage)
2787 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2789 if (proceed == MagickFalse)
2793 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2794 image->rows,image->rows);
2798 Tile texture onto the image background (optimized).
2801 exception=(&image->exception);
2802 image_view=AcquireCacheView(image);
2803 texture_view=AcquireCacheView(texture);
2804 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2805 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2807 for (y=0; y < (ssize_t) image->rows; y++)
2812 register const IndexPacket
2815 register const PixelPacket
2818 register IndexPacket
2824 register PixelPacket
2830 if (status == MagickFalse)
2832 p=GetCacheViewVirtualPixels(texture_view,texture->tile_offset.x,(y+
2833 texture->tile_offset.y) % texture->rows,texture->columns,1,exception);
2834 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2836 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2841 texture_indexes=GetCacheViewVirtualIndexQueue(texture_view);
2842 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2843 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2845 width=texture->columns;
2846 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2847 width=image->columns-x;
2848 (void) CopyMagickMemory(q,p,width*sizeof(*p));
2849 if ((image->colorspace == CMYKColorspace) &&
2850 (texture->colorspace == CMYKColorspace))
2852 (void) CopyMagickMemory(indexes,texture_indexes,width*
2858 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2859 if (sync == MagickFalse)
2861 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2866 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2867 #pragma omp critical (MagickCore_TextureImage)
2869 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2871 if (proceed == MagickFalse)
2875 texture_view=DestroyCacheView(texture_view);
2876 image_view=DestroyCacheView(image_view);