2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
7 % C O O MM MM P P O O SS I T E %
8 % C O O M M M PPPP O O SSS I T EEE %
9 % C O O M M P O O SS I T E %
10 % CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
13 % MagickCore Image Composite Methods %
20 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-private.h"
47 #include "MagickCore/cache-view.h"
48 #include "MagickCore/client.h"
49 #include "MagickCore/color.h"
50 #include "MagickCore/color-private.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/colorspace-private.h"
53 #include "MagickCore/composite.h"
54 #include "MagickCore/composite-private.h"
55 #include "MagickCore/constitute.h"
56 #include "MagickCore/draw.h"
57 #include "MagickCore/fx.h"
58 #include "MagickCore/gem.h"
59 #include "MagickCore/geometry.h"
60 #include "MagickCore/image.h"
61 #include "MagickCore/image-private.h"
62 #include "MagickCore/list.h"
63 #include "MagickCore/log.h"
64 #include "MagickCore/monitor.h"
65 #include "MagickCore/monitor-private.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/option.h"
68 #include "MagickCore/pixel-accessor.h"
69 #include "MagickCore/property.h"
70 #include "MagickCore/quantum.h"
71 #include "MagickCore/resample.h"
72 #include "MagickCore/resource_.h"
73 #include "MagickCore/string_.h"
74 #include "MagickCore/thread-private.h"
75 #include "MagickCore/utility.h"
76 #include "MagickCore/utility-private.h"
77 #include "MagickCore/version.h"
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84 % C o m p o s i t e I m a g e %
88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 % CompositeImage() returns the second image composited onto the first
91 % at the specified offset, using the specified composite method.
93 % The format of the CompositeImage method is:
95 % MagickBooleanType CompositeImage(Image *image,
96 % const CompositeOperator compose,Image *composite_image,
97 % const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
99 % A description of each parameter follows:
101 % o image: the destination image, modified by he composition
103 % o compose: This operator affects how the composite is applied to
104 % the image. The operators and how they are utilized are listed here
105 % http://www.w3.org/TR/SVG12/#compositing.
107 % o composite_image: the composite (source) image.
109 % o x_offset: the column offset of the composited image.
111 % o y_offset: the row offset of the composited image.
113 % Extra Controls from Image meta-data in 'composite_image' (artifacts)
116 % A string containing extra numerical arguments for specific compose
117 % methods, generally expressed as a 'geometry' or a comma separated list
120 % Compose methods needing such arguments include "BlendCompositeOp" and
121 % "DisplaceCompositeOp".
123 % o "compose:outside-overlay"
124 % Modify how the composition is to effect areas not directly covered
125 % by the 'composite_image' at the offset given. Normally this is
126 % dependant on the 'compose' method, especially Duff-Porter methods.
128 % If set to "false" then disable all normal handling of pixels not
129 % covered by the composite_image. Typically used for repeated tiling
130 % of the composite_image by the calling API.
132 % Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
134 % o exception: return any errors or warnings in this structure.
138 static inline double MagickMin(const double x,const double y)
144 static inline double MagickMax(const double x,const double y)
152 Programmers notes on SVG specification.
154 A Composition is defined by...
155 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
156 Blending areas : X = 1 for area of overlap ie: f(Sc,Dc)
157 Y = 1 for source preserved
158 Z = 1 for destination preserved
160 Conversion to transparency (then optimized)
161 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
162 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
165 Sca = Sc*Sa normalized Source color divided by Source alpha
166 Dca = Dc*Da normalized Dest color divided by Dest alpha
167 Dc' = Dca'/Da' the desired color value for this channel.
169 Da' (alpha result) is stored as 'gamma' in the functions.
171 The compose functions defined is just simplifications of the above
172 formula on a case by case bases.
176 The above SVG definitions also defines that Mathematical Composition
177 methods should use a 'Over' blending mode for Alpha Channel.
178 It however was not applied for composition modes of 'Plus', 'Minus',
179 the modulus versions of 'Add' and 'Subtract'.
181 Mathematical operator changes to be applied from IM v6.7...
183 1/ Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
184 'ModulusAdd' and 'ModulusSubtract' for clarity.
186 2/ All mathematical compositions work as per the SVG specification
187 with regard to blending. This now includes 'ModulusAdd' and
190 3/ When the special channel flag 'sync' (syncronize channel updates)
191 is turned off (enabled by default) then mathematical compositions are
192 only performed on the channels specified, and are applied
193 independantally of each other. In other words the mathematics is
194 performed as 'pure' mathematical operations, rather than as image
198 static inline MagickRealType Atop(const MagickRealType p,
199 const MagickRealType Sa,const MagickRealType q,
200 const MagickRealType magick_unused(Da))
202 return(p*Sa+q*(1.0-Sa)); /* Da optimized out, Da/gamma => 1.0 */
205 static inline void CompositeAtop(const PixelInfo *p,const PixelInfo *q,
206 PixelInfo *composite)
211 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
212 composite->alpha=q->alpha; /* optimized Da = 1.0-Gamma */
213 composite->red=Atop(p->red,Sa,q->red,1.0);
214 composite->green=Atop(p->green,Sa,q->green,1.0);
215 composite->blue=Atop(p->blue,Sa,q->blue,1.0);
216 if (q->colorspace == CMYKColorspace)
217 composite->black=Atop(p->black,Sa,q->black,1.0);
221 Bumpmap: Multiply by overlay intensity
222 What is this Composition actually method for? Can't find any specification!
224 I think this was meant to be a 'HardLight effect' using a Shaded Image!
225 That is a Embossing, using a height map! Better to do it piecemeal.
227 static inline void CompositeBumpmap(const PixelInfo *p,const PixelInfo *q,
228 PixelInfo *composite)
233 intensity=(MagickRealType) GetPixelInfoIntensity(p);
234 composite->red=QuantumScale*intensity*q->red;
235 composite->green=QuantumScale*intensity*q->green;
236 composite->blue=QuantumScale*intensity*q->blue;
237 composite->alpha=(MagickRealType) QuantumScale*intensity*p->alpha;
238 if (q->colorspace == CMYKColorspace)
239 composite->black=QuantumScale*intensity*q->black;
242 static inline void CompositeClear(const PixelInfo *q,PixelInfo *composite)
244 composite->alpha=(MagickRealType) TransparentAlpha;
246 composite->green=0.0;
248 composite->black=1.0;
251 static MagickRealType ColorBurn(const MagickRealType Sca,
252 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
254 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
255 return(Sa*Da+Dca*(1.0-Sa));
256 if (Sca < MagickEpsilon)
257 return(Dca*(1.0-Sa));
258 return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
261 static inline void CompositeColorBurn(const PixelInfo *p,const PixelInfo *q,
262 PixelInfo *composite)
269 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
270 Da=QuantumScale*q->alpha;
271 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
272 composite->alpha=(MagickRealType) QuantumRange*gamma;
273 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
274 composite->red=gamma*ColorBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
276 composite->green=gamma*ColorBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
278 composite->blue=gamma*ColorBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
280 if (q->colorspace == CMYKColorspace)
281 composite->black=gamma*ColorBurn(QuantumScale*p->black*Sa,Sa,QuantumScale*
286 static MagickRealType ColorDodge(const MagickRealType Sca,
287 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
290 Working from first principles using the original formula:
294 This works correctly! Looks like the 2004 SVG model was right but just
295 required a extra condition for correct handling.
297 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
298 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
299 if (fabs(Sca-Sa) < MagickEpsilon)
300 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
301 return(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
304 static inline void CompositeColorDodge(const PixelInfo *p,const PixelInfo *q,
305 PixelInfo *composite)
312 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
313 Da=QuantumScale*q->alpha;
314 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
315 composite->alpha=(MagickRealType) QuantumRange*gamma;
316 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
317 composite->red=gamma*ColorDodge(QuantumScale*p->red*Sa,Sa,QuantumScale*
319 composite->green=gamma*ColorDodge(QuantumScale*p->green*Sa,Sa,QuantumScale*
321 composite->blue=gamma*ColorDodge(QuantumScale*p->blue*Sa,Sa,QuantumScale*
323 if (q->colorspace == CMYKColorspace)
324 composite->black=gamma*ColorDodge(QuantumScale*p->black*Sa,Sa,QuantumScale*
328 static inline MagickRealType Darken(const MagickRealType p,
329 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
332 return(MagickOver_(p,alpha,q,beta)); /* src-over */
333 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
336 static inline void CompositeDarken(const Image *image,const PixelInfo *p,
337 const PixelInfo *q,PixelInfo *composite)
343 Darken is equivalent to a 'Minimum' method OR a greyscale version of a
344 binary 'Or' OR the 'Intersection' of pixel sets.
346 if (image->channel_mask != DefaultChannels)
349 Handle channels as separate grayscale channels.
351 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
352 composite->red=MagickMin(p->red,q->red);
353 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
354 composite->green=MagickMin(p->green,q->green);
355 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
356 composite->blue=MagickMin(p->blue,q->blue);
357 if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 &&
358 (q->colorspace == CMYKColorspace))
359 composite->black=MagickMin(p->black,q->black);
360 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
361 composite->alpha=MagickMax(p->alpha,q->alpha);
364 composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */
365 gamma=1.0-QuantumScale*composite->alpha;
366 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
367 composite->red=gamma*Darken(p->red,p->alpha,q->red,q->alpha);
368 composite->green=gamma*Darken(p->green,p->alpha,q->green,q->alpha);
369 composite->blue=gamma*Darken(p->blue,p->alpha,q->blue,q->alpha);
370 if (q->colorspace == CMYKColorspace)
371 composite->black=gamma*Darken(p->black,p->alpha,q->black,q->alpha);
374 static inline void CompositeDarkenIntensity(const Image *image,
375 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
382 Select the pixel based on the intensity level.
383 If 'Sync' flag select whole pixel based on alpha weighted intensity.
384 Otherwise use intensity only, but restrict copy according to channel.
386 if (image->channel_mask != DefaultChannels)
391 from_p=GetPixelInfoIntensity(p) < GetPixelInfoIntensity(q) ? MagickTrue :
393 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
394 composite->red=from_p != MagickFalse ? p->red : q->red;
395 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
396 composite->green=from_p != MagickFalse ? p->green : q->green;
397 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
398 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
399 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
400 (q->colorspace == CMYKColorspace))
401 composite->black=from_p != MagickFalse ? p->black : q->black;
402 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
403 composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha;
406 Sa=QuantumScale*p->alpha;
407 Da=QuantumScale*q->alpha;
408 *composite=(Sa*GetPixelInfoIntensity(p) < Da*GetPixelInfoIntensity(q)) ?
412 static inline MagickRealType Difference(const MagickRealType p,
413 const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
416 Optimized by Multipling by QuantumRange (taken from gamma).
418 return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q));
421 static inline void CompositeDifference(const Image *image,const PixelInfo *p,
422 const PixelInfo *q,PixelInfo *composite)
429 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
430 Da=QuantumScale*q->alpha;
431 if (image->channel_mask != DefaultChannels)
434 Handle channels as separate grayscale channels.
436 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
437 composite->red=fabs((double) (p->red-q->red));
438 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
439 composite->green=fabs((double) (p->green-q->green));
440 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
441 composite->blue=fabs((double) (p->blue-q->blue));
442 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
443 (q->colorspace == CMYKColorspace))
444 composite->black=fabs((double) (p->black-q->black));
445 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
446 composite->alpha=fabs((double) (p->alpha-q->alpha));
449 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
450 composite->alpha=(MagickRealType) QuantumRange*gamma;
451 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
452 composite->red=gamma*Difference(p->red,Sa,q->red,Da);
453 composite->green=gamma*Difference(p->green,Sa,q->green,Da);
454 composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da);
455 if (q->colorspace == CMYKColorspace)
456 composite->black=gamma*Difference(p->black,Sa,q->black,Da);
459 static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
460 const MagickRealType Dca,const MagickRealType Da)
463 Divide Source by Destination
467 But with appropriate handling for special case of Dc == 0 specifically
468 so that f(Black,Black)=Black and f(non-Black,Black)=White.
469 It is however also important to correctly do 'over' alpha blending which
470 is why the formula becomes so complex.
472 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
473 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
474 if (fabs(Dca) < MagickEpsilon)
475 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
476 return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
479 static inline void CompositeDivide(const Image *image,const PixelInfo *p,
480 const PixelInfo *q,PixelInfo *composite)
487 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
488 Da=QuantumScale*q->alpha;
489 if (image->channel_mask != DefaultChannels)
492 Handle channels as separate grayscale channels.
494 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
495 composite->red=QuantumRange*Divide(QuantumScale*p->red,1.0,
496 QuantumScale*q->red,1.0);
497 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
498 composite->green=QuantumRange*Divide(QuantumScale*p->green,1.0,
499 QuantumScale*q->green,1.0);
500 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
501 composite->blue=QuantumRange*Divide(QuantumScale*p->blue,1.0,
502 QuantumScale*q->blue,1.0);
503 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
504 (q->colorspace == CMYKColorspace))
505 composite->black=QuantumRange*Divide(QuantumScale*p->black,1.0,
506 QuantumScale*q->black,1.0);
507 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
508 composite->alpha=QuantumRange*(1.0-Divide(Sa,1.0,Da,1.0));
511 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
512 composite->alpha=(MagickRealType) QuantumRange*gamma;
513 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
514 composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale*
516 composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale*
518 composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale*
520 if (q->colorspace == CMYKColorspace)
521 composite->black=gamma*Divide(QuantumScale*p->black*Sa,Sa,QuantumScale*
525 static MagickRealType Exclusion(const MagickRealType Sca,
526 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
528 return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
531 static inline void CompositeExclusion(const Image *image,const PixelInfo *p,
532 const PixelInfo *q,PixelInfo *composite)
539 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
540 Da=QuantumScale*q->alpha;
541 if (image->channel_mask != DefaultChannels)
544 Handle channels as separate grayscale channels.
546 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
547 composite->red=QuantumRange*Exclusion(QuantumScale*p->red,1.0,
548 QuantumScale*q->red,1.0);
549 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
550 composite->green=QuantumRange*Exclusion(QuantumScale*p->green,1.0,
551 QuantumScale*q->green,1.0);
552 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
553 composite->blue=QuantumRange*Exclusion(QuantumScale*p->blue,1.0,
554 QuantumScale*q->blue,1.0);
555 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
556 (q->colorspace == CMYKColorspace))
557 composite->black=QuantumRange*Exclusion(QuantumScale*p->black,1.0,
558 QuantumScale*q->black,1.0);
559 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
560 composite->alpha=QuantumRange*(1.0-Exclusion(Sa,1.0,Da,1.0));
563 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
564 composite->alpha=(MagickRealType) QuantumRange*gamma;
565 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
566 composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale*
568 composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale*
570 composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale*
572 if (q->colorspace == CMYKColorspace)
573 composite->black=gamma*Exclusion(QuantumScale*p->black*Sa,Sa,
574 QuantumScale*q->black*Da,Da);
577 static MagickRealType HardLight(const MagickRealType Sca,
578 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
581 return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
582 return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
585 static inline void CompositeHardLight(const PixelInfo *p,const PixelInfo *q,
586 PixelInfo *composite)
593 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
594 Da=QuantumScale*q->alpha;
595 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
596 composite->alpha=(MagickRealType) QuantumRange*gamma;
597 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
598 composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
600 composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
602 composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
604 if (q->colorspace == CMYKColorspace)
605 composite->black=gamma*HardLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
609 static void CompositeHSB(const MagickRealType red,const MagickRealType green,
610 const MagickRealType blue,double *hue,double *saturation,double *brightness)
618 Convert RGB to HSB colorspace.
620 assert(hue != (double *) NULL);
621 assert(saturation != (double *) NULL);
622 assert(brightness != (double *) NULL);
623 max=(red > green ? red : green);
626 min=(red < green ? red : green);
631 *brightness=(double) (QuantumScale*max);
632 if (fabs(max) < MagickEpsilon)
634 *saturation=(double) (1.0-min/max);
636 if (fabs(delta) < MagickEpsilon)
638 if (fabs(red-max) < MagickEpsilon)
639 *hue=(double) ((green-blue)/delta);
641 if (fabs(green-max) < MagickEpsilon)
642 *hue=(double) (2.0+(blue-red)/delta);
644 if (fabs(blue-max) < MagickEpsilon)
645 *hue=(double) (4.0+(red-green)/delta);
652 static inline MagickRealType In(const MagickRealType p,const MagickRealType Sa,
653 const MagickRealType magick_unused(q),const MagickRealType Da)
659 static inline void CompositeIn(const PixelInfo *p,const PixelInfo *q,
660 PixelInfo *composite)
668 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
669 Da=QuantumScale*q->alpha;
671 composite->alpha=(MagickRealType) QuantumRange*gamma;
672 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
673 /* really this just preserves the src or p color as is! */
674 composite->red=gamma*In(p->red,Sa,q->red,Da);
675 composite->green=gamma*In(p->green,Sa,q->green,Da);
676 composite->blue=gamma*In(p->blue,Sa,q->blue,Da);
677 if (q->colorspace == CMYKColorspace)
678 composite->black=gamma*In(p->black,Sa,q->black,Da);
680 /* Simplified to a multiply of the Alpha Channel */
681 *composite=*p; /* structure copy */
682 composite->alpha=QuantumScale*p->alpha*q->alpha;
686 static inline MagickRealType Lighten(const MagickRealType p,
687 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
690 return(MagickOver_(p,alpha,q,beta)); /* src-over */
691 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
694 static inline void CompositeLighten(const Image *image,const PixelInfo *p,
695 const PixelInfo *q,PixelInfo *composite)
701 Lighten is also equvalent to a 'Maximum' method OR a greyscale version of a
702 binary 'And' OR the 'Union' of pixel sets.
704 if (image->channel_mask != DefaultChannels)
707 Handle channels as separate grayscale channels
709 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
710 composite->red=MagickMax(p->red,q->red);
711 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
712 composite->green=MagickMax(p->green,q->green);
713 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
714 composite->blue=MagickMax(p->blue,q->blue);
715 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
716 (q->colorspace == CMYKColorspace))
717 composite->black=MagickMax(p->black,q->black);
718 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
719 composite->alpha=MagickMin(p->alpha,q->alpha);
722 composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */
723 gamma=1.0-QuantumScale*composite->alpha;
724 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
725 composite->red=gamma*Lighten(p->red,p->alpha,q->red,q->alpha);
726 composite->green=gamma*Lighten(p->green,p->alpha,q->green,q->alpha);
727 composite->blue=gamma*Lighten(p->blue,p->alpha,q->blue,q->alpha);
728 if (q->colorspace == CMYKColorspace)
729 composite->black=gamma*Lighten(p->black,p->alpha,q->black,q->alpha);
732 static inline void CompositeLightenIntensity(const Image *image,
733 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
740 Select the pixel based on the intensity level.
741 If 'Sync' flag select whole pixel based on alpha weighted intensity.
742 Otherwise use Intenisty only, but restrict copy according to channel.
744 if (image->channel_mask != DefaultChannels)
749 from_p=GetPixelInfoIntensity(p) > GetPixelInfoIntensity(q) ? MagickTrue :
751 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
752 composite->red=from_p != MagickFalse ? p->red : q->red;
753 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
754 composite->green=from_p != MagickFalse ? p->green : q->green;
755 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
756 composite->blue=from_p != MagickFalse ? p->blue : q->blue;
757 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
758 (q->colorspace == CMYKColorspace))
759 composite->black=from_p != MagickFalse ? p->black : q->black;
760 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
761 composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha;
764 Sa=QuantumScale*p->alpha;
765 Da=QuantumScale*q->alpha;
766 *composite=(Sa*GetPixelInfoIntensity(p) > Da*GetPixelInfoIntensity(q)) ?
770 static inline void CompositeLinearDodge(const PixelInfo *p,const PixelInfo *q,
771 PixelInfo *composite)
778 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
779 Da=QuantumScale*q->alpha;
780 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
781 composite->alpha=(MagickRealType) QuantumRange*gamma;
782 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
783 composite->red=gamma*(p->red*Sa+q->red*Da);
784 composite->green=gamma*(p->green*Sa+q->green*Da);
785 composite->blue=gamma*(p->blue*Sa+q->blue*Da);
786 if (q->colorspace == CMYKColorspace)
787 composite->black=gamma*(p->black*Sa+q->black*Da);
791 static inline MagickRealType LinearBurn(const MagickRealType Sca,
792 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
795 LinearBurn: as defined by Abode Photoshop, according to
796 http://www.simplefilter.de/en/basics/mixmods.html is:
798 f(Sc,Dc) = Sc + Dc - 1
800 return(Sca+Dca-Sa*Da);
803 static inline void CompositeLinearBurn(const PixelInfo *p,const PixelInfo *q,
804 PixelInfo *composite)
811 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
812 Da=QuantumScale*q->alpha;
813 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
814 composite->alpha=(MagickRealType) QuantumRange*gamma;
815 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
816 composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
818 composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
820 composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
822 if (q->colorspace == CMYKColorspace)
823 composite->black=gamma*LinearBurn(QuantumScale*p->black*Sa,Sa,QuantumScale*
827 static inline MagickRealType LinearLight(const MagickRealType Sca,
828 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
831 LinearLight: as defined by Abode Photoshop, according to
832 http://www.simplefilter.de/en/basics/mixmods.html is:
834 f(Sc,Dc) = Dc + 2*Sc - 1
836 return((Sca-Sa)*Da+Sca+Dca);
839 static inline void CompositeLinearLight(const PixelInfo *p,const PixelInfo *q,
840 PixelInfo *composite)
847 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
848 Da=QuantumScale*q->alpha;
849 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
850 composite->alpha=(MagickRealType) QuantumRange*gamma;
851 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
852 composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
854 composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
856 composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
858 if (q->colorspace == CMYKColorspace)
859 composite->black=gamma*LinearLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
863 static inline MagickRealType Mathematics(const MagickRealType Sca,
864 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
865 const GeometryInfo *geometry_info)
871 'Mathematics' a free form user control mathematical composition is defined
874 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
876 Where the arguments A,B,C,D are (currently) passed to composite as
877 a command separated 'geometry' string in "compose:args" image artifact.
879 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
881 Applying the SVG transparency formula (see above), we get...
883 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
885 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
888 gamma=geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
889 geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
894 static inline void CompositeMathematics(const Image *image,const PixelInfo *p,
895 const PixelInfo *q,const GeometryInfo *args,PixelInfo *composite)
902 Sa=QuantumScale*p->alpha; /* ??? - AT */
903 Da=QuantumScale*q->alpha;
904 if (image->channel_mask != DefaultChannels)
907 Handle channels as separate grayscale channels.
909 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
910 composite->red=QuantumRange*Mathematics(QuantumScale*p->red,1.0,
911 QuantumScale*q->red,1.0,args);
912 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
913 composite->green=QuantumRange*Mathematics(QuantumScale*p->green,1.0,
914 QuantumScale*q->green,1.0,args);
915 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
916 composite->blue=QuantumRange*Mathematics(QuantumScale*p->blue,1.0,
917 QuantumScale*q->blue,1.0,args);
918 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
919 (q->colorspace == CMYKColorspace))
920 composite->black=QuantumRange*Mathematics(QuantumScale*p->black,1.0,
921 QuantumScale*q->black,1.0,args);
922 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
923 composite->alpha=QuantumRange*(1.0-Mathematics(Sa,1.0,Da,1.0,args));
926 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
927 composite->alpha=(MagickRealType) QuantumRange*gamma;
928 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
929 composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
931 composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,
932 QuantumScale*q->green*Da,Da,args);
933 composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
935 if (q->colorspace == CMYKColorspace)
936 composite->black=gamma*Mathematics(QuantumScale*p->black*Sa,Sa,
937 QuantumScale*q->black*Da,Da,args);
940 static inline void CompositePlus(const Image *image,const PixelInfo *p,
941 const PixelInfo *q,PixelInfo *composite)
944 NOTE: "Plus" does not use 'over' alpha-blending but uses a special
945 'plus' form of alph-blending. It is the ONLY mathematical operator to
946 do this. this is what makes it different to the otherwise equivalent
947 "LinearDodge" composition method.
949 Note however that color channels are still effected by the alpha channel
950 as a result of the blending, making it just as useless for independant
951 channel maths, just like all other mathematical composition methods.
953 As such the removal of the 'sync' flag, is still a usful convention.
955 The CompositePixelInfoPlus() function is defined in
956 "composite-private.h" so it can also be used for Image Blending.
958 if (image->channel_mask != DefaultChannels)
961 Handle channels as separate grayscale channels.
963 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
964 composite->red=p->red+q->red;
965 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
966 composite->green=p->green+q->green;
967 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
968 composite->blue=p->blue+q->blue;
969 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
970 (q->colorspace == CMYKColorspace))
971 composite->black=p->black+q->black;
972 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
973 composite->alpha=p->alpha+q->alpha-QuantumRange;
976 CompositePixelInfoPlus(p,p->alpha,q,q->alpha,composite);
979 static inline MagickRealType Minus(const MagickRealType Sca,
980 const MagickRealType Sa,const MagickRealType Dca,
981 const MagickRealType magick_unused(Da))
984 Minus Source from Destination
988 return(Sca+Dca-2.0*Dca*Sa);
991 static inline void CompositeMinus(const Image *image,const PixelInfo *p,
992 const PixelInfo *q,PixelInfo *composite)
999 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1000 Da=QuantumScale*q->alpha;
1001 if (image->channel_mask != DefaultChannels)
1004 Handle channels as separate grayscale channels.
1006 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1007 composite->red=p->red-q->red;
1008 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1009 composite->green=p->green-q->green;
1010 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1011 composite->blue=p->blue-q->blue;
1012 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1013 (q->colorspace == CMYKColorspace))
1014 composite->black=p->black-q->black;
1015 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1016 composite->alpha=QuantumRange*(1.0-(Sa-Da));
1019 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1020 composite->alpha=(MagickRealType) QuantumRange*gamma;
1021 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1022 composite->red=gamma*Minus(p->red*Sa,Sa,q->red*Da,Da);
1023 composite->green=gamma*Minus(p->green*Sa,Sa,q->green*Da,Da);
1024 composite->blue=gamma*Minus(p->blue*Sa,Sa,q->blue*Da,Da);
1025 if (q->colorspace == CMYKColorspace)
1026 composite->black=gamma*Minus(p->black*Sa,Sa,q->black*Da,Da);
1029 static inline MagickRealType ModulusAdd(const MagickRealType p,
1030 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1036 if (pixel > QuantumRange)
1037 pixel-=(QuantumRange+1.0);
1038 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1041 static inline void CompositeModulusAdd(const Image *image,const PixelInfo *p,
1042 const PixelInfo *q,PixelInfo *composite)
1049 if (image->channel_mask != DefaultChannels)
1052 Handle channels as separate grayscale channels.
1054 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1055 composite->red=ModulusAdd(p->red,1.0,q->red,1.0);
1056 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1057 composite->green=ModulusAdd(p->green,1.0,q->green,1.0);
1058 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1059 composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0);
1060 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1061 (q->colorspace == CMYKColorspace))
1062 composite->black=ModulusAdd(p->black,1.0,q->black,1.0);
1063 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1064 composite->alpha=ModulusAdd(p->alpha,1.0,q->alpha,1.0);
1067 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1068 Da=QuantumScale*q->alpha;
1069 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1070 composite->alpha=(MagickRealType) QuantumRange*gamma;
1071 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1072 composite->red=ModulusAdd(p->red,Sa,q->red,Da);
1073 composite->green=ModulusAdd(p->green,Sa,q->green,Da);
1074 composite->blue=ModulusAdd(p->blue,Sa,q->blue,Da);
1075 if (q->colorspace == CMYKColorspace)
1076 composite->black=ModulusAdd(p->black,Sa,q->black,Da);
1079 static inline MagickRealType ModulusSubtract(const MagickRealType p,
1080 const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
1087 pixel+=(QuantumRange+1.0);
1088 return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
1091 static inline void CompositeModulusSubtract(const Image *image,
1092 const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
1099 if (image->channel_mask != DefaultChannels)
1102 Handle channels as separate grayscale channels,
1104 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1105 composite->red=ModulusSubtract(p->red,1.0,q->red,1.0);
1106 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1107 composite->green=ModulusSubtract(p->green,1.0,q->green,1.0);
1108 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1109 composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0);
1110 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1111 (q->colorspace == CMYKColorspace))
1112 composite->black=ModulusSubtract(p->black,1.0,q->black,1.0);
1113 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1114 composite->alpha=ModulusSubtract(p->alpha,1.0,q->alpha,1.0);
1117 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1118 Da=QuantumScale*q->alpha;
1119 gamma = RoundToUnity(Sa+Da-Sa*Da);
1120 composite->alpha=(MagickRealType) QuantumRange*gamma;
1121 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1122 composite->red=ModulusSubtract(p->red,Sa,q->red,Da);
1123 composite->green=ModulusSubtract(p->green,Sa,q->green,Da);
1124 composite->blue=ModulusSubtract(p->blue,Sa,q->blue,Da);
1125 if (q->colorspace == CMYKColorspace)
1126 composite->black=ModulusSubtract(p->black,Sa,q->black,Da);
1129 static inline MagickRealType Multiply(const MagickRealType Sca,
1130 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1132 return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1135 static inline void CompositeMultiply(const Image *image,const PixelInfo *p,
1136 const PixelInfo *q,PixelInfo *composite)
1143 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1144 Da=QuantumScale*q->alpha;
1145 if (image->channel_mask != DefaultChannels)
1148 Handle channels as separate grayscale channels.
1150 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1151 composite->red=QuantumScale*p->red*q->red;
1152 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1153 composite->green=QuantumScale*p->green*q->green;
1154 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1155 composite->blue=QuantumScale*p->blue*q->blue;
1156 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1157 (q->colorspace == CMYKColorspace))
1158 composite->black=QuantumScale*p->black*q->black;
1159 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1160 composite->alpha=QuantumRange*(1.0-Sa*Da);
1163 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1164 composite->alpha=(MagickRealType) QuantumRange*gamma;
1165 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1166 composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
1168 composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
1170 composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1172 if (q->colorspace == CMYKColorspace)
1173 composite->black=gamma*Multiply(QuantumScale*p->black*Sa,Sa,
1174 QuantumScale*q->black*Da,Da);
1178 static inline MagickRealType Out(const MagickRealType p,const MagickRealType Sa,
1179 const MagickRealType magick_unused(q),const MagickRealType Da)
1181 return(Sa*p*(1.0-Da));
1185 static inline void CompositeOut(const PixelInfo *p,const PixelInfo *q,
1186 PixelInfo *composite)
1194 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1195 Da=QuantumScale*q->alpha;
1197 composite->alpha=(MagickRealType) QuantumRange*gamma;
1198 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1199 composite->red=gamma*Out(p->red,Sa,q->red,Da);
1200 composite->green=gamma*Out(p->green,Sa,q->green,Da);
1201 composite->blue=gamma*Out(p->blue,Sa,q->blue,Da);
1202 if (q->colorspace == CMYKColorspace)
1203 composite->black=gamma*Out(p->black,Sa,q->black,Da);
1205 /* Simplified to a negated multiply of the Alpha Channel */
1206 *composite=*p; /* structure copy */
1207 composite->alpha=p->alpha*(1.0-QuantumScale*q->alpha);
1211 static MagickRealType PegtopLight(const MagickRealType Sca,
1212 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1215 PegTop: A Soft-Light alternative: A continuous version of the Softlight
1216 function, producing very similar results.
1218 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
1220 See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
1222 if (fabs(Da) < MagickEpsilon)
1224 return(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+Dca*(1.0-Sa));
1227 static inline void CompositePegtopLight(const PixelInfo *p,const PixelInfo *q,
1228 PixelInfo *composite)
1235 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1236 Da=QuantumScale*q->alpha;
1237 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1238 composite->alpha=(MagickRealType) QuantumRange*gamma;
1239 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1240 composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1242 composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1244 composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1246 if (q->colorspace == CMYKColorspace)
1247 composite->black=gamma*PegtopLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1251 static MagickRealType PinLight(const MagickRealType Sca,const MagickRealType Sa,
1252 const MagickRealType Dca,const MagickRealType Da)
1255 PinLight: A Photoshop 7 composition method
1256 http://www.simplefilter.de/en/basics/mixmods.html
1258 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
1260 if (Dca*Sa < Da*(2.0*Sca-Sa))
1261 return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
1262 if ((Dca*Sa) > (2.0*Sca*Da))
1263 return(Sca*Da+Sca+Dca*(1.0-Sa));
1264 return(Sca*(1.0-Da)+Dca);
1267 static inline void CompositePinLight(const PixelInfo *p,const PixelInfo *q,
1268 PixelInfo *composite)
1275 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1276 Da=QuantumScale*q->alpha;
1277 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1278 composite->alpha=(MagickRealType) QuantumRange*gamma;
1279 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1280 composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1282 composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1284 composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1286 if (q->colorspace == CMYKColorspace)
1287 composite->black=gamma*PinLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1291 static inline MagickRealType Screen(const MagickRealType Sca,
1292 const MagickRealType Dca)
1295 Screen: A negated multiply
1296 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
1298 return(Sca+Dca-Sca*Dca);
1301 static inline void CompositeScreen(const Image *image,const PixelInfo *p,
1302 const PixelInfo *q,PixelInfo *composite)
1309 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1310 Da=QuantumScale*q->alpha;
1311 if (image->channel_mask != DefaultChannels)
1314 Handle channels as separate grayscale channels.
1316 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1317 composite->red=QuantumRange*Screen(QuantumScale*p->red,
1318 QuantumScale*q->red);
1319 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1320 composite->green=QuantumRange*Screen(QuantumScale*p->green,
1321 QuantumScale*q->green);
1322 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1323 composite->blue=QuantumRange*Screen(QuantumScale*p->blue,
1324 QuantumScale*q->blue);
1325 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1326 (q->colorspace == CMYKColorspace))
1327 composite->black=QuantumRange*Screen(QuantumScale*p->black,
1328 QuantumScale*q->black);
1329 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1330 composite->alpha=QuantumRange*(1.0-Screen(Sa,Da));
1333 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1334 composite->alpha=(MagickRealType) QuantumRange*gamma;
1335 Sa*=QuantumScale; Da*=QuantumScale; /* optimization */
1336 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1337 composite->red=gamma*Screen(p->red*Sa,q->red*Da);
1338 composite->green=gamma*Screen(p->green*Sa,q->green*Da);
1339 composite->blue=gamma*Screen(p->blue*Sa,q->blue*Da);
1340 if (q->colorspace == CMYKColorspace)
1341 composite->black=gamma*Screen(p->black*Sa,q->black*Da);
1344 static MagickRealType SoftLight(const MagickRealType Sca,
1345 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1352 New specification: March 2009 SVG specification.
1356 return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1357 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1359 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
1360 alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1363 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1367 static inline void CompositeSoftLight(const PixelInfo *p,const PixelInfo *q,
1368 PixelInfo *composite)
1375 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1376 Da=QuantumScale*q->alpha;
1377 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1378 composite->alpha=(MagickRealType) QuantumRange*gamma;
1379 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1380 composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1382 composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1384 composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1386 if (q->colorspace == CMYKColorspace)
1387 composite->black=gamma*SoftLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1391 static inline MagickRealType Threshold(const MagickRealType p,
1392 const MagickRealType q,const MagickRealType threshold,
1393 const MagickRealType amount)
1399 Multiply difference by amount, if differance larger than threshold???
1400 What use this is is completely unknown. The Opacity calculation appears to
1401 be inverted -- Anthony Thyssen
1406 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
1408 return(q+delta*amount);
1411 static inline void CompositeThreshold(const PixelInfo *p,const PixelInfo *q,
1412 const MagickRealType threshold,const MagickRealType amount,
1413 PixelInfo *composite)
1415 composite->red=Threshold(p->red,q->red,threshold,amount);
1416 composite->green=Threshold(p->green,q->green,threshold,amount);
1417 composite->blue=Threshold(p->blue,q->blue,threshold,amount);
1418 composite->alpha=Threshold(p->alpha,q->alpha,threshold,amount);
1419 if (q->colorspace == CMYKColorspace)
1420 composite->black=Threshold(p->black,q->black,threshold,amount);
1424 static MagickRealType VividLight(const MagickRealType Sca,
1425 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
1428 VividLight: A Photoshop 7 composition method. See
1429 http://www.simplefilter.de/en/basics/mixmods.html.
1431 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
1433 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
1434 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1435 if ((2.0*Sca) <= Sa)
1436 return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1437 return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1440 static inline void CompositeVividLight(const PixelInfo *p,const PixelInfo *q,
1441 PixelInfo *composite)
1448 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1449 Da=QuantumScale*q->alpha;
1450 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1451 composite->alpha=(MagickRealType) QuantumRange*gamma;
1452 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1453 composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1455 composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1457 composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1459 if (q->colorspace == CMYKColorspace)
1460 composite->black=gamma*VividLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
1464 static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
1465 const MagickRealType Dca,const MagickRealType Da)
1467 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
1470 static inline void CompositeXor(const PixelInfo *p,const PixelInfo *q,
1471 PixelInfo *composite)
1478 Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
1479 Da=QuantumScale*q->alpha;
1480 gamma=Sa+Da-2.0*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
1481 composite->alpha=(MagickRealType) QuantumRange*gamma;
1482 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1483 composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
1484 composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
1485 composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
1486 if (q->colorspace == CMYKColorspace)
1487 composite->black=gamma*Xor(p->black*Sa,Sa,q->black*Da,Da);
1490 static void HSBComposite(const double hue,const double saturation,
1491 const double brightness,MagickRealType *red,MagickRealType *green,
1492 MagickRealType *blue)
1502 Convert HSB to RGB colorspace.
1504 assert(red != (MagickRealType *) NULL);
1505 assert(green != (MagickRealType *) NULL);
1506 assert(blue != (MagickRealType *) NULL);
1507 if (saturation == 0.0)
1509 *red=(MagickRealType) QuantumRange*brightness;
1514 h=6.0*(hue-floor(hue));
1515 f=h-floor((double) h);
1516 p=brightness*(1.0-saturation);
1517 q=brightness*(1.0-saturation*f);
1518 t=brightness*(1.0-saturation*(1.0-f));
1524 *red=(MagickRealType) QuantumRange*brightness;
1525 *green=(MagickRealType) QuantumRange*t;
1526 *blue=(MagickRealType) QuantumRange*p;
1531 *red=(MagickRealType) QuantumRange*q;
1532 *green=(MagickRealType) QuantumRange*brightness;
1533 *blue=(MagickRealType) QuantumRange*p;
1538 *red=(MagickRealType) QuantumRange*p;
1539 *green=(MagickRealType) QuantumRange*brightness;
1540 *blue=(MagickRealType) QuantumRange*t;
1545 *red=(MagickRealType) QuantumRange*p;
1546 *green=(MagickRealType) QuantumRange*q;
1547 *blue=(MagickRealType) QuantumRange*brightness;
1552 *red=(MagickRealType) QuantumRange*t;
1553 *green=(MagickRealType) QuantumRange*p;
1554 *blue=(MagickRealType) QuantumRange*brightness;
1559 *red=(MagickRealType) QuantumRange*brightness;
1560 *green=(MagickRealType) QuantumRange*p;
1561 *blue=(MagickRealType) QuantumRange*q;
1567 MagickExport MagickBooleanType CompositeImage(Image *image,
1568 const CompositeOperator compose,const Image *composite_image,
1569 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
1571 #define CompositeImageTag "Composite/Image"
1590 modify_outside_overlay,
1598 destination_dissolve,
1615 Prepare composite image.
1617 assert(image != (Image *) NULL);
1618 assert(image->signature == MagickSignature);
1619 if (image->debug != MagickFalse)
1620 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1621 assert(composite_image != (Image *) NULL);
1622 assert(composite_image->signature == MagickSignature);
1623 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1624 return(MagickFalse);
1625 GetPixelInfo(image,&zero);
1626 destination_image=(Image *) NULL;
1628 destination_dissolve=1.0;
1629 modify_outside_overlay=MagickFalse;
1630 percent_brightness=100.0;
1631 percent_saturation=100.0;
1632 source_dissolve=1.0;
1636 case ClearCompositeOp:
1637 case SrcCompositeOp:
1639 case SrcInCompositeOp:
1640 case OutCompositeOp:
1641 case SrcOutCompositeOp:
1642 case DstInCompositeOp:
1643 case DstAtopCompositeOp:
1646 Modify destination outside the overlaid region.
1648 modify_outside_overlay=MagickTrue;
1651 case CopyCompositeOp:
1653 if ((x_offset < 0) || (y_offset < 0))
1655 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
1657 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
1660 image_view=AcquireCacheView(image);
1661 composite_view=AcquireCacheView(composite_image);
1662 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1663 #pragma omp parallel for schedule(dynamic,4) shared(status)
1665 for (y=0; y < (ssize_t) composite_image->rows; y++)
1670 register const Quantum
1679 if (status == MagickFalse)
1681 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1683 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1684 composite_image->columns,1,exception);
1685 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1690 for (x=0; x < (ssize_t) composite_image->columns; x++)
1692 SetPixelRed(image,GetPixelRed(composite_image,p),q);
1693 SetPixelGreen(image,GetPixelGreen(composite_image,p),q);
1694 SetPixelBlue(image,GetPixelBlue(composite_image,p),q);
1695 SetPixelAlpha(image,GetPixelAlpha(composite_image,p),q);
1696 if (image->colorspace == CMYKColorspace)
1697 SetPixelBlack(image,GetPixelBlack(composite_image,p),q);
1698 p+=GetPixelChannels(composite_image);
1699 q+=GetPixelChannels(image);
1701 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1702 if (sync == MagickFalse)
1704 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1709 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1710 #pragma omp critical (MagickCore_CompositeImage)
1712 proceed=SetImageProgress(image,CompositeImageTag,
1713 (MagickOffsetType) y,image->rows);
1714 if (proceed == MagickFalse)
1718 composite_view=DestroyCacheView(composite_view);
1719 image_view=DestroyCacheView(image_view);
1722 case CopyOpacityCompositeOp:
1723 case ChangeMaskCompositeOp:
1726 Modify destination outside the overlaid region and require an alpha
1727 channel to exist, to add transparency.
1729 if (image->matte == MagickFalse)
1730 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1731 modify_outside_overlay=MagickTrue;
1734 case BlurCompositeOp:
1756 Blur Image dictated by an overlay gradient map: X = red_channel;
1757 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
1759 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1761 if (destination_image == (Image *) NULL)
1762 return(MagickFalse);
1764 Determine the horizontal and vertical maximim blur.
1766 SetGeometryInfo(&geometry_info);
1768 value=GetImageArtifact(composite_image,"compose:args");
1769 if (value != (char *) NULL)
1770 flags=ParseGeometry(value,&geometry_info);
1771 if ((flags & WidthValue) == 0 )
1773 destination_image=DestroyImage(destination_image);
1774 return(MagickFalse);
1776 width=geometry_info.rho;
1777 height=geometry_info.sigma;
1778 blur.x1=geometry_info.rho;
1781 blur.y2=geometry_info.sigma;
1784 if ((flags & HeightValue) == 0)
1786 if ((flags & XValue) != 0 )
1791 angle=DegreesToRadians(geometry_info.xi);
1792 blur.x1=width*cos(angle);
1793 blur.x2=width*sin(angle);
1794 blur.y1=(-height*sin(angle));
1795 blur.y2=height*cos(angle);
1797 if ((flags & YValue) != 0 )
1799 angle_start=DegreesToRadians(geometry_info.xi);
1800 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1803 Blur Image by resampling.
1806 resample_filter=AcquireResampleFilter(image,exception);
1807 SetResampleFilter(resample_filter,CubicFilter,2.0);
1808 destination_view=AcquireCacheView(destination_image);
1809 composite_view=AcquireCacheView(composite_image);
1810 for (y=0; y < (ssize_t) composite_image->rows; y++)
1815 register const Quantum
1824 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1826 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1828 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1829 destination_image->columns,1,exception);
1830 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1832 for (x=0; x < (ssize_t) composite_image->columns; x++)
1834 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1836 p+=GetPixelChannels(composite_image);
1839 if (fabs(angle_range) > MagickEpsilon)
1844 angle=angle_start+angle_range*QuantumScale*
1845 GetPixelBlue(composite_image,p);
1846 blur.x1=width*cos(angle);
1847 blur.x2=width*sin(angle);
1848 blur.y1=(-height*sin(angle));
1849 blur.y2=height*cos(angle);
1851 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
1852 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
1853 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
1854 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
1855 GetPixelGreen(composite_image,p));
1856 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1857 (double) y_offset+y,&pixel);
1858 SetPixelPixelInfo(destination_image,&pixel,q);
1859 p+=GetPixelChannels(composite_image);
1860 q+=GetPixelChannels(destination_image);
1862 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1863 if (sync == MagickFalse)
1866 resample_filter=DestroyResampleFilter(resample_filter);
1867 composite_view=DestroyCacheView(composite_view);
1868 destination_view=DestroyCacheView(destination_view);
1869 composite_image=destination_image;
1872 case DisplaceCompositeOp:
1873 case DistortCompositeOp:
1892 Displace/Distort based on overlay gradient map:
1893 X = red_channel; Y = green_channel;
1894 compose:args = x_scale[,y_scale[,center.x,center.y]]
1896 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1898 if (destination_image == (Image *) NULL)
1899 return(MagickFalse);
1900 SetGeometryInfo(&geometry_info);
1902 value=GetImageArtifact(composite_image,"compose:args");
1903 if (value != (char *) NULL)
1904 flags=ParseGeometry(value,&geometry_info);
1905 if ((flags & (WidthValue|HeightValue)) == 0 )
1907 if ((flags & AspectValue) == 0)
1909 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1911 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1915 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1916 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1921 horizontal_scale=geometry_info.rho;
1922 vertical_scale=geometry_info.sigma;
1923 if ((flags & PercentValue) != 0)
1925 if ((flags & AspectValue) == 0)
1927 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1928 vertical_scale*=(composite_image->rows-1.0)/200.0;
1932 horizontal_scale*=(image->columns-1.0)/200.0;
1933 vertical_scale*=(image->rows-1.0)/200.0;
1936 if ((flags & HeightValue) == 0)
1937 vertical_scale=horizontal_scale;
1940 Determine fixed center point for absolute distortion map
1942 Displace offset relative to a fixed absolute point
1943 Select that point according to +X+Y user inputs.
1944 default = center of overlay image
1945 arg flag '!' = locations/percentage relative to background image
1947 center.x=(MagickRealType) x_offset;
1948 center.y=(MagickRealType) y_offset;
1949 if (compose == DistortCompositeOp)
1951 if ((flags & XValue) == 0)
1952 if ((flags & AspectValue) == 0)
1953 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
1956 center.x=((MagickRealType) image->columns-1)/2.0;
1958 if ((flags & AspectValue) == 0)
1959 center.x=(MagickRealType) x_offset+geometry_info.xi;
1961 center.x=geometry_info.xi;
1962 if ((flags & YValue) == 0)
1963 if ((flags & AspectValue) == 0)
1964 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
1966 center.y=((MagickRealType) image->rows-1)/2.0;
1968 if ((flags & AspectValue) == 0)
1969 center.y=(MagickRealType) y_offset+geometry_info.psi;
1971 center.y=geometry_info.psi;
1974 Shift the pixel offset point as defined by the provided,
1975 displacement/distortion map. -- Like a lens...
1978 image_view=AcquireCacheView(image);
1979 destination_view=AcquireCacheView(destination_image);
1980 composite_view=AcquireCacheView(composite_image);
1981 for (y=0; y < (ssize_t) composite_image->rows; y++)
1986 register const Quantum
1995 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1997 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1999 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
2000 destination_image->columns,1,exception);
2001 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2003 for (x=0; x < (ssize_t) composite_image->columns; x++)
2005 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
2007 p+=GetPixelChannels(composite_image);
2011 Displace the offset.
2013 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
2014 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2015 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
2017 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
2018 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
2019 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
2021 (void) InterpolatePixelInfo(image,image_view,
2022 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2025 Mask with the 'invalid pixel mask' in alpha channel.
2027 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
2028 pixel.alpha)*(1.0-QuantumScale*
2029 GetPixelAlpha(composite_image,p)));
2030 SetPixelPixelInfo(destination_image,&pixel,q);
2031 p+=GetPixelChannels(composite_image);
2032 q+=GetPixelChannels(destination_image);
2034 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
2035 if (sync == MagickFalse)
2038 destination_view=DestroyCacheView(destination_view);
2039 composite_view=DestroyCacheView(composite_view);
2040 image_view=DestroyCacheView(image_view);
2041 composite_image=destination_image;
2044 case DissolveCompositeOp:
2047 Geometry arguments to dissolve factors.
2049 value=GetImageArtifact(composite_image,"compose:args");
2050 if (value != (char *) NULL)
2052 flags=ParseGeometry(value,&geometry_info);
2053 source_dissolve=geometry_info.rho/100.0;
2054 destination_dissolve=1.0;
2055 if ((source_dissolve-MagickEpsilon) < 0.0)
2056 source_dissolve=0.0;
2057 if ((source_dissolve+MagickEpsilon) > 1.0)
2059 destination_dissolve=2.0-source_dissolve;
2060 source_dissolve=1.0;
2062 if ((flags & SigmaValue) != 0)
2063 destination_dissolve=geometry_info.sigma/100.0;
2064 if ((destination_dissolve-MagickEpsilon) < 0.0)
2065 destination_dissolve=0.0;
2066 modify_outside_overlay=MagickTrue;
2067 if ((destination_dissolve+MagickEpsilon) > 1.0 )
2069 destination_dissolve=1.0;
2070 modify_outside_overlay=MagickFalse;
2075 case BlendCompositeOp:
2077 value=GetImageArtifact(composite_image,"compose:args");
2078 if (value != (char *) NULL)
2080 flags=ParseGeometry(value,&geometry_info);
2081 source_dissolve=geometry_info.rho/100.0;
2082 destination_dissolve=1.0-source_dissolve;
2083 if ((flags & SigmaValue) != 0)
2084 destination_dissolve=geometry_info.sigma/100.0;
2085 modify_outside_overlay=MagickTrue;
2086 if ((destination_dissolve+MagickEpsilon) > 1.0)
2087 modify_outside_overlay=MagickFalse;
2091 case MathematicsCompositeOp:
2094 Just collect the values from "compose:args", setting.
2095 Unused values are set to zero automagically.
2097 Arguments are normally a comma separated list, so this probably should
2098 be changed to some 'general comma list' parser, (with a minimum
2101 SetGeometryInfo(&geometry_info);
2102 value=GetImageArtifact(composite_image,"compose:args");
2103 if (value != (char *) NULL)
2104 (void) ParseGeometry(value,&geometry_info);
2107 case ModulateCompositeOp:
2110 Determine the brightness and saturation scale.
2112 value=GetImageArtifact(composite_image,"compose:args");
2113 if (value != (char *) NULL)
2115 flags=ParseGeometry(value,&geometry_info);
2116 percent_brightness=geometry_info.rho;
2117 if ((flags & SigmaValue) != 0)
2118 percent_saturation=geometry_info.sigma;
2122 case ThresholdCompositeOp:
2125 Determine the amount and threshold.
2127 value=GetImageArtifact(composite_image,"compose:args");
2128 if (value != (char *) NULL)
2130 flags=ParseGeometry(value,&geometry_info);
2131 amount=geometry_info.rho;
2132 threshold=geometry_info.sigma;
2133 if ((flags & SigmaValue) == 0)
2136 threshold*=QuantumRange;
2142 value=GetImageArtifact(composite_image,"compose:outside-overlay");
2143 if (value != (const char *) NULL)
2144 modify_outside_overlay=IsMagickTrue(value);
2150 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2151 GetPixelInfo(composite_image,&zero);
2152 image_view=AcquireCacheView(image);
2153 composite_view=AcquireCacheView(composite_image);
2154 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2155 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2157 for (y=0; y < (ssize_t) image->rows; y++)
2172 register const Quantum
2181 if (status == MagickFalse)
2183 if (modify_outside_overlay == MagickFalse)
2187 if ((y-y_offset) >= (ssize_t) composite_image->rows)
2191 If pixels is NULL, y is outside overlay region.
2193 pixels=(Quantum *) NULL;
2195 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
2197 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
2198 composite_image->columns,1,exception);
2199 if (p == (const Quantum *) NULL)
2206 p-=x_offset*GetPixelChannels(composite_image);
2208 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2209 if (q == (Quantum *) NULL)
2219 for (x=0; x < (ssize_t) image->columns; x++)
2221 if (modify_outside_overlay == MagickFalse)
2225 q+=GetPixelChannels(image);
2228 if ((x-x_offset) >= (ssize_t) composite_image->columns)
2231 destination.red=(MagickRealType) GetPixelRed(image,q);
2232 destination.green=(MagickRealType) GetPixelGreen(image,q);
2233 destination.blue=(MagickRealType) GetPixelBlue(image,q);
2234 if (image->colorspace == CMYKColorspace)
2235 destination.black=(MagickRealType) GetPixelBlack(image,q);
2236 if (image->colorspace == CMYKColorspace)
2238 destination.red=(MagickRealType) QuantumRange-destination.red;
2239 destination.green=(MagickRealType) QuantumRange-destination.green;
2240 destination.blue=(MagickRealType) QuantumRange-destination.blue;
2241 destination.black=(MagickRealType) QuantumRange-destination.black;
2243 if (image->matte != MagickFalse)
2244 destination.alpha=(MagickRealType) GetPixelAlpha(image,q);
2246 Handle destination modifications outside overlaid region.
2248 composite=destination;
2249 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2250 ((x-x_offset) >= (ssize_t) composite_image->columns))
2254 case DissolveCompositeOp:
2255 case BlendCompositeOp:
2257 composite.alpha=destination_dissolve*(composite.alpha);
2260 case ClearCompositeOp:
2261 case SrcCompositeOp:
2263 CompositeClear(&destination,&composite);
2267 case SrcInCompositeOp:
2268 case OutCompositeOp:
2269 case SrcOutCompositeOp:
2270 case DstInCompositeOp:
2271 case DstAtopCompositeOp:
2272 case CopyOpacityCompositeOp:
2273 case ChangeMaskCompositeOp:
2275 composite.alpha=(MagickRealType) TransparentAlpha;
2280 (void) GetOneVirtualMagickPixel(composite_image,
2281 GetPixelCacheVirtualMethod(composite_image),x-x_offset,y-
2282 y_offset,&composite,exception);
2286 if (image->colorspace == CMYKColorspace)
2288 composite.red=(MagickRealType) QuantumRange-composite.red;
2289 composite.green=(MagickRealType) QuantumRange-composite.green;
2290 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2291 composite.black=(MagickRealType) QuantumRange-composite.black;
2293 SetPixelRed(image,ClampToQuantum(composite.red),q);
2294 SetPixelGreen(image,ClampToQuantum(composite.green),q);
2295 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2296 if (image->matte != MagickFalse)
2297 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2298 if (image->colorspace == CMYKColorspace)
2299 SetPixelBlack(image,ClampToQuantum(composite.black),q);
2300 q+=GetPixelChannels(image);
2304 Handle normal overlay of source onto destination.
2306 source.red=(MagickRealType) GetPixelRed(composite_image,p);
2307 source.green=(MagickRealType) GetPixelGreen(composite_image,p);
2308 source.blue=(MagickRealType) GetPixelBlue(composite_image,p);
2309 if (composite_image->colorspace == CMYKColorspace)
2310 source.black=(MagickRealType) GetPixelBlack(composite_image,p);
2311 if (composite_image->colorspace == CMYKColorspace)
2313 source.red=(MagickRealType) QuantumRange-source.red;
2314 source.green=(MagickRealType) QuantumRange-source.green;
2315 source.blue=(MagickRealType) QuantumRange-source.blue;
2316 source.black=(MagickRealType) QuantumRange-source.black;
2318 if (composite_image->matte != MagickFalse)
2319 source.alpha=(MagickRealType) GetPixelAlpha(composite_image,p);
2321 Porter-Duff compositions.
2325 case ClearCompositeOp:
2327 CompositeClear(&destination,&composite);
2330 case SrcCompositeOp:
2331 case CopyCompositeOp:
2332 case ReplaceCompositeOp:
2338 case DstCompositeOp:
2340 case OverCompositeOp:
2341 case SrcOverCompositeOp:
2343 CompositePixelInfoOver(&source,source.alpha,&destination,
2344 destination.alpha,&composite);
2347 case DstOverCompositeOp:
2349 CompositePixelInfoOver(&destination,destination.alpha,&source,
2350 source.alpha,&composite);
2353 case SrcInCompositeOp:
2356 CompositeIn(&source,&destination,&composite);
2359 case DstInCompositeOp:
2361 CompositeIn(&destination,&source,&composite);
2364 case OutCompositeOp:
2365 case SrcOutCompositeOp:
2367 CompositeOut(&source,&destination,&composite);
2370 case DstOutCompositeOp:
2372 CompositeOut(&destination,&source,&composite);
2375 case AtopCompositeOp:
2376 case SrcAtopCompositeOp:
2378 CompositeAtop(&source,&destination,&composite);
2381 case DstAtopCompositeOp:
2383 CompositeAtop(&destination,&source,&composite);
2386 case XorCompositeOp:
2388 CompositeXor(&source,&destination,&composite);
2391 case PlusCompositeOp:
2393 CompositePlus(image,&source,&destination,&composite);
2396 case MinusDstCompositeOp:
2398 CompositeMinus(image,&source,&destination,&composite);
2401 case MinusSrcCompositeOp:
2403 CompositeMinus(image,&destination,&source,&composite);
2406 case ModulusAddCompositeOp:
2408 CompositeModulusAdd(image,&source,&destination,&composite);
2411 case ModulusSubtractCompositeOp:
2413 CompositeModulusSubtract(image,&source,&destination,&composite);
2416 case DifferenceCompositeOp:
2418 CompositeDifference(image,&source,&destination,&composite);
2421 case ExclusionCompositeOp:
2423 CompositeExclusion(image,&source,&destination,&composite);
2426 case MultiplyCompositeOp:
2428 CompositeMultiply(image,&source,&destination,&composite);
2431 case ScreenCompositeOp:
2433 CompositeScreen(image,&source,&destination,&composite);
2436 case DivideDstCompositeOp:
2438 CompositeDivide(image,&source,&destination,&composite);
2441 case DivideSrcCompositeOp:
2443 CompositeDivide(image,&destination,&source,&composite);
2446 case DarkenCompositeOp:
2448 CompositeDarken(image,&source,&destination,&composite);
2451 case LightenCompositeOp:
2453 CompositeLighten(image,&source,&destination,&composite);
2456 case DarkenIntensityCompositeOp:
2458 CompositeDarkenIntensity(image,&source,&destination,&composite);
2461 case LightenIntensityCompositeOp:
2463 CompositeLightenIntensity(image,&source,&destination,&composite);
2466 case MathematicsCompositeOp:
2468 CompositeMathematics(image,&source,&destination,&geometry_info,
2472 case ColorDodgeCompositeOp:
2474 CompositeColorDodge(&source,&destination,&composite);
2477 case ColorBurnCompositeOp:
2479 CompositeColorBurn(&source,&destination,&composite);
2482 case LinearDodgeCompositeOp:
2484 CompositeLinearDodge(&source,&destination,&composite);
2487 case LinearBurnCompositeOp:
2489 CompositeLinearBurn(&source,&destination,&composite);
2492 case HardLightCompositeOp:
2494 CompositeHardLight(&source,&destination,&composite);
2497 case OverlayCompositeOp:
2499 CompositeHardLight(&destination,&source,&composite);
2502 case SoftLightCompositeOp:
2504 CompositeSoftLight(&source,&destination,&composite);
2507 case LinearLightCompositeOp:
2509 CompositeLinearLight(&source,&destination,&composite);
2512 case PegtopLightCompositeOp:
2514 CompositePegtopLight(&source,&destination,&composite);
2517 case VividLightCompositeOp:
2519 CompositeVividLight(&source,&destination,&composite);
2522 case PinLightCompositeOp:
2524 CompositePinLight(&source,&destination,&composite);
2527 case ChangeMaskCompositeOp:
2529 if ((composite.alpha > ((MagickRealType) QuantumRange/2.0)) ||
2530 (IsFuzzyEquivalencePixelInfo(&source,&destination) != MagickFalse))
2531 composite.alpha=(MagickRealType) TransparentAlpha;
2533 composite.alpha=(MagickRealType) OpaqueAlpha;
2536 case BumpmapCompositeOp:
2538 if (source.alpha == TransparentAlpha)
2540 CompositeBumpmap(&source,&destination,&composite);
2543 case DissolveCompositeOp:
2545 CompositePixelInfoOver(&source,source_dissolve*source.alpha,
2546 &destination,(MagickRealType) (destination_dissolve*
2547 destination.alpha),&composite);
2550 case BlendCompositeOp:
2552 CompositePixelInfoBlend(&source,source_dissolve,&destination,
2553 destination_dissolve,&composite);
2556 case ThresholdCompositeOp:
2558 CompositeThreshold(&source,&destination,threshold,amount,&composite);
2561 case ModulateCompositeOp:
2566 if (source.alpha == TransparentAlpha)
2568 offset=(ssize_t) (GetPixelInfoIntensity(&source)-midpoint);
2571 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2572 &saturation,&brightness);
2573 brightness+=(0.01*percent_brightness*offset)/midpoint;
2574 saturation*=0.01*percent_saturation;
2575 HSBComposite(hue,saturation,brightness,&composite.red,
2576 &composite.green,&composite.blue);
2579 case HueCompositeOp:
2581 if (source.alpha == TransparentAlpha)
2583 if (destination.alpha == TransparentAlpha)
2588 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2589 &saturation,&brightness);
2590 CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
2591 HSBComposite(hue,saturation,brightness,&composite.red,
2592 &composite.green,&composite.blue);
2593 if (source.alpha < destination.alpha)
2594 composite.alpha=source.alpha;
2597 case SaturateCompositeOp:
2599 if (source.alpha == TransparentAlpha)
2601 if (destination.alpha == TransparentAlpha)
2606 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2607 &saturation,&brightness);
2608 CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
2610 HSBComposite(hue,saturation,brightness,&composite.red,
2611 &composite.green,&composite.blue);
2612 if (source.alpha < destination.alpha)
2613 composite.alpha=source.alpha;
2616 case LuminizeCompositeOp:
2618 if (source.alpha == TransparentAlpha)
2620 if (destination.alpha == TransparentAlpha)
2625 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2626 &saturation,&brightness);
2627 CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
2629 HSBComposite(hue,saturation,brightness,&composite.red,
2630 &composite.green,&composite.blue);
2631 if (source.alpha < destination.alpha)
2632 composite.alpha=source.alpha;
2635 case ColorizeCompositeOp:
2637 if (source.alpha == TransparentAlpha)
2639 if (destination.alpha == TransparentAlpha)
2644 CompositeHSB(destination.red,destination.green,destination.blue,&sans,
2646 CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
2648 HSBComposite(hue,saturation,brightness,&composite.red,
2649 &composite.green,&composite.blue);
2650 if (source.alpha < destination.alpha)
2651 composite.alpha=source.alpha;
2654 case CopyRedCompositeOp:
2655 case CopyCyanCompositeOp:
2657 composite.red=source.red;
2660 case CopyGreenCompositeOp:
2661 case CopyMagentaCompositeOp:
2663 composite.green=source.green;
2666 case CopyBlueCompositeOp:
2667 case CopyYellowCompositeOp:
2669 composite.blue=source.blue;
2672 case CopyOpacityCompositeOp:
2674 if (source.matte == MagickFalse)
2676 composite.alpha=(MagickRealType) GetPixelInfoIntensity(&source);
2679 composite.alpha=source.alpha;
2682 case CopyBlackCompositeOp:
2684 if (source.colorspace != CMYKColorspace)
2685 ConvertRGBToCMYK(&source);
2686 composite.black=source.black;
2689 case BlurCompositeOp:
2690 case DisplaceCompositeOp:
2691 case DistortCompositeOp:
2699 if (image->colorspace == CMYKColorspace)
2701 composite.red=(MagickRealType) QuantumRange-composite.red;
2702 composite.green=(MagickRealType) QuantumRange-composite.green;
2703 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2704 composite.black=(MagickRealType) QuantumRange-composite.black;
2706 SetPixelRed(image,ClampToQuantum(composite.red),q);
2707 SetPixelGreen(image,ClampToQuantum(composite.green),q);
2708 SetPixelBlue(image,ClampToQuantum(composite.blue),q);
2709 if (image->colorspace == CMYKColorspace)
2710 SetPixelBlack(image,ClampToQuantum(composite.black),q);
2711 SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
2712 p+=GetPixelChannels(composite_image);
2713 if (p >= (pixels+composite_image->columns*GetPixelChannels(composite_image)))
2715 q+=GetPixelChannels(image);
2717 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2719 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2724 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2725 #pragma omp critical (MagickCore_CompositeImage)
2727 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2729 if (proceed == MagickFalse)
2733 composite_view=DestroyCacheView(composite_view);
2734 image_view=DestroyCacheView(image_view);
2735 if (destination_image != (Image * ) NULL)
2736 destination_image=DestroyImage(destination_image);
2741 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2745 % T e x t u r e I m a g e %
2749 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2751 % TextureImage() repeatedly tiles the texture image across and down the image
2754 % The format of the TextureImage method is:
2756 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2757 % ExceptionInfo *exception)
2759 % A description of each parameter follows:
2761 % o image: the image.
2763 % o texture: This image is the texture to layer on the background.
2766 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2767 ExceptionInfo *exception)
2769 #define TextureImageTag "Texture/Image"
2781 assert(image != (Image *) NULL);
2782 if (image->debug != MagickFalse)
2783 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2784 assert(image->signature == MagickSignature);
2785 if (texture == (const Image *) NULL)
2786 return(MagickFalse);
2787 (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
2788 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2789 return(MagickFalse);
2791 if ((image->compose != CopyCompositeOp) &&
2792 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2793 (texture->matte != MagickFalse)))
2796 Tile texture onto the image background.
2798 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2799 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2801 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
2806 if (status == MagickFalse)
2808 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2813 thread_status=CompositeImage(image,image->compose,texture,x+
2814 texture->tile_offset.x,y+texture->tile_offset.y,exception);
2815 if (thread_status == MagickFalse)
2817 status=thread_status;
2821 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2826 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2827 #pragma omp critical (MagickCore_TextureImage)
2829 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2831 if (proceed == MagickFalse)
2835 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2836 image->rows,image->rows);
2840 Tile texture onto the image background (optimized).
2843 image_view=AcquireCacheView(image);
2844 texture_view=AcquireCacheView(texture);
2845 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2846 #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
2848 for (y=0; y < (ssize_t) image->rows; y++)
2853 register const Quantum
2866 if (status == MagickFalse)
2868 pixels=GetCacheViewVirtualPixels(texture_view,texture->tile_offset.x,(y+
2869 texture->tile_offset.y) % texture->rows,texture->columns,1,exception);
2870 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2872 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2877 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
2883 width=texture->columns;
2884 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2885 width=image->columns-x;
2886 for (i=0; i < (ssize_t) width; i++)
2888 SetPixelRed(image,GetPixelRed(texture,p),q);
2889 SetPixelGreen(image,GetPixelGreen(texture,p),q);
2890 SetPixelBlue(image,GetPixelBlue(texture,p),q);
2891 SetPixelAlpha(image,GetPixelAlpha(texture,p),q);
2892 if ((image->colorspace == CMYKColorspace) &&
2893 (texture->colorspace == CMYKColorspace))
2894 SetPixelBlack(image,GetPixelBlack(texture,p),q);
2895 p+=GetPixelChannels(texture);
2896 q+=GetPixelChannels(image);
2899 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2900 if (sync == MagickFalse)
2902 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2907 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2908 #pragma omp critical (MagickCore_TextureImage)
2910 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2912 if (proceed == MagickFalse)
2916 texture_view=DestroyCacheView(texture_view);
2917 image_view=DestroyCacheView(image_view);