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*
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 equivalent 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 void CompositeDarkenIntensity(const MagickPixelPacket *p,
407 const MagickPixelPacket *q,const ChannelType channel,
408 MagickPixelPacket *composite)
411 Select the pixel based on the intensity level.
412 If 'Sync' flag select whole pixel based on alpha weighted intensity.
413 Otherwise use intensity only, but restrict copy according to channel.
415 if ( (channel & SyncChannels) != 0 ) {
420 Sa=1.0-QuantumScale*p->opacity;
421 Da=1.0-QuantumScale*q->opacity;
422 *composite = (Sa*MagickPixelIntensity(p) < Da*MagickPixelIntensity(q))
426 int from_p = (MagickPixelIntensity(p) < MagickPixelIntensity(q));
427 if ( (channel & AlphaChannel) != 0 )
428 composite->opacity = from_p ? p->opacity : q->opacity;
429 if ( (channel & RedChannel) != 0 )
430 composite->red = from_p ? p->red : q->red;
431 if ( (channel & GreenChannel) != 0 )
432 composite->green = from_p ? p->green : q->green;
433 if ( (channel & BlueChannel) != 0 )
434 composite->blue = from_p ? p->blue : q->blue;
435 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
436 composite->index = from_p ? p->index : q->index;
440 static inline MagickRealType Difference(const MagickRealType p,
441 const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
443 /* Optimized by Multipling by QuantumRange (taken from gamma). */
444 return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q));
447 static inline void CompositeDifference(const MagickPixelPacket *p,
448 const MagickPixelPacket *q,const ChannelType channel,
449 MagickPixelPacket *composite)
456 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
457 Da=1.0-QuantumScale*q->opacity;
458 if ( (channel & SyncChannels) != 0 ) {
459 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
460 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
461 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
462 /* Values are not normalized as an optimization. */
463 composite->red=gamma*Difference(p->red,Sa,q->red,Da);
464 composite->green=gamma*Difference(p->green,Sa,q->green,Da);
465 composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da);
466 if (q->colorspace == CMYKColorspace)
467 composite->index=gamma*Difference(p->index,Sa,q->index,Da);
469 else { /* handle channels as separate grayscale channels */
470 if ( (channel & AlphaChannel) != 0 )
471 composite->opacity=QuantumRange-fabs(p->opacity - q->opacity);
472 if ( (channel & RedChannel) != 0 )
473 composite->red=fabs(p->red - q->red);
474 if ( (channel & GreenChannel) != 0 )
475 composite->green=fabs(p->green - q->green);
476 if ( (channel & BlueChannel) != 0 )
477 composite->blue=fabs(p->blue - q->blue);
478 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
479 composite->index=fabs(p->index - q->index);
483 static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
484 const MagickRealType Dca,const MagickRealType Da)
487 Divide Source by Destination
491 But with appropriate handling for special case of Dc == 0 specifically
492 so that f(Black,Black)=Black and f(non-Black,Black)=White.
493 It is however also important to correctly do 'over' alpha blending which
494 is why the formula becomes so complex.
496 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
497 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
498 if (fabs(Dca) < MagickEpsilon)
499 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
500 return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
503 static inline void CompositeDivide(const MagickPixelPacket *p,
504 const MagickPixelPacket *q,const ChannelType channel,
505 MagickPixelPacket *composite)
512 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
513 Da=1.0-QuantumScale*q->opacity;
514 if ( (channel & SyncChannels) != 0 ) {
515 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
516 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
517 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
518 composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale*
520 composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale*
522 composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale*
524 if (q->colorspace == CMYKColorspace)
525 composite->index=gamma*Divide(QuantumScale*p->index*Sa,Sa,QuantumScale*
528 else { /* handle channels as separate grayscale channels */
529 if ( (channel & AlphaChannel) != 0 )
530 composite->opacity=QuantumRange*(1.0-Divide(Sa,1.0,Da,1.0));
531 if ( (channel & RedChannel) != 0 )
532 composite->red=QuantumRange*
533 Divide(QuantumScale*p->red,1.0,QuantumScale*q->red,1.0);
534 if ( (channel & GreenChannel) != 0 )
535 composite->green=QuantumRange*
536 Divide(QuantumScale*p->green,1.0,QuantumScale*q->green,1.0);
537 if ( (channel & BlueChannel) != 0 )
538 composite->blue=QuantumRange*
539 Divide(QuantumScale*p->blue,1.0,QuantumScale*q->blue,1.0);
540 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
541 composite->index=QuantumRange*
542 Divide(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0);
546 static MagickRealType Exclusion(const MagickRealType Sca,
547 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
549 return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
552 static inline void CompositeExclusion(const MagickPixelPacket *p,
553 const MagickPixelPacket *q,const ChannelType channel,
554 MagickPixelPacket *composite)
561 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
562 Da=1.0-QuantumScale*q->opacity;
563 if ( (channel & SyncChannels) != 0 ) {
564 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
565 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
566 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
567 composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale*
569 composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale*
571 composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale*
573 if (q->colorspace == CMYKColorspace)
574 composite->index=gamma*Exclusion(QuantumScale*p->index*Sa,Sa,QuantumScale*
577 else { /* handle channels as separate grayscale channels */
578 if ( (channel & AlphaChannel) != 0 )
579 composite->opacity=QuantumRange*(1.0-Exclusion(Sa,1.0,Da,1.0));
580 if ( (channel & RedChannel) != 0 )
581 composite->red=QuantumRange*
582 Exclusion(QuantumScale*p->red,1.0,QuantumScale*q->red,1.0);
583 if ( (channel & GreenChannel) != 0 )
584 composite->green=QuantumRange*
585 Exclusion(QuantumScale*p->green,1.0,QuantumScale*q->green,1.0);
586 if ( (channel & BlueChannel) != 0 )
587 composite->blue=QuantumRange*
588 Exclusion(QuantumScale*p->blue,1.0,QuantumScale*q->blue,1.0);
589 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
590 composite->index=QuantumRange*
591 Exclusion(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0);
595 static MagickRealType HardLight(const MagickRealType Sca,
596 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
599 return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
600 return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
603 static inline void CompositeHardLight(const MagickPixelPacket *p,
604 const MagickPixelPacket *q,MagickPixelPacket *composite)
611 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
612 Da=1.0-QuantumScale*q->opacity;
613 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
614 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
615 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
616 composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
618 composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
620 composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
622 if (q->colorspace == CMYKColorspace)
623 composite->index=gamma*HardLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
627 static void CompositeHSB(const MagickRealType red,const MagickRealType green,
628 const MagickRealType blue,double *hue,double *saturation,double *brightness)
636 Convert RGB to HSB colorspace.
638 assert(hue != (double *) NULL);
639 assert(saturation != (double *) NULL);
640 assert(brightness != (double *) NULL);
641 max=(red > green ? red : green);
644 min=(red < green ? red : green);
649 *brightness=(double) (QuantumScale*max);
652 *saturation=(double) (1.0-min/max);
657 *hue=(double) ((green-blue)/delta);
660 *hue=(double) (2.0+(blue-red)/delta);
663 *hue=(double) (4.0+(red-green)/delta);
669 static inline MagickRealType In(const MagickRealType p,
670 const MagickRealType Sa,const MagickRealType magick_unused(q),
671 const MagickRealType Da)
676 static inline void CompositeIn(const MagickPixelPacket *p,
677 const MagickPixelPacket *q,MagickPixelPacket *composite)
684 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
685 Da=1.0-QuantumScale*q->opacity;
687 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
688 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
689 composite->red=gamma*In(p->red,Sa,q->red,Da);
690 composite->green=gamma*In(p->green,Sa,q->green,Da);
691 composite->blue=gamma*In(p->blue,Sa,q->blue,Da);
692 if (q->colorspace == CMYKColorspace)
693 composite->index=gamma*In(p->index,Sa,q->index,Da);
696 static inline MagickRealType Lighten(const MagickRealType p,
697 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
700 return(MagickOver_(p,alpha,q,beta)); /* src-over */
701 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
704 static inline void CompositeLighten(const MagickPixelPacket *p,
705 const MagickPixelPacket *q,const ChannelType channel,
706 MagickPixelPacket *composite)
709 Lighten is also equvalent to a 'Maximum' method
710 OR a greyscale version of a binary 'And'
711 OR the 'Union' of pixel sets.
716 if ( (channel & SyncChannels) != 0 ) {
717 composite->opacity=QuantumScale*p->opacity*q->opacity; /* Over Blend */
718 gamma=1.0-QuantumScale*composite->opacity;
719 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
720 composite->red=gamma*Lighten(p->red,p->opacity,q->red,q->opacity);
721 composite->green=gamma*Lighten(p->green,p->opacity,q->green,q->opacity);
722 composite->blue=gamma*Lighten(p->blue,p->opacity,q->blue,q->opacity);
723 if (q->colorspace == CMYKColorspace)
724 composite->index=gamma*Lighten(p->index,p->opacity,q->index,q->opacity);
726 else { /* handle channels as separate grayscale channels */
727 if ( (channel & AlphaChannel) != 0 )
728 composite->opacity=MagickMin(p->opacity,q->opacity);
729 if ( (channel & RedChannel) != 0 )
730 composite->red=MagickMax(p->red,q->red);
731 if ( (channel & GreenChannel) != 0 )
732 composite->green=MagickMax(p->green,q->green);
733 if ( (channel & BlueChannel) != 0 )
734 composite->blue=MagickMax(p->blue,q->blue);
735 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
736 composite->index=MagickMax(p->index,q->index);
740 static inline void CompositeLightenIntensity(const MagickPixelPacket *p,
741 const MagickPixelPacket *q,const ChannelType channel,
742 MagickPixelPacket *composite)
745 Select the pixel based on the intensity level.
746 If 'Sync' flag select whole pixel based on alpha weighted intensity.
747 Otherwise use Intenisty only, but restrict copy according to channel.
749 if ( (channel & SyncChannels) != 0 ) {
754 Sa=1.0-QuantumScale*p->opacity;
755 Da=1.0-QuantumScale*q->opacity;
756 *composite = (Sa*MagickPixelIntensity(p) > Da*MagickPixelIntensity(q))
760 int from_p = (MagickPixelIntensity(p) > MagickPixelIntensity(q));
761 if ( (channel & AlphaChannel) != 0 )
762 composite->opacity = from_p ? p->opacity : q->opacity;
763 if ( (channel & RedChannel) != 0 )
764 composite->red = from_p ? p->red : q->red;
765 if ( (channel & GreenChannel) != 0 )
766 composite->green = from_p ? p->green : q->green;
767 if ( (channel & BlueChannel) != 0 )
768 composite->blue = from_p ? p->blue : q->blue;
769 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
770 composite->index = from_p ? p->index : q->index;
775 static inline MagickRealType LinearDodge(const MagickRealType Sca,
776 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
779 LinearDodge: simplifies to a trivial formula
787 static inline void CompositeLinearDodge(const MagickPixelPacket *p,
788 const MagickPixelPacket *q,MagickPixelPacket *composite)
795 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
796 Da=1.0-QuantumScale*q->opacity;
797 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
798 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
799 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
800 composite->red=gamma*(p->red*Sa+q->red*Da);
801 composite->green=gamma*(p->green*Sa+q->green*Da);
802 composite->blue=gamma*(p->blue*Sa+q->blue*Da);
803 if (q->colorspace == CMYKColorspace)
804 composite->index=gamma*(p->index*Sa+q->index*Da);
808 static inline MagickRealType LinearBurn(const MagickRealType Sca,
809 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
812 LinearBurn: as defined by Abode Photoshop, according to
813 http://www.simplefilter.de/en/basics/mixmods.html is:
815 f(Sc,Dc) = Sc + Dc - 1
817 return(Sca+Dca-Sa*Da);
820 static inline void CompositeLinearBurn(const MagickPixelPacket *p,
821 const MagickPixelPacket *q,MagickPixelPacket *composite)
828 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
829 Da=1.0-QuantumScale*q->opacity;
830 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
831 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
832 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
833 composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
835 composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
837 composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
839 if (q->colorspace == CMYKColorspace)
840 composite->index=gamma*LinearBurn(QuantumScale*p->index*Sa,Sa,QuantumScale*
844 static inline MagickRealType LinearLight(const MagickRealType Sca,
845 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
849 Previous formula, was only valid for fully-opaque images.
851 return(Dca+2*Sca-1.0);
854 LinearLight: as defined by Abode Photoshop, according to
855 http://www.simplefilter.de/en/basics/mixmods.html is:
857 f(Sc,Dc) = Dc + 2*Sc - 1
859 return((Sca-Sa)*Da+Sca+Dca);
863 static inline void CompositeLinearLight(const MagickPixelPacket *p,
864 const MagickPixelPacket *q,MagickPixelPacket *composite)
871 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
872 Da=1.0-QuantumScale*q->opacity;
873 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
874 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
875 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
876 composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
878 composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
880 composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
882 if (q->colorspace == CMYKColorspace)
883 composite->index=gamma*LinearLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
887 static inline MagickRealType Mathematics(const MagickRealType Sca,
888 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
889 const GeometryInfo *geometry_info)
892 'Mathematics' a free form user control mathematical composition is defined
895 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
897 Where the arguments A,B,C,D are (currently) passed to composite as
898 a command separated 'geometry' string in "compose:args" image artifact.
900 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
902 Applying the SVG transparency formula (see above), we get...
904 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
906 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
909 return(geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
910 geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
914 static inline void CompositeMathematics(const MagickPixelPacket *p,
915 const MagickPixelPacket *q,const ChannelType channel, const GeometryInfo
916 *args, MagickPixelPacket *composite)
923 Sa=1.0-QuantumScale*p->opacity; /* ??? - AT */
924 Da=1.0-QuantumScale*q->opacity;
925 if ( (channel & SyncChannels) != 0 ) {
926 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
927 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
928 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
929 composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
931 composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,QuantumScale*
932 q->green*Da,Da,args);
933 composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
935 if (q->colorspace == CMYKColorspace)
936 composite->index=gamma*Mathematics(QuantumScale*p->index*Sa,Sa,QuantumScale*
937 q->index*Da,Da,args);
939 else { /* handle channels as separate grayscale channels */
940 if ( (channel & AlphaChannel) != 0 )
941 composite->opacity=QuantumRange*(1.0-Mathematics(Sa,1.0,Da,1.0,args));
942 if ( (channel & RedChannel) != 0 )
943 composite->red=QuantumRange*
944 Mathematics(QuantumScale*p->red,1.0,QuantumScale*q->red,1.0,args);
945 if ( (channel & GreenChannel) != 0 )
946 composite->green=QuantumRange*
947 Mathematics(QuantumScale*p->green,1.0,QuantumScale*q->green,1.0,args);
948 if ( (channel & BlueChannel) != 0 )
949 composite->blue=QuantumRange*
950 Mathematics(QuantumScale*p->blue,1.0,QuantumScale*q->blue,1.0,args);
951 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
952 composite->index=QuantumRange*
953 Mathematics(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0,args);
958 static inline void CompositePlus(const MagickPixelPacket *p,
959 const MagickPixelPacket *q,const ChannelType channel,
960 MagickPixelPacket *composite)
962 if ( (channel & SyncChannels) != 0 ) {
964 NOTE: "Plus" does not use 'over' alpha-blending but uses a
965 special 'plus' form of alph-blending. It is the ONLY mathematical
966 operator to do this. this is what makes it different to the
967 otherwise equivalent "LinearDodge" composition method.
969 Note however that color channels are still effected by the alpha channel
970 as a result of the blending, making it just as useless for independant
971 channel maths, just like all other mathematical composition methods.
973 As such the removal of the 'sync' flag, is still a usful convention.
975 The MagickPixelCompositePlus() function is defined in
976 "composite-private.h" so it can also be used for Image Blending.
978 MagickPixelCompositePlus(p,p->opacity,q,q->opacity,composite);
980 else { /* handle channels as separate grayscale channels */
981 if ( (channel & AlphaChannel) != 0 )
982 composite->opacity=p->opacity+q->opacity-QuantumRange;
983 if ( (channel & RedChannel) != 0 )
984 composite->red=p->red+q->red;
985 if ( (channel & GreenChannel) != 0 )
986 composite->green=p->green+q->green;
987 if ( (channel & BlueChannel) != 0 )
988 composite->blue=p->blue+q->blue;
989 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
990 composite->index=p->index+q->index;
994 static inline MagickRealType Minus(const MagickRealType Sca,
995 const MagickRealType Sa,const MagickRealType Dca,
996 const MagickRealType magick_unused(Da))
999 Minus Source from Destination
1004 return(Sca + Dca - 2*Dca*Sa);
1007 static inline void CompositeMinus(const MagickPixelPacket *p,
1008 const MagickPixelPacket *q,const ChannelType channel,
1009 MagickPixelPacket *composite)
1016 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1017 Da=1.0-QuantumScale*q->opacity;
1018 if ( (channel & SyncChannels) != 0 ) {
1019 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1020 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1021 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1022 composite->red=gamma*Minus(p->red*Sa,Sa,q->red*Da,Da);
1023 composite->green=gamma*Minus(p->green*Sa,Sa,q->green*Da,Da);
1024 composite->blue=gamma*Minus(p->blue*Sa,Sa,q->blue*Da,Da);
1025 if (q->colorspace == CMYKColorspace)
1026 composite->index=gamma*Minus(p->index*Sa,Sa,q->index*Da,Da);
1028 else { /* handle channels as separate grayscale channels */
1029 if ( (channel & AlphaChannel) != 0 )
1030 composite->opacity=QuantumRange*(1.0-(Sa-Da));
1031 if ( (channel & RedChannel) != 0 )
1032 composite->red=p->red-q->red;
1033 if ( (channel & GreenChannel) != 0 )
1034 composite->green=p->green-q->green;
1035 if ( (channel & BlueChannel) != 0 )
1036 composite->blue=p->blue-q->blue;
1037 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1038 composite->index=p->index-q->index;
1042 static inline MagickRealType ModulusAdd(const MagickRealType p,
1043 const MagickRealType Sa, const MagickRealType q, const MagickRealType Da)
1049 if (pixel > QuantumRange)
1050 pixel-=(QuantumRange+1.0);
1051 return(pixel*Sa*Da + p*Sa*(1-Da) + q*Da*(1-Sa));
1054 static inline void CompositeModulusAdd(const MagickPixelPacket *p,
1055 const MagickPixelPacket *q, const ChannelType channel,
1056 MagickPixelPacket *composite)
1058 if ( (channel & SyncChannels) != 0 ) {
1064 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1065 Da=1.0-QuantumScale*q->opacity;
1066 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1067 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1068 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1069 composite->red=ModulusAdd(p->red,Sa,q->red,Da);
1070 composite->green=ModulusAdd(p->green,Sa,q->green,Da);
1071 composite->blue=ModulusAdd(p->blue,Sa,q->blue,Da);
1072 if (q->colorspace == CMYKColorspace)
1073 composite->index=ModulusAdd(p->index,Sa,q->index,Da);
1075 else { /* handle channels as separate grayscale channels */
1076 if ( (channel & AlphaChannel) != 0 )
1077 composite->opacity=QuantumRange-ModulusAdd(QuantumRange-p->opacity,
1078 1.0,QuantumRange-q->opacity,1.0);
1079 if ( (channel & RedChannel) != 0 )
1080 composite->red=ModulusAdd(p->red,1.0,q->red,1.0);
1081 if ( (channel & GreenChannel) != 0 )
1082 composite->green=ModulusAdd(p->green,1.0,q->green,1.0);
1083 if ( (channel & BlueChannel) != 0 )
1084 composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0);
1085 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1086 composite->index=ModulusAdd(p->index,1.0,q->index,1.0);
1090 static inline MagickRealType ModulusSubtract(const MagickRealType p,
1091 const MagickRealType Sa, const MagickRealType q, const MagickRealType Da)
1098 pixel+=(QuantumRange+1.0);
1099 return(pixel*Sa*Da + p*Sa*(1-Da) + q*Da*(1-Sa));
1102 static inline void CompositeModulusSubtract(const MagickPixelPacket *p,
1103 const MagickPixelPacket *q, const ChannelType channel,
1104 MagickPixelPacket *composite)
1106 if ( (channel & SyncChannels) != 0 ) {
1112 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1113 Da=1.0-QuantumScale*q->opacity;
1114 gamma = RoundToUnity(Sa+Da-Sa*Da);
1115 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1116 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1117 composite->red=ModulusSubtract(p->red,Sa,q->red,Da);
1118 composite->green=ModulusSubtract(p->green,Sa,q->green,Da);
1119 composite->blue=ModulusSubtract(p->blue,Sa,q->blue,Da);
1120 if (q->colorspace == CMYKColorspace)
1121 composite->index=ModulusSubtract(p->index,Sa,q->index,Da);
1123 else { /* handle channels as separate grayscale channels */
1124 if ( (channel & AlphaChannel) != 0 )
1125 composite->opacity=QuantumRange-ModulusSubtract(QuantumRange-p->opacity,
1126 1.0,QuantumRange-q->opacity,1.0);
1127 if ( (channel & RedChannel) != 0 )
1128 composite->red=ModulusSubtract(p->red,1.0,q->red,1.0);
1129 if ( (channel & GreenChannel) != 0 )
1130 composite->green=ModulusSubtract(p->green,1.0,q->green,1.0);
1131 if ( (channel & BlueChannel) != 0 )
1132 composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0);
1133 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1134 composite->index=ModulusSubtract(p->index,1.0,q->index,1.0);
1138 static inline MagickRealType Multiply(const MagickRealType Sca,
1139 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1141 return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1144 static inline void CompositeMultiply(const MagickPixelPacket *p,
1145 const MagickPixelPacket *q,const ChannelType channel,
1146 MagickPixelPacket *composite)
1153 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1154 Da=1.0-QuantumScale*q->opacity;
1155 if ( (channel & SyncChannels) != 0 ) {
1156 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1157 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1158 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1159 composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
1161 composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
1163 composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1165 if (q->colorspace == CMYKColorspace)
1166 composite->index=gamma*Multiply(QuantumScale*p->index*Sa,Sa,QuantumScale*
1169 else { /* handle channels as separate grayscale channels */
1170 if ( (channel & AlphaChannel) != 0 )
1171 composite->opacity=QuantumRange*(1.0-Sa*Da);
1172 if ( (channel & RedChannel) != 0 )
1173 composite->red=QuantumScale*p->red*q->red;
1174 if ( (channel & GreenChannel) != 0 )
1175 composite->green=QuantumScale*p->green*q->green;
1176 if ( (channel & BlueChannel) != 0 )
1177 composite->blue=QuantumScale*p->blue*q->blue;
1178 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1179 composite->index=QuantumScale*p->index*q->index;
1183 static inline MagickRealType Out(const MagickRealType p,
1184 const MagickRealType Sa,const MagickRealType magick_unused(q),
1185 const MagickRealType Da)
1187 return(Sa*p*(1.0-Da));
1190 static inline void CompositeOut(const MagickPixelPacket *p,
1191 const MagickPixelPacket *q,MagickPixelPacket *composite)
1198 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1199 Da=1.0-QuantumScale*q->opacity;
1201 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1202 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1203 composite->red=gamma*Out(p->red,Sa,q->red,Da);
1204 composite->green=gamma*Out(p->green,Sa,q->green,Da);
1205 composite->blue=gamma*Out(p->blue,Sa,q->blue,Da);
1206 if (q->colorspace == CMYKColorspace)
1207 composite->index=gamma*Out(p->index,Sa,q->index,Da);
1210 static MagickRealType PegtopLight(const MagickRealType Sca,
1211 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1214 PegTop: A Soft-Light alternative: A continuous version of the Softlight
1215 function, producing very similar results.
1217 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
1219 See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
1221 if (fabs(Da) < MagickEpsilon)
1223 return(Dca*Dca*(Sa-2*Sca)/Da+Sca*(2*Dca+1-Da)+Dca*(1-Sa));
1226 static inline void CompositePegtopLight(const MagickPixelPacket *p,
1227 const MagickPixelPacket *q,MagickPixelPacket *composite)
1234 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1235 Da=1.0-QuantumScale*q->opacity;
1236 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1237 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1238 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1239 composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1241 composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1243 composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1245 if (q->colorspace == CMYKColorspace)
1246 composite->index=gamma*PegtopLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1250 static MagickRealType PinLight(const MagickRealType Sca,
1251 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1254 PinLight: A Photoshop 7 composition method
1255 http://www.simplefilter.de/en/basics/mixmods.html
1257 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
1259 if (Dca*Sa < Da*(2*Sca-Sa))
1260 return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
1261 if ((Dca*Sa) > (2*Sca*Da))
1262 return(Sca*Da+Sca+Dca*(1.0-Sa));
1263 return(Sca*(1.0-Da)+Dca);
1266 static inline void CompositePinLight(const MagickPixelPacket *p,
1267 const MagickPixelPacket *q,MagickPixelPacket *composite)
1274 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1275 Da=1.0-QuantumScale*q->opacity;
1276 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1277 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1278 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1279 composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1281 composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1283 composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1285 if (q->colorspace == CMYKColorspace)
1286 composite->index=gamma*PinLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1290 static inline MagickRealType Screen(const MagickRealType Sca,
1291 const MagickRealType Dca)
1293 /* Screen: A negated multiply
1294 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
1296 return(Sca+Dca-Sca*Dca);
1299 static inline void CompositeScreen(const MagickPixelPacket *p,
1300 const MagickPixelPacket *q,const ChannelType channel,
1301 MagickPixelPacket *composite)
1308 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1309 Da=1.0-QuantumScale*q->opacity;
1310 if ( (channel & SyncChannels) != 0 ) {
1311 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1312 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1313 Sa*=QuantumScale; Da*=QuantumScale; /* optimization */
1314 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1315 composite->red=gamma*Screen(p->red*Sa,q->red*Da);
1316 composite->green=gamma*Screen(p->green*Sa,q->green*Da);
1317 composite->blue=gamma*Screen(p->blue*Sa,q->blue*Da);
1318 if (q->colorspace == CMYKColorspace)
1319 composite->index=gamma*Screen(p->index*Sa,q->index*Da);
1321 else { /* handle channels as separate grayscale channels */
1322 if ( (channel & AlphaChannel) != 0 )
1323 composite->opacity=QuantumRange*(1.0-Screen(Sa,Da));
1324 if ( (channel & RedChannel) != 0 )
1325 composite->red=QuantumRange*Screen(QuantumScale*p->red,
1326 QuantumScale*q->red);
1327 if ( (channel & GreenChannel) != 0 )
1328 composite->green=QuantumRange*Screen(QuantumScale*p->green,
1329 QuantumScale*q->green);
1330 if ( (channel & BlueChannel) != 0 )
1331 composite->blue=QuantumRange*Screen(QuantumScale*p->blue,
1332 QuantumScale*q->blue);
1333 if ( (channel & IndexChannel) != 0 && q->colorspace == CMYKColorspace)
1334 composite->index=QuantumRange*Screen(QuantumScale*p->index,
1335 QuantumScale*q->index);
1339 static MagickRealType SoftLight(const MagickRealType Sca,
1340 const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
1344 Oct 2004 SVG specification -- was found to be incorrect
1345 See http://lists.w3.org/Archives/Public/www-svg/2009Feb/0014.html.
1348 return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1350 return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa)*(3.0-8.0*Dca/Da))+
1351 Sca*(1.0-Da)+Dca*(1.0-Sa));
1352 return((Dca*Sa+(pow(Dca/Da,0.5)*Da-Dca)*(2.0*Sca-Sa))+Sca*(1.0-Da)+
1360 New specification: March 2009 SVG specification.
1364 return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1365 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1367 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
1368 alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1371 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1376 static inline void CompositeSoftLight(const MagickPixelPacket *p,
1377 const MagickPixelPacket *q,MagickPixelPacket *composite)
1384 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1385 Da=1.0-QuantumScale*q->opacity;
1386 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1387 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1388 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1389 composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1391 composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1393 composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1395 if (q->colorspace == CMYKColorspace)
1396 composite->index=gamma*SoftLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1402 Multiply difference by amount, if differance larger than threshold???
1403 What use this is is completely unknown
1404 The Opacity calculation appears to be inverted -- Anthony Thyssen
1406 static inline MagickRealType Threshold(const MagickRealType p,
1407 const MagickRealType q,const MagickRealType threshold,
1408 const MagickRealType amount)
1414 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
1416 return(q+delta*amount);
1419 static inline void CompositeThreshold(const MagickPixelPacket *p,
1420 const MagickPixelPacket *q,const MagickRealType threshold,
1421 const MagickRealType amount,MagickPixelPacket *composite)
1423 composite->red=Threshold(p->red,q->red,threshold,amount);
1424 composite->green=Threshold(p->green,q->green,threshold,amount);
1425 composite->blue=Threshold(p->blue,q->blue,threshold,amount);
1426 composite->opacity=QuantumRange-Threshold(p->opacity,q->opacity,
1428 if (q->colorspace == CMYKColorspace)
1429 composite->index=Threshold(p->index,q->index,threshold,amount);
1433 static MagickRealType VividLight(const MagickRealType Sca,
1434 const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
1437 VividLight: A Photoshop 7 composition method. See
1438 http://www.simplefilter.de/en/basics/mixmods.html.
1440 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
1442 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
1443 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1445 return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1446 return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1449 static inline void CompositeVividLight(const MagickPixelPacket *p,
1450 const MagickPixelPacket *q,MagickPixelPacket *composite)
1457 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1458 Da=1.0-QuantumScale*q->opacity;
1459 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1460 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1461 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1462 composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1464 composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1466 composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1468 if (q->colorspace == CMYKColorspace)
1469 composite->index=gamma*VividLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1473 static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
1474 const MagickRealType Dca,const MagickRealType Da)
1476 return(Sca*(1-Da)+Dca*(1-Sa));
1479 static inline void CompositeXor(const MagickPixelPacket *p,
1480 const MagickPixelPacket *q,MagickPixelPacket *composite)
1487 Sa=1.0-QuantumScale*p->opacity; /* simplify and speed up equations */
1488 Da=1.0-QuantumScale*q->opacity;
1489 gamma=Sa+Da-2*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
1490 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1491 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1492 composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
1493 composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
1494 composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
1495 if (q->colorspace == CMYKColorspace)
1496 composite->index=gamma*Xor(p->index*Sa,Sa,q->index*Da,Da);
1499 static void HSBComposite(const double hue,const double saturation,
1500 const double brightness,MagickRealType *red,MagickRealType *green,
1501 MagickRealType *blue)
1511 Convert HSB to RGB colorspace.
1513 assert(red != (MagickRealType *) NULL);
1514 assert(green != (MagickRealType *) NULL);
1515 assert(blue != (MagickRealType *) NULL);
1516 if (saturation == 0.0)
1518 *red=(MagickRealType) QuantumRange*brightness;
1523 h=6.0*(hue-floor(hue));
1524 f=h-floor((double) h);
1525 p=brightness*(1.0-saturation);
1526 q=brightness*(1.0-saturation*f);
1527 t=brightness*(1.0-saturation*(1.0-f));
1533 *red=(MagickRealType) QuantumRange*brightness;
1534 *green=(MagickRealType) QuantumRange*t;
1535 *blue=(MagickRealType) QuantumRange*p;
1540 *red=(MagickRealType) QuantumRange*q;
1541 *green=(MagickRealType) QuantumRange*brightness;
1542 *blue=(MagickRealType) QuantumRange*p;
1547 *red=(MagickRealType) QuantumRange*p;
1548 *green=(MagickRealType) QuantumRange*brightness;
1549 *blue=(MagickRealType) QuantumRange*t;
1554 *red=(MagickRealType) QuantumRange*p;
1555 *green=(MagickRealType) QuantumRange*q;
1556 *blue=(MagickRealType) QuantumRange*brightness;
1561 *red=(MagickRealType) QuantumRange*t;
1562 *green=(MagickRealType) QuantumRange*p;
1563 *blue=(MagickRealType) QuantumRange*brightness;
1568 *red=(MagickRealType) QuantumRange*brightness;
1569 *green=(MagickRealType) QuantumRange*p;
1570 *blue=(MagickRealType) QuantumRange*q;
1576 MagickExport MagickBooleanType CompositeImage(Image *image,
1577 const CompositeOperator compose,const Image *composite_image,
1578 const ssize_t x_offset,const ssize_t y_offset)
1583 status=CompositeImageChannel(image,DefaultChannels,compose,composite_image,
1588 MagickExport MagickBooleanType CompositeImageChannel(Image *image,
1589 const ChannelType channel,const CompositeOperator compose,
1590 const Image *composite_image,const ssize_t x_offset,const ssize_t y_offset)
1592 #define CompositeImageTag "Composite/Image"
1614 modify_outside_overlay,
1625 destination_dissolve,
1639 Prepare composite image.
1641 assert(image != (Image *) NULL);
1642 assert(image->signature == MagickSignature);
1643 if (image->debug != MagickFalse)
1644 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1645 assert(composite_image != (Image *) NULL);
1646 assert(composite_image->signature == MagickSignature);
1647 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1648 return(MagickFalse);
1649 GetMagickPixelPacket(image,&zero);
1650 destination_image=(Image *) NULL;
1652 destination_dissolve=1.0;
1653 modify_outside_overlay=MagickFalse;
1654 percent_brightness=100.0;
1655 percent_saturation=100.0;
1656 source_dissolve=1.0;
1660 case ClearCompositeOp:
1661 case SrcCompositeOp:
1663 case SrcInCompositeOp:
1664 case OutCompositeOp:
1665 case SrcOutCompositeOp:
1666 case DstInCompositeOp:
1667 case DstAtopCompositeOp:
1670 Modify destination outside the overlaid region.
1672 modify_outside_overlay=MagickTrue;
1675 case OverCompositeOp:
1677 if (image->matte != MagickFalse)
1679 if (composite_image->matte != MagickFalse)
1682 case CopyCompositeOp:
1684 if ((x_offset < 0) || (y_offset < 0))
1686 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
1688 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
1691 exception=(&image->exception);
1692 image_view=AcquireCacheView(image);
1693 composite_view=AcquireCacheView(composite_image);
1694 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1695 #pragma omp parallel for schedule(dynamic,4) shared(status)
1697 for (y=0; y < (ssize_t) composite_image->rows; y++)
1702 register const IndexPacket
1705 register const PixelPacket
1708 register IndexPacket
1711 register PixelPacket
1714 if (status == MagickFalse)
1716 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1718 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1719 composite_image->columns,1,exception);
1720 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1725 composite_indexes=GetCacheViewVirtualIndexQueue(composite_view);
1726 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1727 (void) CopyMagickMemory(q,p,composite_image->columns*sizeof(*p));
1728 if ((indexes != (IndexPacket *) NULL) &&
1729 (composite_indexes != (const IndexPacket *) NULL))
1730 (void) CopyMagickMemory(indexes,composite_indexes,
1731 composite_image->columns*sizeof(*indexes));
1732 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1733 if (sync == MagickFalse)
1735 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1740 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1741 #pragma omp critical (MagickCore_CompositeImage)
1743 proceed=SetImageProgress(image,CompositeImageTag,
1744 (MagickOffsetType) y,image->rows);
1745 if (proceed == MagickFalse)
1749 composite_view=DestroyCacheView(composite_view);
1750 image_view=DestroyCacheView(image_view);
1753 case CopyOpacityCompositeOp:
1754 case ChangeMaskCompositeOp:
1757 Modify destination outside the overlaid region and require an alpha
1758 channel to exist, to add transparency.
1760 if (image->matte == MagickFalse)
1761 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1762 modify_outside_overlay=MagickTrue;
1765 case BlurCompositeOp:
1787 Blur Image dictated by an overlay gradient map: X = red_channel;
1788 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
1790 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1792 if (destination_image == (Image *) NULL)
1793 return(MagickFalse);
1795 Determine the horizontal and vertical maximim blur.
1797 SetGeometryInfo(&geometry_info);
1799 value=GetImageArtifact(composite_image,"compose:args");
1800 if (value != (char *) NULL)
1801 flags=ParseGeometry(value,&geometry_info);
1802 if ((flags & WidthValue) == 0 )
1804 destination_image=DestroyImage(destination_image);
1805 return(MagickFalse);
1807 width=geometry_info.rho;
1808 height=geometry_info.sigma;
1809 blur.x1=geometry_info.rho;
1812 blur.y2=geometry_info.sigma;
1815 if ((flags & HeightValue) == 0)
1817 if ((flags & XValue) != 0 )
1822 angle=DegreesToRadians(geometry_info.xi);
1823 blur.x1=width*cos(angle);
1824 blur.x2=width*sin(angle);
1825 blur.y1=(-height*sin(angle));
1826 blur.y2=height*cos(angle);
1828 if ((flags & YValue) != 0 )
1830 angle_start=DegreesToRadians(geometry_info.xi);
1831 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1834 Blur Image by resampling.
1837 exception=(&image->exception);
1838 resample_filter=AcquireResampleFilter(image,&image->exception);
1839 SetResampleFilter(resample_filter,CubicFilter,2.0);
1840 destination_view=AcquireCacheView(destination_image);
1841 composite_view=AcquireCacheView(composite_image);
1842 for (y=0; y < (ssize_t) composite_image->rows; y++)
1847 register const PixelPacket
1850 register PixelPacket
1853 register IndexPacket
1854 *restrict destination_indexes;
1859 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1861 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1863 r=QueueCacheViewAuthenticPixels(destination_view,0,y,
1864 destination_image->columns,1,&image->exception);
1865 if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
1867 destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
1868 for (x=0; x < (ssize_t) composite_image->columns; x++)
1870 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1875 if (fabs(angle_range) > MagickEpsilon)
1880 angle=angle_start+angle_range*QuantumScale*
1881 GetBluePixelComponent(p);
1882 blur.x1=width*cos(angle);
1883 blur.x2=width*sin(angle);
1884 blur.y1=(-height*sin(angle));
1885 blur.y2=height*cos(angle);
1887 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
1888 GetRedPixelComponent(p),blur.y1*QuantumScale*
1889 GetGreenPixelComponent(p),blur.x2*QuantumScale*
1890 GetRedPixelComponent(p),blur.y2*QuantumScale*
1891 GetGreenPixelComponent(p));
1892 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1893 (double) y_offset+y,&pixel);
1894 SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
1898 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1899 if (sync == MagickFalse)
1902 resample_filter=DestroyResampleFilter(resample_filter);
1903 composite_view=DestroyCacheView(composite_view);
1904 destination_view=DestroyCacheView(destination_view);
1905 composite_image=destination_image;
1908 case DisplaceCompositeOp:
1909 case DistortCompositeOp:
1927 register IndexPacket
1928 *restrict destination_indexes;
1930 register PixelPacket
1934 Displace/Distort based on overlay gradient map:
1935 X = red_channel; Y = green_channel;
1936 compose:args = x_scale[,y_scale[,center.x,center.y]]
1938 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1940 if (destination_image == (Image *) NULL)
1941 return(MagickFalse);
1942 SetGeometryInfo(&geometry_info);
1944 value=GetImageArtifact(composite_image,"compose:args");
1945 if (value != (char *) NULL)
1946 flags=ParseGeometry(value,&geometry_info);
1947 if ((flags & (WidthValue|HeightValue)) == 0 )
1949 if ((flags & AspectValue) == 0)
1951 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1953 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1957 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1958 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1963 horizontal_scale=geometry_info.rho;
1964 vertical_scale=geometry_info.sigma;
1965 if ((flags & PercentValue) != 0)
1967 if ((flags & AspectValue) == 0)
1969 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1970 vertical_scale*=(composite_image->rows-1.0)/200.0;
1974 horizontal_scale*=(image->columns-1.0)/200.0;
1975 vertical_scale*=(image->rows-1.0)/200.0;
1978 if ((flags & HeightValue) == 0)
1979 vertical_scale=horizontal_scale;
1982 Determine fixed center point for absolute distortion map
1984 Displace offset relative to a fixed absolute point
1985 Select that point according to +X+Y user inputs.
1986 default = center of overlay image
1987 arg flag '!' = locations/percentage relative to background image
1989 center.x=(MagickRealType) x_offset;
1990 center.y=(MagickRealType) y_offset;
1991 if (compose == DistortCompositeOp)
1993 if ((flags & XValue) == 0)
1994 if ((flags & AspectValue) == 0)
1995 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
1998 center.x=((MagickRealType) image->columns-1)/2.0;
2000 if ((flags & AspectValue) == 0)
2001 center.x=(MagickRealType) x_offset+geometry_info.xi;
2003 center.x=geometry_info.xi;
2004 if ((flags & YValue) == 0)
2005 if ((flags & AspectValue) == 0)
2006 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
2008 center.y=((MagickRealType) image->rows-1)/2.0;
2010 if ((flags & AspectValue) == 0)
2011 center.y=(MagickRealType) y_offset+geometry_info.psi;
2013 center.y=geometry_info.psi;
2016 Shift the pixel offset point as defined by the provided,
2017 displacement/distortion map. -- Like a lens...
2020 exception=(&image->exception);
2021 image_view=AcquireCacheView(image);
2022 destination_view=AcquireCacheView(destination_image);
2023 composite_view=AcquireCacheView(composite_image);
2024 for (y=0; y < (ssize_t) composite_image->rows; y++)
2029 register const PixelPacket
2035 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
2037 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
2039 r=QueueCacheViewAuthenticPixels(destination_view,0,y,
2040 destination_image->columns,1,&image->exception);
2041 if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
2043 destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
2044 for (x=0; x < (ssize_t) composite_image->columns; x++)
2046 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
2052 Displace the offset.
2054 offset.x=(horizontal_scale*(GetRedPixelComponent(p)-
2055 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2056 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
2058 offset.y=(vertical_scale*(GetGreenPixelComponent(p)-
2059 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2060 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
2062 (void) InterpolateMagickPixelPacket(image,image_view,
2063 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2066 Mask with the 'invalid pixel mask' in alpha channel.
2068 pixel.opacity=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
2069 pixel.opacity)*(1.0-QuantumScale*GetOpacityPixelComponent(p)));
2070 SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
2074 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
2075 if (sync == MagickFalse)
2078 destination_view=DestroyCacheView(destination_view);
2079 composite_view=DestroyCacheView(composite_view);
2080 image_view=DestroyCacheView(image_view);
2081 composite_image=destination_image;
2084 case DissolveCompositeOp:
2087 Geometry arguments to dissolve factors.
2089 value=GetImageArtifact(composite_image,"compose:args");
2090 if (value != (char *) NULL)
2092 flags=ParseGeometry(value,&geometry_info);
2093 source_dissolve=geometry_info.rho/100.0;
2094 destination_dissolve=1.0;
2095 if ((source_dissolve-MagickEpsilon) < 0.0)
2096 source_dissolve=0.0;
2097 if ((source_dissolve+MagickEpsilon) > 1.0)
2099 destination_dissolve=2.0-source_dissolve;
2100 source_dissolve=1.0;
2102 if ((flags & SigmaValue) != 0)
2103 destination_dissolve=geometry_info.sigma/100.0;
2104 if ((destination_dissolve-MagickEpsilon) < 0.0)
2105 destination_dissolve=0.0;
2106 modify_outside_overlay=MagickTrue;
2107 if ((destination_dissolve+MagickEpsilon) > 1.0 )
2109 destination_dissolve=1.0;
2110 modify_outside_overlay=MagickFalse;
2115 case BlendCompositeOp:
2117 value=GetImageArtifact(composite_image,"compose:args");
2118 if (value != (char *) NULL)
2120 flags=ParseGeometry(value,&geometry_info);
2121 source_dissolve=geometry_info.rho/100.0;
2122 destination_dissolve=1.0-source_dissolve;
2123 if ((flags & SigmaValue) != 0)
2124 destination_dissolve=geometry_info.sigma/100.0;
2125 modify_outside_overlay=MagickTrue;
2126 if ((destination_dissolve+MagickEpsilon) > 1.0)
2127 modify_outside_overlay=MagickFalse;
2131 case MathematicsCompositeOp:
2134 Just collect the values from "compose:args", setting.
2135 Unused values are set to zero automagically.
2137 Arguments are normally a comma separated list, so this probably should
2138 be changed to some 'general comma list' parser, (with a minimum
2141 SetGeometryInfo(&geometry_info);
2142 value=GetImageArtifact(composite_image,"compose:args");
2143 if (value != (char *) NULL)
2144 (void) ParseGeometry(value,&geometry_info);
2147 case ModulateCompositeOp:
2150 Determine the brightness and saturation scale.
2152 value=GetImageArtifact(composite_image,"compose:args");
2153 if (value != (char *) NULL)
2155 flags=ParseGeometry(value,&geometry_info);
2156 percent_brightness=geometry_info.rho;
2157 if ((flags & SigmaValue) != 0)
2158 percent_saturation=geometry_info.sigma;
2162 case ThresholdCompositeOp:
2165 Determine the amount and threshold.
2166 This Composition method is depreciated
2168 value=GetImageArtifact(composite_image,"compose:args");
2169 if (value != (char *) NULL)
2171 flags=ParseGeometry(value,&geometry_info);
2172 amount=geometry_info.rho;
2173 threshold=geometry_info.sigma;
2174 if ((flags & SigmaValue) == 0)
2177 threshold*=QuantumRange;
2183 value=GetImageArtifact(composite_image,"compose:outside-overlay");
2184 if (value != (const char *) NULL)
2185 modify_outside_overlay=IsMagickTrue(value);
2191 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2192 GetMagickPixelPacket(composite_image,&zero);
2193 exception=(&image->exception);
2194 image_view=AcquireCacheView(image);
2195 composite_view=AcquireCacheView(composite_image);
2196 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2197 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2199 for (y=0; y < (ssize_t) image->rows; y++)
2214 register const IndexPacket
2215 *restrict composite_indexes;
2217 register const PixelPacket
2220 register IndexPacket
2226 register PixelPacket
2229 if (status == MagickFalse)
2231 if (modify_outside_overlay == MagickFalse)
2235 if ((y-y_offset) >= (ssize_t) composite_image->rows)
2239 If pixels is NULL, y is outside overlay region.
2241 pixels=(PixelPacket *) NULL;
2242 p=(PixelPacket *) NULL;
2243 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
2245 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
2246 composite_image->columns,1,exception);
2247 if (p == (const PixelPacket *) NULL)
2256 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2257 if (q == (PixelPacket *) NULL)
2262 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2263 composite_indexes=GetCacheViewVirtualIndexQueue(composite_view);
2269 for (x=0; x < (ssize_t) image->columns; x++)
2271 if (modify_outside_overlay == MagickFalse)
2278 if ((x-x_offset) >= (ssize_t) composite_image->columns)
2281 destination.red=(MagickRealType) GetRedPixelComponent(q);
2282 destination.green=(MagickRealType) GetGreenPixelComponent(q);
2283 destination.blue=(MagickRealType) GetBluePixelComponent(q);
2284 if (image->matte != MagickFalse)
2285 destination.opacity=(MagickRealType) GetOpacityPixelComponent(q);
2286 if (image->colorspace == CMYKColorspace)
2287 destination.index=(MagickRealType) GetIndexPixelComponent(indexes+x);
2288 if (image->colorspace == CMYKColorspace)
2290 destination.red=(MagickRealType) QuantumRange-destination.red;
2291 destination.green=(MagickRealType) QuantumRange-destination.green;
2292 destination.blue=(MagickRealType) QuantumRange-destination.blue;
2293 destination.index=(MagickRealType) QuantumRange-destination.index;
2296 Handle destination modifications outside overlaid region.
2298 composite=destination;
2299 if ((pixels == (PixelPacket *) NULL) || (x < x_offset) ||
2300 ((x-x_offset) >= (ssize_t) composite_image->columns))
2304 case DissolveCompositeOp:
2305 case BlendCompositeOp:
2307 composite.opacity=(MagickRealType) (QuantumRange-
2308 destination_dissolve*(QuantumRange-composite.opacity));
2311 case ClearCompositeOp:
2312 case SrcCompositeOp:
2314 CompositeClear(&destination,&composite);
2318 case SrcInCompositeOp:
2319 case OutCompositeOp:
2320 case SrcOutCompositeOp:
2321 case DstInCompositeOp:
2322 case DstAtopCompositeOp:
2323 case CopyOpacityCompositeOp:
2324 case ChangeMaskCompositeOp:
2326 composite.opacity=(MagickRealType) TransparentOpacity;
2331 (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,
2332 y-y_offset,&composite,exception);
2336 if (image->colorspace == CMYKColorspace)
2338 composite.red=(MagickRealType) QuantumRange-composite.red;
2339 composite.green=(MagickRealType) QuantumRange-composite.green;
2340 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2341 composite.index=(MagickRealType) QuantumRange-composite.index;
2343 SetRedPixelComponent(q,ClampToQuantum(composite.red));
2344 SetGreenPixelComponent(q,ClampToQuantum(composite.green));
2345 SetBluePixelComponent(q,ClampToQuantum(composite.blue));
2346 if (image->matte != MagickFalse)
2347 SetOpacityPixelComponent(q,ClampToQuantum(composite.opacity));
2348 if (image->colorspace == CMYKColorspace)
2349 SetIndexPixelComponent(indexes+x,ClampToQuantum(composite.index));
2354 Handle normal overlay of source onto destination.
2356 source.red=(MagickRealType) GetRedPixelComponent(p);
2357 source.green=(MagickRealType) GetGreenPixelComponent(p);
2358 source.blue=(MagickRealType) GetBluePixelComponent(p);
2359 if (composite_image->matte != MagickFalse)
2360 source.opacity=(MagickRealType) GetOpacityPixelComponent(p);
2361 if (composite_image->colorspace == CMYKColorspace)
2362 source.index=(MagickRealType) GetIndexPixelComponent(composite_indexes+
2364 if (composite_image->colorspace == CMYKColorspace)
2366 source.red=(MagickRealType) QuantumRange-source.red;
2367 source.green=(MagickRealType) QuantumRange-source.green;
2368 source.blue=(MagickRealType) QuantumRange-source.blue;
2369 source.index=(MagickRealType) QuantumRange-source.index;
2373 /* Duff-Porter Compositions */
2374 case ClearCompositeOp:
2376 CompositeClear(&destination,&composite);
2379 case SrcCompositeOp:
2380 case CopyCompositeOp:
2381 case ReplaceCompositeOp:
2387 case DstCompositeOp:
2389 case OverCompositeOp:
2390 case SrcOverCompositeOp:
2392 MagickPixelCompositeOver(&source,source.opacity,&destination,
2393 destination.opacity,&composite);
2396 case DstOverCompositeOp:
2398 MagickPixelCompositeOver(&destination,destination.opacity,&source,
2399 source.opacity,&composite);
2402 case SrcInCompositeOp:
2405 CompositeIn(&source,&destination,&composite);
2408 case DstInCompositeOp:
2410 CompositeIn(&destination,&source,&composite);
2413 case OutCompositeOp:
2414 case SrcOutCompositeOp:
2416 CompositeOut(&source,&destination,&composite);
2419 case DstOutCompositeOp:
2421 CompositeOut(&destination,&source,&composite);
2424 case AtopCompositeOp:
2425 case SrcAtopCompositeOp:
2427 CompositeAtop(&source,&destination,&composite);
2430 case DstAtopCompositeOp:
2432 CompositeAtop(&destination,&source,&composite);
2435 case XorCompositeOp:
2437 CompositeXor(&source,&destination,&composite);
2440 /* Mathematical Compositions */
2441 case PlusCompositeOp:
2443 CompositePlus(&source,&destination,channel,&composite);
2446 case MinusDstCompositeOp:
2448 CompositeMinus(&source,&destination,channel,&composite);
2451 case MinusSrcCompositeOp:
2453 CompositeMinus(&destination,&source,channel,&composite);
2456 case ModulusAddCompositeOp:
2458 CompositeModulusAdd(&source,&destination,channel,&composite);
2461 case ModulusSubtractCompositeOp:
2463 CompositeModulusSubtract(&source,&destination,channel,&composite);
2466 case DifferenceCompositeOp:
2468 CompositeDifference(&source,&destination,channel,&composite);
2471 case ExclusionCompositeOp:
2473 CompositeExclusion(&source,&destination,channel,&composite);
2476 case MultiplyCompositeOp:
2478 CompositeMultiply(&source,&destination,channel,&composite);
2481 case ScreenCompositeOp:
2483 CompositeScreen(&source,&destination,channel,&composite);
2486 case DivideDstCompositeOp:
2488 CompositeDivide(&source,&destination,channel,&composite);
2491 case DivideSrcCompositeOp:
2493 CompositeDivide(&destination,&source,channel,&composite);
2496 case DarkenCompositeOp:
2498 CompositeDarken(&source,&destination,channel,&composite);
2501 case LightenCompositeOp:
2503 CompositeLighten(&source,&destination,channel,&composite);
2506 case DarkenIntensityCompositeOp:
2508 CompositeDarkenIntensity(&source,&destination,channel,&composite);
2511 case LightenIntensityCompositeOp:
2513 CompositeLightenIntensity(&source,&destination,channel,&composite);
2516 case MathematicsCompositeOp:
2518 CompositeMathematics(&source,&destination,channel,&geometry_info,
2522 /* Lighting Compositions */
2523 case ColorDodgeCompositeOp:
2525 CompositeColorDodge(&source,&destination,&composite);
2528 case ColorBurnCompositeOp:
2530 CompositeColorBurn(&source,&destination,&composite);
2533 case LinearDodgeCompositeOp:
2535 CompositeLinearDodge(&source,&destination,&composite);
2538 case LinearBurnCompositeOp:
2540 CompositeLinearBurn(&source,&destination,&composite);
2543 case HardLightCompositeOp:
2545 CompositeHardLight(&source,&destination,&composite);
2548 case OverlayCompositeOp:
2550 /* Overlay = Reversed HardLight. */
2551 CompositeHardLight(&destination,&source,&composite);
2554 case SoftLightCompositeOp:
2556 CompositeSoftLight(&source,&destination,&composite);
2559 case LinearLightCompositeOp:
2561 CompositeLinearLight(&source,&destination,&composite);
2564 case PegtopLightCompositeOp:
2566 CompositePegtopLight(&source,&destination,&composite);
2569 case VividLightCompositeOp:
2571 CompositeVividLight(&source,&destination,&composite);
2574 case PinLightCompositeOp:
2576 CompositePinLight(&source,&destination,&composite);
2579 /* Other Composition */
2580 case ChangeMaskCompositeOp:
2582 if ((composite.opacity > ((MagickRealType) QuantumRange/2.0)) ||
2583 (IsMagickColorSimilar(&source,&destination) != MagickFalse))
2584 composite.opacity=(MagickRealType) TransparentOpacity;
2586 composite.opacity=(MagickRealType) OpaqueOpacity;
2589 case BumpmapCompositeOp:
2591 if (source.opacity == TransparentOpacity)
2593 CompositeBumpmap(&source,&destination,&composite);
2596 case DissolveCompositeOp:
2598 MagickPixelCompositeOver(&source,(MagickRealType) (QuantumRange-
2599 source_dissolve*(QuantumRange-source.opacity)),&destination,
2600 (MagickRealType) (QuantumRange-destination_dissolve*(QuantumRange-
2601 destination.opacity)),&composite);
2604 case BlendCompositeOp:
2606 MagickPixelCompositeBlend(&source,source_dissolve,&destination,
2607 destination_dissolve,&composite);
2610 case ThresholdCompositeOp:
2612 CompositeThreshold(&source,&destination,threshold,amount,&composite);
2615 case ModulateCompositeOp:
2620 if (source.opacity == TransparentOpacity)
2622 offset=(ssize_t) (MagickPixelIntensityToQuantum(&source)-midpoint);
2625 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2626 &saturation,&brightness);
2627 brightness+=(0.01*percent_brightness*offset)/midpoint;
2628 saturation*=0.01*percent_saturation;
2629 HSBComposite(hue,saturation,brightness,&composite.red,
2630 &composite.green,&composite.blue);
2633 case HueCompositeOp:
2635 if (source.opacity == TransparentOpacity)
2637 if (destination.opacity == TransparentOpacity)
2642 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2643 &saturation,&brightness);
2644 CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
2645 HSBComposite(hue,saturation,brightness,&composite.red,
2646 &composite.green,&composite.blue);
2647 if (source.opacity < destination.opacity)
2648 composite.opacity=source.opacity;
2651 case SaturateCompositeOp:
2653 if (source.opacity == TransparentOpacity)
2655 if (destination.opacity == TransparentOpacity)
2660 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2661 &saturation,&brightness);
2662 CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
2664 HSBComposite(hue,saturation,brightness,&composite.red,
2665 &composite.green,&composite.blue);
2666 if (source.opacity < destination.opacity)
2667 composite.opacity=source.opacity;
2670 case LuminizeCompositeOp:
2672 if (source.opacity == TransparentOpacity)
2674 if (destination.opacity == TransparentOpacity)
2679 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2680 &saturation,&brightness);
2681 CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
2683 HSBComposite(hue,saturation,brightness,&composite.red,
2684 &composite.green,&composite.blue);
2685 if (source.opacity < destination.opacity)
2686 composite.opacity=source.opacity;
2689 case ColorizeCompositeOp:
2691 if (source.opacity == TransparentOpacity)
2693 if (destination.opacity == TransparentOpacity)
2698 CompositeHSB(destination.red,destination.green,destination.blue,&sans,
2700 CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
2702 HSBComposite(hue,saturation,brightness,&composite.red,
2703 &composite.green,&composite.blue);
2704 if (source.opacity < destination.opacity)
2705 composite.opacity=source.opacity;
2708 case CopyRedCompositeOp:
2709 case CopyCyanCompositeOp:
2711 composite.red=source.red;
2714 case CopyGreenCompositeOp:
2715 case CopyMagentaCompositeOp:
2717 composite.green=source.green;
2720 case CopyBlueCompositeOp:
2721 case CopyYellowCompositeOp:
2723 composite.blue=source.blue;
2726 case CopyOpacityCompositeOp:
2728 if (source.matte == MagickFalse)
2730 composite.opacity=(MagickRealType) (QuantumRange-
2731 MagickPixelIntensityToQuantum(&source));
2734 composite.opacity=source.opacity;
2737 case CopyBlackCompositeOp:
2739 if (source.colorspace != CMYKColorspace)
2740 ConvertRGBToCMYK(&source);
2741 composite.index=source.index;
2744 /* compose methods that are already handled */
2745 case BlurCompositeOp:
2746 case DisplaceCompositeOp:
2747 case DistortCompositeOp:
2755 if (image->colorspace == CMYKColorspace)
2757 composite.red=(MagickRealType) QuantumRange-composite.red;
2758 composite.green=(MagickRealType) QuantumRange-composite.green;
2759 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2760 composite.index=(MagickRealType) QuantumRange-composite.index;
2762 SetRedPixelComponent(q,ClampToQuantum(composite.red));
2763 SetGreenPixelComponent(q,ClampToQuantum(composite.green));
2764 SetBluePixelComponent(q,ClampToQuantum(composite.blue));
2765 SetOpacityPixelComponent(q,ClampToQuantum(composite.opacity));
2766 if (image->colorspace == CMYKColorspace)
2767 SetIndexPixelComponent(indexes+x,ClampToQuantum(composite.index));
2769 if (p >= (pixels+composite_image->columns))
2773 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2775 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2780 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2781 #pragma omp critical (MagickCore_CompositeImageChannel)
2783 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2785 if (proceed == MagickFalse)
2789 composite_view=DestroyCacheView(composite_view);
2790 image_view=DestroyCacheView(image_view);
2791 if (destination_image != (Image * ) NULL)
2792 destination_image=DestroyImage(destination_image);
2797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2801 % T e x t u r e I m a g e %
2805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2807 % TextureImage() repeatedly tiles the texture image across and down the image
2810 % The format of the TextureImage method is:
2812 % MagickBooleanType TextureImage(Image *image,const Image *texture)
2814 % A description of each parameter follows:
2816 % o image: the image.
2818 % o texture: This image is the texture to layer on the background.
2821 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
2823 #define TextureImageTag "Texture/Image"
2838 assert(image != (Image *) NULL);
2839 if (image->debug != MagickFalse)
2840 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2841 assert(image->signature == MagickSignature);
2842 if (texture == (const Image *) NULL)
2843 return(MagickFalse);
2844 (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
2845 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2846 return(MagickFalse);
2848 if ((image->compose != CopyCompositeOp) &&
2849 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2850 (texture->matte != MagickFalse)))
2853 Tile texture onto the image background.
2855 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2856 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2858 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
2863 if (status == MagickFalse)
2865 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2870 thread_status=CompositeImage(image,image->compose,texture,x+
2871 texture->tile_offset.x,y+texture->tile_offset.y);
2872 if (thread_status == MagickFalse)
2874 status=thread_status;
2878 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2883 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2884 #pragma omp critical (MagickCore_TextureImage)
2886 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2888 if (proceed == MagickFalse)
2892 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2893 image->rows,image->rows);
2897 Tile texture onto the image background (optimized).
2900 exception=(&image->exception);
2901 image_view=AcquireCacheView(image);
2902 texture_view=AcquireCacheView(texture);
2903 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2904 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2906 for (y=0; y < (ssize_t) image->rows; y++)
2911 register const IndexPacket
2914 register const PixelPacket
2917 register IndexPacket
2923 register PixelPacket
2929 if (status == MagickFalse)
2931 p=GetCacheViewVirtualPixels(texture_view,texture->tile_offset.x,(y+
2932 texture->tile_offset.y) % texture->rows,texture->columns,1,exception);
2933 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2935 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2940 texture_indexes=GetCacheViewVirtualIndexQueue(texture_view);
2941 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2942 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2944 width=texture->columns;
2945 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2946 width=image->columns-x;
2947 (void) CopyMagickMemory(q,p,width*sizeof(*p));
2948 if ((image->colorspace == CMYKColorspace) &&
2949 (texture->colorspace == CMYKColorspace))
2951 (void) CopyMagickMemory(indexes,texture_indexes,width*
2957 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2958 if (sync == MagickFalse)
2960 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2965 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2966 #pragma omp critical (MagickCore_TextureImage)
2968 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2970 if (proceed == MagickFalse)
2974 texture_view=DestroyCacheView(texture_view);
2975 image_view=DestroyCacheView(image_view);