blob: 7891fdd67ad8e5661484f3fe3531bb5d86b772bc [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
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 %
11% %
12% %
13% MagickCore Image Composite Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
20% Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
21% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
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. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/artifact.h"
45#include "magick/cache-view.h"
46#include "magick/client.h"
47#include "magick/color.h"
48#include "magick/color-private.h"
49#include "magick/colorspace.h"
50#include "magick/colorspace-private.h"
51#include "magick/composite.h"
52#include "magick/composite-private.h"
53#include "magick/constitute.h"
54#include "magick/draw.h"
55#include "magick/fx.h"
56#include "magick/gem.h"
57#include "magick/geometry.h"
58#include "magick/image.h"
59#include "magick/image-private.h"
60#include "magick/list.h"
61#include "magick/log.h"
62#include "magick/monitor.h"
63#include "magick/monitor-private.h"
64#include "magick/memory_.h"
65#include "magick/option.h"
66#include "magick/pixel-private.h"
67#include "magick/property.h"
68#include "magick/quantum.h"
69#include "magick/resample.h"
70#include "magick/resource_.h"
71#include "magick/string_.h"
72#include "magick/utility.h"
73#include "magick/version.h"
74
75/*
76%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77% %
78% %
79% %
80% C o m p o s i t e I m a g e C h a n n e l %
81% %
82% %
83% %
84%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85%
86% CompositeImageChannel() returns the second image composited onto the first
87% at the specified offset, using the specified composite method.
88%
89% The format of the CompositeImageChannel method is:
90%
91% MagickBooleanType CompositeImage(Image *image,
92% const CompositeOperator compose,Image *composite_image,
93% const long x_offset,const long y_offset)
94% MagickBooleanType CompositeImageChannel(Image *image,
95% const ChannelType channel,const CompositeOperator compose,
96% Image *composite_image,const long x_offset,const long y_offset)
97%
98% A description of each parameter follows:
99%
100% o image: the destination image, modified by he composition
101%
102% o channel: the channel.
103%
104% o compose: This operator affects how the composite is applied to
105% the image. The operators and how they are utilized are listed here
106% http://www.w3.org/TR/SVG12/#compositing.
107%
108% o composite_image: the composite (source) image.
109%
110% o x_offset: the column offset of the composited image.
111%
112% o y_offset: the row offset of the composited image.
113%
114% Extra Controls from Image meta-data in 'composite_image' (artifacts)
115%
116% o "compose:args"
117% A string containing extra numerical arguments for specific compose
118% methods, generally expressed as a 'geometry' or a comma separated list
119% of numbers.
120%
121% Compose methods needing such arguments include "BlendCompositeOp" and
122% "DisplaceCompositeOp".
123%
124% o "compose:outside-overlay"
125% Modify how the composition is to effect areas not directly covered
126% by the 'composite_image' at the offset given. Normally this is
127% dependant on the 'compose' method, especially Duff-Porter methods.
128%
129% If set to "false" then disable all normal handling of pixels not
130% covered by the composite_image. Typically used for repeated tiling
131% of the composite_image by the calling API.
132%
133% Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
134%
135*/
136
137static inline double MagickMin(const double x,const double y)
138{
139 if (x < y)
140 return(x);
141 return(y);
142}
143static inline double MagickMax(const double x,const double y)
144{
145 if (x > y)
146 return(x);
147 return(y);
148}
149
150/*
151** Programmers notes on SVG specification.
152**
153** A Composition is defined by...
154** Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
155** Blending areas : X = 1 for area of overlap ie: f(Sc,Dc)
156** Y = 1 for source preserved
157** Z = 1 for destination preserved
158**
159** Conversion to transparency (then optimized)
160** Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
161** Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
162**
163** Where...
164** Sca = Sc*Sa normalized Source color divided by Source alpha
165** Dca = Dc*Da normalized Dest color divided by Dest alpha
166** Dc' = Dca'/Da' the desired color value for this channel.
167**
168** Da' in in the follow formula as 'gamma' The resulting alpla value.
169**
170**
171** Most functions use a blending mode of over (X=1,Y=1,Z=1)
172** this results in the following optimizations...
173** gamma = Sa+Da-Sa*Da;
174** gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
175** opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
176*/
177
178static inline MagickRealType Add(const MagickRealType p,const MagickRealType q)
179{
180 MagickRealType
181 pixel;
182
183 pixel=p+q;
184 if (pixel > QuantumRange)
185 pixel-=(QuantumRange+1.0);
186 return(pixel);
187}
188
189static inline void CompositeAdd(const MagickPixelPacket *p,
190 const MagickRealType alpha,const MagickPixelPacket *q,
191 const MagickRealType beta,MagickPixelPacket *composite)
192{
193 composite->red=Add(p->red,q->red);
194 composite->green=Add(p->green,q->green);
195 composite->blue=Add(p->blue,q->blue);
196 composite->opacity=Add(alpha,beta);
197 if (q->colorspace == CMYKColorspace)
198 composite->index=Add(p->index,q->index);
199}
200
201static inline MagickRealType Atop(const MagickRealType p,
202 const MagickRealType Sa,const MagickRealType q,
203 const MagickRealType magick_unused(Da))
204{
205 return(p*Sa+q*(1.0-Sa)); /* Da optimized out, Da/gamma => 1.0 */
206}
207
208static inline void CompositeAtop(const MagickPixelPacket *p,
209 const MagickRealType alpha,const MagickPixelPacket *q,
210 const MagickRealType beta,MagickPixelPacket *composite)
211{
212 MagickRealType
213 Sa;
214
215 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
216 composite->opacity=beta; /* optimized 1.0-Gamma */
217 composite->red=Atop(p->red,Sa,q->red,1.0);
218 composite->green=Atop(p->green,Sa,q->green,1.0);
219 composite->blue=Atop(p->blue,Sa,q->blue,1.0);
220 if (q->colorspace == CMYKColorspace)
221 composite->index=Atop(p->index,Sa,q->index,1.0);
222}
223
224/*
225 What is this Composition method for, can't find any specification!
226 WARNING this is not doing correct 'over' blend handling (Anthony Thyssen).
227*/
228static inline void CompositeBumpmap(const MagickPixelPacket *p,
229 const MagickRealType magick_unused(alpha),const MagickPixelPacket *q,
230 const MagickRealType magick_unused(beta),MagickPixelPacket *composite)
231{
232 MagickRealType
233 intensity;
234
235 intensity=MagickPixelIntensity(p);
236 composite->red=QuantumScale*intensity*q->red;
237 composite->green=QuantumScale*intensity*q->green;
238 composite->blue=QuantumScale*intensity*q->blue;
239 composite->opacity=(MagickRealType) QuantumScale*intensity*p->opacity;
240 if (q->colorspace == CMYKColorspace)
241 composite->index=QuantumScale*intensity*q->index;
242}
243
244static inline void CompositeClear(const MagickPixelPacket *q,
245 MagickPixelPacket *composite)
246{
247 composite->opacity=(MagickRealType) TransparentOpacity;
248 composite->red=0.0;
249 composite->green=0.0;
250 composite->blue=0.0;
251 if (q->colorspace == CMYKColorspace)
252 composite->index=0.0;
253}
254
255static MagickRealType ColorBurn(const MagickRealType Sca,
256 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
257{
258#if 0
259 /*
260 Oct 2004 SVG specification.
261 */
262 if (Sca*Da + Dca*Sa <= Sa*Da)
263 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
264 return(Sa*(Sca*Da+Dca*Sa-Sa*Da)/Sca + Sca*(1.0-Da) + Dca*(1.0-Sa));
265#else
266 /*
267 March 2009 SVG specification.
268 */
269 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
270 return(Sa*Da+Dca*(1.0-Sa));
271 if (Sca < MagickEpsilon)
272 return(Dca*(1.0-Sa));
273 return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
274#endif
275}
276
277static inline void CompositeColorBurn(const MagickPixelPacket *p,
278 const MagickRealType alpha,const MagickPixelPacket *q,
279 const MagickRealType beta,MagickPixelPacket *composite)
280{
281 MagickRealType
282 Da,
283 gamma,
284 Sa;
285
286 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
287 Da=1.0-QuantumScale*beta;
288 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
289 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
290 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
291 composite->red=gamma*ColorBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
292 q->red*Da,Da);
293 composite->green=gamma*ColorBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
294 q->green*Da,Da);
295 composite->blue=gamma*ColorBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
296 q->blue*Da,Da);
297 if (q->colorspace == CMYKColorspace)
298 composite->index=gamma*ColorBurn(QuantumScale*p->index*Sa,Sa,QuantumScale*
299 q->index*Da,Da);
300}
301
302
303static MagickRealType ColorDodge(const MagickRealType Sca,
304 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
305{
306#if 0
307 /*
308 Oct 2004 SVG specification.
309 */
310 if ((Sca*Da+Dca*Sa) >= Sa*Da)
311 return( Sa*Da + Sca*(1.0-Da) + Dca*(1.0-Sa) );
312 return( Dca*Sa*Sa/(Sa-Sca) + Sca*(1.0-Da) + Dca*(1.0-Sa) );
313#endif
314#if 0
315 /*
316 New specification, March 2009 SVG specification. This specification was
317 also wrong of non-overlap cases.
318 */
319 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
320 return(Sca*(1.0-Da));
321 if (fabs(Sca-Sa) < MagickEpsilon)
322 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
323 return(Sa*MagickMin(Da,Dca*Sa/(Sa-Sca)));
324#endif
325 /*
326 Working from first principles using the original formula:
327
328 f(Sc,Dc) = Dc/(1-Sc)
329
330 This works correctly! Looks like the 2004 model was right but just
331 required a extra condition for correct handling.
332 */
333 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
334 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
335 if (fabs(Sca-Sa) < MagickEpsilon)
336 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
337 return(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
338}
339
340static inline void CompositeColorDodge(const MagickPixelPacket *p,
341 const MagickRealType alpha,const MagickPixelPacket *q,
342 const MagickRealType beta,MagickPixelPacket *composite)
343{
344 MagickRealType
345 Da,
346 gamma,
347 Sa;
348
349 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
350 Da=1.0-QuantumScale*beta;
351 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
352 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
353 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
354 composite->red=gamma*ColorDodge(QuantumScale*p->red*Sa,Sa,QuantumScale*
355 q->red*Da,Da);
356 composite->green=gamma*ColorDodge(QuantumScale*p->green*Sa,Sa,QuantumScale*
357 q->green*Da,Da);
358 composite->blue=gamma*ColorDodge(QuantumScale*p->blue*Sa,Sa,QuantumScale*
359 q->blue*Da,Da);
360 if (q->colorspace == CMYKColorspace)
361 composite->index=gamma*ColorDodge(QuantumScale*p->index*Sa,Sa,QuantumScale*
362 q->index*Da,Da);
363}
364
365static inline MagickRealType Darken(const MagickRealType p,
366 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
367{
368 if (p < q)
369 return(MagickOver_(p,alpha,q,beta)); /* src-over */
370 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
371}
372
373static inline void CompositeDarken(const MagickPixelPacket *p,
374 const MagickRealType alpha,const MagickPixelPacket *q,
375 const MagickRealType beta,MagickPixelPacket *composite)
376{
377 MagickRealType
378 gamma;
379
380 gamma=1.0-QuantumScale*QuantumScale*alpha*beta;
381 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
382 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
383 composite->red=gamma*Darken(p->red,alpha,q->red,beta);
384 composite->green=gamma*Darken(p->green,alpha,q->green,beta);
385 composite->blue=gamma*Darken(p->blue,alpha,q->blue,beta);
386 if (q->colorspace == CMYKColorspace)
387 composite->index=gamma*Darken(p->index,alpha,q->index,beta);
388}
389
390static inline MagickRealType Difference(const MagickRealType p,
391 const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
392{
393 /*
394 Optimized by Multipling by QuantumRange (taken from gamma).
395 */
396 return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q));
397}
398
399static inline void CompositeDifference(const MagickPixelPacket *p,
400 const MagickRealType alpha,const MagickPixelPacket *q,
401 const MagickRealType beta,MagickPixelPacket *composite)
402{
403 MagickRealType
404 Da,
405 gamma,
406 Sa;
407
408 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
409 Da=1.0-QuantumScale*beta;
410 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
411 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
412 /*
413 Values not normalized as an optimization.
414 */
415 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
416 composite->red=gamma*Difference(p->red,Sa,q->red,Da);
417 composite->green=gamma*Difference(p->green,Sa,q->green,Da);
418 composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da);
419 if (q->colorspace == CMYKColorspace)
420 composite->index=gamma*Difference(p->index,Sa,q->index,Da);
421}
422
423static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
424 const MagickRealType Dca,const MagickRealType Da)
425{
426 /*
427 Divide:
428
429 f(Sc,Dc) = Sc/Dc
430
431 But with appropriate handling for special case of Dc == 0 specifically
432 f(Black,Black) = Black and f(non-Black,Black) = White. It is however
433 also important to correctly do 'over' alpha blending which is why it
434 becomes so complex looking.
435 */
436 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
437 return(Sca*(1.0-Da)+Dca*(1.0-Sa));
438 if (fabs(Dca) < MagickEpsilon)
439 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
440 return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
441}
442
443static inline void CompositeDivide(const MagickPixelPacket *p,
444 const MagickRealType alpha,const MagickPixelPacket *q,
445 const MagickRealType beta,MagickPixelPacket *composite)
446{
447 MagickRealType
448 Da,
449 gamma,
450 Sa;
451
452 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
453 Da=1.0-QuantumScale*beta;
454 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
455 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
456 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
457 composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale*
458 q->red*Da,Da);
459 composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale*
460 q->green*Da,Da);
461 composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale*
462 q->blue*Da,Da);
463 if (q->colorspace == CMYKColorspace)
464 composite->index=gamma*Divide(QuantumScale*p->index*Sa,Sa,QuantumScale*
465 q->index*Da,Da);
466}
467
468static MagickRealType Exclusion(const MagickRealType Sca,
469 const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
470{
471 return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
472}
473
474static inline void CompositeExclusion(const MagickPixelPacket *p,
475 const MagickRealType alpha,const MagickPixelPacket *q,
476 const MagickRealType beta,MagickPixelPacket *composite)
477{
478 MagickRealType
479 gamma,
480 Sa,
481 Da;
482
483 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
484 Da=1.0-QuantumScale*beta;
485 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
486 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
487 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
488 composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale*
489 q->red*Da,Da);
490 composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale*
491 q->green*Da,Da);
492 composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale*
493 q->blue*Da,Da);
494 if (q->colorspace == CMYKColorspace)
495 composite->index=gamma*Exclusion(QuantumScale*p->index*Sa,Sa,QuantumScale*
496 q->index*Da,Da);
497}
498
499static MagickRealType HardLight(const MagickRealType Sca,
500 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
501{
502 if ((2.0*Sca) < Sa)
503 return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
504 return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
505}
506
507static inline void CompositeHardLight(const MagickPixelPacket *p,
508 const MagickRealType alpha,const MagickPixelPacket *q,
509 const MagickRealType beta,MagickPixelPacket *composite)
510{
511 MagickRealType
512 Da,
513 gamma,
514 Sa;
515
516 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
517 Da=1.0-QuantumScale*beta;
518 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
519 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
520 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
521 composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
522 q->red*Da,Da);
523 composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
524 q->green*Da,Da);
525 composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
526 q->blue*Da,Da);
527 if (q->colorspace == CMYKColorspace)
528 composite->index=gamma*HardLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
529 q->index*Da,Da);
530}
531
532
533static void CompositeHSB(const MagickRealType red,const MagickRealType green,
534 const MagickRealType blue,double *hue,double *saturation,double *brightness)
535{
536 MagickRealType
537 delta,
538 max,
539 min;
540
541 /*
542 Convert RGB to HSB colorspace.
543 */
544 assert(hue != (double *) NULL);
545 assert(saturation != (double *) NULL);
546 assert(brightness != (double *) NULL);
547 max=(red > green ? red : green);
548 if (blue > max)
549 max=blue;
550 min=(red < green ? red : green);
551 if (blue < min)
552 min=blue;
553 *hue=0.0;
554 *saturation=0.0;
555 *brightness=(double) (QuantumScale*max);
556 if (max == 0.0)
557 return;
558 *saturation=(double) (1.0-min/max);
559 delta=max-min;
560 if (delta == 0.0)
561 return;
562 if (red == max)
563 *hue=(double) ((green-blue)/delta);
564 else
565 if (green == max)
566 *hue=(double) (2.0+(blue-red)/delta);
567 else
568 if (blue == max)
569 *hue=(double) (4.0+(red-green)/delta);
570 *hue/=6.0;
571 if (*hue < 0.0)
572 *hue+=1.0;
573}
574
575static inline MagickRealType In(const MagickRealType p,
576 const MagickRealType alpha,const MagickRealType magick_unused(q),
577 const MagickRealType beta)
578{
579 return((1.0-QuantumScale*alpha)*p*(1.0-QuantumScale*beta));
580}
581
582static inline void CompositeIn(const MagickPixelPacket *p,
583 const MagickRealType alpha,const MagickPixelPacket *q,
584 const MagickRealType beta,MagickPixelPacket *composite)
585{
586 MagickRealType
587 gamma;
588
589 gamma=(1.0-QuantumScale*alpha)*(1.0-QuantumScale*beta);
590 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
591 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
592 composite->red=gamma*In(p->red,alpha,q->red,beta);
593 composite->green=gamma*In(p->green,alpha,q->green,beta);
594 composite->blue=gamma*In(p->blue,alpha,q->blue,beta);
595 if (q->colorspace == CMYKColorspace)
596 composite->index=gamma*In(p->index,alpha,q->index,beta);
597}
598
599static inline MagickRealType Lighten(const MagickRealType p,
600 const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
601{
602 if (p > q)
603 return(MagickOver_(p,alpha,q,beta)); /* src-over */
604 return(MagickOver_(q,beta,p,alpha)); /* dst-over */
605}
606
607static inline void CompositeLighten(const MagickPixelPacket *p,
608 const MagickRealType alpha,const MagickPixelPacket *q,
609 const MagickRealType beta,MagickPixelPacket *composite)
610{
611 MagickRealType
612 gamma;
613
614 composite->opacity=QuantumScale*alpha*beta; /* optimized 1-gamma */
615 gamma=1.0-QuantumScale*composite->opacity;
616 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
617 composite->red=gamma*Lighten(p->red,alpha,q->red,beta);
618 composite->green=gamma*Lighten(p->green,alpha,q->green,beta);
619 composite->blue=gamma*Lighten(p->blue,alpha,q->blue,beta);
620 if (q->colorspace == CMYKColorspace)
621 composite->index=gamma*Lighten(p->index,alpha,q->index,beta);
622}
623
624static inline void CompositeLinearDodge(const MagickPixelPacket *p,
625 const MagickRealType alpha,const MagickPixelPacket *q,
626 const MagickRealType beta,MagickPixelPacket *composite)
627{
628 MagickRealType
629 Da,
630 gamma,
631 Sa;
632
633 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
634 Da=1.0-QuantumScale*beta;
635 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
636 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
637 /*
638 Operation performed directly - not need for sub-routine.
639 */
640 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
641 composite->red=gamma*(p->red*Sa+q->red*Da);
642 composite->green=gamma*(p->green*Sa+q->green*Da);
643 composite->blue=gamma*(p->blue*Sa+q->blue*Da);
644 if (q->colorspace == CMYKColorspace)
645 composite->index=gamma*(p->index*Sa+q->index*Da);
646}
647
648
649static inline MagickRealType LinearBurn(const MagickRealType Sca,
650 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
651{
652 /*
653 LinearLight: as defined by Abode Photoshop, according to
654 http://www.simplefilter.de/en/basics/mixmods.html is:
655
656 f(Sc,Dc) = Dc + Sc - 1
657 */
658 return(Sca+Dca-Sa*Da);
659}
660
661static inline void CompositeLinearBurn(const MagickPixelPacket *p,
662 const MagickRealType alpha,const MagickPixelPacket *q,
663 const MagickRealType beta,MagickPixelPacket *composite)
664{
665 MagickRealType
666 Da,
667 gamma,
668 Sa;
669
670 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
671 Da=1.0-QuantumScale*beta;
672 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
673 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
674 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
675 composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
676 q->red*Da,Da);
677 composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
678 q->green*Da,Da);
679 composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
680 q->blue*Da,Da);
681 if (q->colorspace == CMYKColorspace)
682 composite->index=gamma*LinearBurn(QuantumScale*p->index*Sa,Sa,QuantumScale*
683 q->index*Da,Da);
684}
685
686
687static inline MagickRealType LinearLight(const MagickRealType Sca,
688 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
689{
690#if 0
691 /*
692 Previous formula, only valid for fully-opaque images.
693 */
694 return(Dca+2*Sca-1.0);
695#else
696 /*
697 LinearLight: as defined by Abode Photoshop, according to
698 http://www.simplefilter.de/en/basics/mixmods.html is:
699
700 f(Sc,Dc) = Dc + 2*Sc - 1
701 */
702 return((Sca-Sa)*Da+Sca+Dca);
703#endif
704}
705
706static inline void CompositeLinearLight(const MagickPixelPacket *p,
707 const MagickRealType alpha,const MagickPixelPacket *q,
708 const MagickRealType beta,MagickPixelPacket *composite)
709{
710 MagickRealType
711 Da,
712 gamma,
713 Sa;
714
715 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
716 Da=1.0-QuantumScale*beta;
717 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
718 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
719 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
720 composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
721 q->red*Da,Da);
722 composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
723 q->green*Da,Da);
724 composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
725 q->blue*Da,Da);
726 if (q->colorspace == CMYKColorspace)
727 composite->index=gamma*LinearLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
728 q->index*Da,Da);
729}
730
731static inline MagickRealType Mathematics(const MagickRealType Sca,
732 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
733 const GeometryInfo *geometry_info)
734{
735 /*
736 'Mathematics' a free form user control mathematical composition is defined
737 as...
738
739 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
740
741 Where the arguments A,B,C,D are (currently) passed to composite as
742 a command separated 'geometry' string in "compose:args" image artifact.
743
744 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
745
746 Applying the SVG transparency formula (see above), we get...
747
748 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
749
750 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
751 Dca*(1.0-Sa)
752 */
753 return(geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
754 geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
755 Dca*(1.0-Sa));
756}
757
758static inline void CompositeMathematics(const MagickPixelPacket *p,
759 const MagickPixelPacket *q,const GeometryInfo *args,
760 MagickPixelPacket *composite)
761{
762 MagickRealType
763 Da,
764 gamma,
765 Sa;
766
767 Sa=1.0-QuantumScale*p->opacity;
768 Da=1.0-QuantumScale*q->opacity;
769 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
770 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
771 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
772 composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
773 q->red*Da,Da,args);
774 composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,QuantumScale*
775 q->green*Da,Da,args);
776 composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
777 q->blue*Da,Da,args);
778 if (q->colorspace == CMYKColorspace)
779 composite->index=gamma*Mathematics(QuantumScale*p->index*Sa,Sa,QuantumScale*
780 q->index*Da,Da,args);
781}
782
783static inline MagickRealType Minus(const MagickRealType Sca,
784 const MagickRealType Dca)
785{
786 return(Sca-Dca);
787}
788
789static inline void CompositeMinus(const MagickPixelPacket *p,
790 const MagickRealType alpha,const MagickPixelPacket *q,
791 const MagickRealType beta,MagickPixelPacket *composite)
792{
793 MagickRealType
794 Da,
795 gamma,
796 Sa;
797
798 Sa=1.0-QuantumScale*alpha;
799 Da=1.0-QuantumScale*beta;
800 gamma=RoundToUnity(Sa-Da); /* is this correct? - I do not think so! */
801 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
802 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
803 composite->red=gamma*Minus(p->red*Sa,q->red*Da);
804 composite->green=gamma*Minus(p->green*Sa,q->green*Da);
805 composite->blue=gamma*Minus(p->blue*Sa,q->blue*Da);
806 if (q->colorspace == CMYKColorspace)
807 composite->index=gamma*Minus(p->index*Sa,q->index*Da);
808}
809
810static inline MagickRealType Multiply(const MagickRealType Sca,
811 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
812{
813 return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
814}
815
816static inline void CompositeMultiply(const MagickPixelPacket *p,
817 const MagickRealType alpha,const MagickPixelPacket *q,
818 const MagickRealType beta,MagickPixelPacket *composite)
819{
820 MagickRealType
821 Da,
822 gamma,
823 Sa;
824
825 Sa=1.0-QuantumScale*alpha;
826 Da=1.0-QuantumScale*beta;
827 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
828 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
829 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
830 composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
831 q->red*Da,Da);
832 composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
833 q->green*Da,Da);
834 composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
835 q->blue*Da,Da);
836 if (q->colorspace == CMYKColorspace)
837 composite->index=gamma*Multiply(QuantumScale*p->index*Sa,Sa,QuantumScale*
838 q->index*Da,Da);
839}
840
841static inline MagickRealType Out(const MagickRealType p,
842 const MagickRealType alpha,const MagickRealType magick_unused(q),
843 const MagickRealType beta)
844{
845 return((1.0-QuantumScale*alpha)*p*QuantumScale*beta);
846}
847
848static inline void CompositeOut(const MagickPixelPacket *p,
849 const MagickRealType alpha,const MagickPixelPacket *q,
850 const MagickRealType beta,MagickPixelPacket *composite)
851{
852 MagickRealType
853 gamma;
854
855 gamma=(1.0-QuantumScale*alpha)*QuantumScale*beta;
856 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
857 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
858 composite->red=gamma*Out(p->red,alpha,q->red,beta);
859 composite->green=gamma*Out(p->green,alpha,q->green,beta);
860 composite->blue=gamma*Out(p->blue,alpha,q->blue,beta);
861 if (q->colorspace == CMYKColorspace)
862 composite->index=gamma*Out(p->index,alpha,q->index,beta);
863}
864
865static inline void CompositeOver(const MagickPixelPacket *p,
866 const MagickRealType alpha,const MagickPixelPacket *q,
867 const MagickRealType beta,MagickPixelPacket *composite)
868{
869 MagickPixelCompositeOver(p,alpha,q,beta,composite);
870}
871
872static MagickRealType PegtopLight(const MagickRealType Sca,
873 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
874{
875 /*
876 PegTOP Soft-Light alternative: A continuous version of the Softlight
877 function, producing very similar results however it does not take into
878 account alpha channel.
879
880 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
881
882 See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
883 */
884 if (fabs(Da) < MagickEpsilon)
885 return(Sca);
886 return(Dca*Dca*(Sa-2*Sca)/Da+Sca*(2*Dca+1-Da)+Dca*(1-Sa));
887}
888
889static inline void CompositePegtopLight(const MagickPixelPacket *p,
890 const MagickRealType alpha,const MagickPixelPacket *q,
891 const MagickRealType beta,MagickPixelPacket *composite)
892{
893 MagickRealType
894 Da,
895 gamma,
896 Sa;
897
898 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
899 Da=1.0-QuantumScale*beta;
900 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
901 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
902 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
903 composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
904 q->red*Da,Da);
905 composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
906 q->green*Da,Da);
907 composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
908 q->blue*Da,Da);
909 if (q->colorspace == CMYKColorspace)
910 composite->index=gamma*PegtopLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
911 q->index*Da,Da);
912}
913
914static MagickRealType PinLight(const MagickRealType Sca,
915 const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
916{
917 /*
918 PinLight: A Photoshop 7 composition method
919 http://www.simplefilter.de/en/basics/mixmods.html
920
921 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
922 */
923 if (Dca*Sa < Da*(2*Sca-Sa))
924 return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
925 if ((Dca*Sa) > (2*Sca*Da))
926 return(Sca*Da+Sca+Dca*(1.0-Sa));
927 return(Sca*(1.0-Da)+Dca);
928}
929
930static inline void CompositePinLight(const MagickPixelPacket *p,
931 const MagickRealType alpha,const MagickPixelPacket *q,
932 const MagickRealType beta,MagickPixelPacket *composite)
933{
934 MagickRealType
935 Da,
936 gamma,
937 Sa;
938
939 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
940 Da=1.0-QuantumScale*beta;
941 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
942 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
943 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
944 composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
945 q->red*Da,Da);
946 composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
947 q->green*Da,Da);
948 composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
949 q->blue*Da,Da);
950 if (q->colorspace == CMYKColorspace)
951 composite->index=gamma*PinLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
952 q->index*Da,Da);
953}
954
955static inline void CompositePlus(const MagickPixelPacket *p,
956 const MagickRealType alpha,const MagickPixelPacket *q,
957 const MagickRealType beta,MagickPixelPacket *composite)
958{
959 MagickPixelCompositePlus(p,alpha,q,beta,composite);
960}
961
962static inline MagickRealType Screen(const MagickRealType Sca,
963 const MagickRealType Dca)
964{
965 return(Sca+Dca-Sca*Dca);
966}
967
968static inline void CompositeScreen(const MagickPixelPacket *p,
969 const MagickRealType alpha,const MagickPixelPacket *q,
970 const MagickRealType beta,MagickPixelPacket *composite)
971{
972 MagickRealType
973 Da,
974 gamma,
975 Sa;
976
977 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
978 Da=1.0-QuantumScale*beta;
979 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
980 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
981 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
982 composite->red=gamma*Screen(QuantumScale*p->red*Sa,QuantumScale*
983 q->red*Da);
984 composite->green=gamma*Screen(QuantumScale*p->green*Sa,QuantumScale*
985 q->green*Da);
986 composite->blue=gamma*Screen(QuantumScale*p->blue*Sa,QuantumScale*
987 q->blue*Da);
988 if (q->colorspace == CMYKColorspace)
989 composite->index=gamma*Screen(QuantumScale*p->index*Sa,QuantumScale*
990 q->index*Da);
991}
992
993static MagickRealType SoftLight(const MagickRealType Sca,
994 const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
995{
996#if 0
997 /*
998 Oct 2004 SVG specification -- spec discovered to be incorrect
999 See http://lists.w3.org/Archives/Public/www-svg/2009Feb/0014.html.
1000 */
1001 if (2.0*Sca < Sa)
1002 return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1003 if (8.0*Dca <= Da)
1004 return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa)*(3.0-8.0*Dca/Da))+
1005 Sca*(1.0-Da)+Dca*(1.0-Sa));
1006 return((Dca*Sa+(pow(Dca/Da,0.5)*Da-Dca)*(2.0*Sca-Sa))+Sca*(1.0-Da)+
1007 Dca*(1.0-Sa));
1008#else
1009 MagickRealType
1010 alpha,
1011 beta;
1012
1013 /*
1014 New specification: March 2009 SVG specification.
1015 */
1016 alpha=Dca/Da;
1017 if ((2.0*Sca) < Sa)
1018 return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1019 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1020 {
1021 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
1022 alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1023 return(beta);
1024 }
1025 beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
1026 return(beta);
1027#endif
1028}
1029
1030static inline void CompositeSoftLight(const MagickPixelPacket *p,
1031 const MagickRealType alpha,const MagickPixelPacket *q,
1032 const MagickRealType beta,MagickPixelPacket *composite)
1033{
1034 MagickRealType
1035 Da,
1036 gamma,
1037 Sa;
1038
1039 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
1040 Da=1.0-QuantumScale*beta;
1041 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1042 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1043 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1044 composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1045 q->red*Da,Da);
1046 composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1047 q->green*Da,Da);
1048 composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1049 q->blue*Da,Da);
1050 if (q->colorspace == CMYKColorspace)
1051 composite->index=gamma*SoftLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1052 q->index*Da,Da);
1053}
1054
1055static inline MagickRealType Subtract(const MagickRealType p,
1056 const MagickRealType magick_unused(alpha),const MagickRealType q,
1057 const MagickRealType magick_unused(beta))
1058{
1059 MagickRealType
1060 pixel;
1061
1062 pixel=p-q;
1063 if (pixel < 0.0)
1064 pixel+=(QuantumRange+1.0);
1065 return(pixel);
1066}
1067
1068static inline void CompositeSubtract(const MagickPixelPacket *p,
1069 const MagickRealType alpha,const MagickPixelPacket *q,
1070 const MagickRealType beta,MagickPixelPacket *composite)
1071{
1072 composite->red=Subtract(p->red,alpha,q->red,beta);
1073 composite->green=Subtract(p->green,alpha,q->green,beta);
1074 composite->blue=Subtract(p->blue,alpha,q->blue,beta);
1075 if (q->colorspace == CMYKColorspace)
1076 composite->index=Subtract(p->index,alpha,q->index,beta);
1077}
1078
1079static inline MagickRealType Threshold(const MagickRealType p,
1080 const MagickRealType magick_unused(alpha),const MagickRealType q,
1081 const MagickRealType magick_unused(beta),const MagickRealType threshold,
1082 const MagickRealType amount)
1083{
1084 MagickRealType
1085 delta;
1086
1087 delta=p-q;
1088 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
1089 return(q);
1090 return(q+delta*amount);
1091}
1092
1093static inline void CompositeThreshold(const MagickPixelPacket *p,
1094 const MagickRealType alpha,const MagickPixelPacket *q,
1095 const MagickRealType beta,const MagickRealType threshold,
1096 const MagickRealType amount,MagickPixelPacket *composite)
1097{
1098 composite->red=Threshold(p->red,alpha,q->red,beta,threshold,amount);
1099 composite->green=Threshold(p->green,alpha,q->green,beta,threshold,amount);
1100 composite->blue=Threshold(p->blue,alpha,q->blue,beta,threshold,amount);
1101 composite->opacity=(MagickRealType) QuantumRange-
1102 Threshold(p->opacity,alpha,q->opacity,beta,threshold,amount);
1103 if (q->colorspace == CMYKColorspace)
1104 composite->index=Threshold(p->index,alpha,q->index,beta,threshold,amount);
1105}
1106
1107static MagickRealType VividLight(const MagickRealType Sca,
1108 const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
1109{
1110 /*
1111 VividLight: A Photoshop 7 composition method. See
1112 http://www.simplefilter.de/en/basics/mixmods.html.
1113
1114 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
1115 */
1116 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
1117 return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1118 if ((2*Sca) <= Sa)
1119 return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1120 return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
1121}
1122
1123static inline void CompositeVividLight(const MagickPixelPacket *p,
1124 const MagickRealType alpha,const MagickPixelPacket *q,
1125 const MagickRealType beta,MagickPixelPacket *composite)
1126{
1127 MagickRealType
1128 Da,
1129 gamma,
1130 Sa;
1131
1132 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
1133 Da=1.0-QuantumScale*beta;
1134 gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
1135 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1136 gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1137 composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
1138 q->red*Da,Da);
1139 composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
1140 q->green*Da,Da);
1141 composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
1142 q->blue*Da,Da);
1143 if (q->colorspace == CMYKColorspace)
1144 composite->index=gamma*VividLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
1145 q->index*Da,Da);
1146}
1147
1148static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
1149 const MagickRealType Dca,const MagickRealType Da)
1150{
1151 return(Sca*(1-Da)+Dca*(1-Sa));
1152}
1153
1154static inline void CompositeXor(const MagickPixelPacket *p,
1155 const MagickRealType alpha,const MagickPixelPacket *q,
1156 const MagickRealType beta,MagickPixelPacket *composite)
1157{
1158 MagickRealType
1159 Da,
1160 gamma,
1161 Sa;
1162
1163 Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
1164 Da=1.0-QuantumScale*beta;
1165 gamma=Sa+Da-2*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
1166 composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
1167 /*
1168 Optimized by multipling QuantumRange taken from gamma.
1169 */
1170 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
1171 composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
1172 composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
1173 composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
1174 if (q->colorspace == CMYKColorspace)
1175 composite->index=gamma*Xor(p->index*Sa,Sa,q->index*Da,Da);
1176}
1177
1178static void HSBComposite(const double hue,const double saturation,
1179 const double brightness,MagickRealType *red,MagickRealType *green,
1180 MagickRealType *blue)
1181{
1182 MagickRealType
1183 f,
1184 h,
1185 p,
1186 q,
1187 t;
1188
1189 /*
1190 Convert HSB to RGB colorspace.
1191 */
1192 assert(red != (MagickRealType *) NULL);
1193 assert(green != (MagickRealType *) NULL);
1194 assert(blue != (MagickRealType *) NULL);
1195 if (saturation == 0.0)
1196 {
1197 *red=(MagickRealType) QuantumRange*brightness;
1198 *green=(*red);
1199 *blue=(*red);
1200 return;
1201 }
1202 h=6.0*(hue-floor(hue));
1203 f=h-floor((double) h);
1204 p=brightness*(1.0-saturation);
1205 q=brightness*(1.0-saturation*f);
1206 t=brightness*(1.0-saturation*(1.0-f));
1207 switch ((int) h)
1208 {
1209 case 0:
1210 default:
1211 {
1212 *red=(MagickRealType) QuantumRange*brightness;
1213 *green=(MagickRealType) QuantumRange*t;
1214 *blue=(MagickRealType) QuantumRange*p;
1215 break;
1216 }
1217 case 1:
1218 {
1219 *red=(MagickRealType) QuantumRange*q;
1220 *green=(MagickRealType) QuantumRange*brightness;
1221 *blue=(MagickRealType) QuantumRange*p;
1222 break;
1223 }
1224 case 2:
1225 {
1226 *red=(MagickRealType) QuantumRange*p;
1227 *green=(MagickRealType) QuantumRange*brightness;
1228 *blue=(MagickRealType) QuantumRange*t;
1229 break;
1230 }
1231 case 3:
1232 {
1233 *red=(MagickRealType) QuantumRange*p;
1234 *green=(MagickRealType) QuantumRange*q;
1235 *blue=(MagickRealType) QuantumRange*brightness;
1236 break;
1237 }
1238 case 4:
1239 {
1240 *red=(MagickRealType) QuantumRange*t;
1241 *green=(MagickRealType) QuantumRange*p;
1242 *blue=(MagickRealType) QuantumRange*brightness;
1243 break;
1244 }
1245 case 5:
1246 {
1247 *red=(MagickRealType) QuantumRange*brightness;
1248 *green=(MagickRealType) QuantumRange*p;
1249 *blue=(MagickRealType) QuantumRange*q;
1250 break;
1251 }
1252 }
1253}
1254
1255MagickExport MagickBooleanType CompositeImage(Image *image,
1256 const CompositeOperator compose,const Image *composite_image,
1257 const long x_offset,const long y_offset)
1258{
1259 MagickBooleanType
1260 status;
1261
1262 status=CompositeImageChannel(image,DefaultChannels,compose,composite_image,
1263 x_offset,y_offset);
1264 return(status);
1265}
1266
1267MagickExport MagickBooleanType CompositeImageChannel(Image *image,
1268 const ChannelType magick_unused(channel),const CompositeOperator compose,
1269 const Image *composite_image,const long x_offset,const long y_offset)
1270{
1271#define CompositeImageTag "Composite/Image"
1272
1273 CacheView
1274 *composite_view,
1275 *image_view;
1276
1277 const char
1278 *value;
1279
1280 double
1281 sans;
1282
1283 ExceptionInfo
1284 *exception;
1285
1286 GeometryInfo
1287 geometry_info;
1288
1289 Image
1290 *destination_image;
1291
1292 long
1293 progress,
1294 y;
1295
1296 MagickBooleanType
1297 modify_outside_overlay,
1298 status;
1299
1300 MagickPixelPacket
1301 zero;
1302
1303 MagickRealType
1304 amount,
1305 destination_dissolve,
1306 midpoint,
1307 percent_brightness,
1308 percent_saturation,
1309 source_dissolve,
1310 threshold;
1311
1312 MagickStatusType
1313 flags;
1314
1315 /*
1316 Prepare composite image.
1317 */
1318 assert(image != (Image *) NULL);
1319 assert(image->signature == MagickSignature);
1320 if (image->debug != MagickFalse)
1321 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1322 assert(composite_image != (Image *) NULL);
1323 assert(composite_image->signature == MagickSignature);
1324 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1325 return(MagickFalse);
1326 GetMagickPixelPacket(image,&zero);
1327 destination_image=(Image *) NULL;
1328 amount=0.5;
1329 destination_dissolve=1.0;
1330 modify_outside_overlay=MagickFalse;
1331 percent_brightness=100.0;
1332 percent_saturation=100.0;
1333 source_dissolve=1.0;
1334 threshold=0.05f;
1335 switch (compose)
1336 {
1337 case ClearCompositeOp:
1338 case SrcCompositeOp:
1339 case InCompositeOp:
1340 case SrcInCompositeOp:
1341 case OutCompositeOp:
1342 case SrcOutCompositeOp:
1343 case DstInCompositeOp:
1344 case DstAtopCompositeOp:
1345 {
1346 /*
1347 Modify destination outside the overlaid region.
1348 */
1349 modify_outside_overlay=MagickTrue;
1350 break;
1351 }
cristy308b4e62009-09-21 14:40:44 +00001352 case OverCompositeOp:
1353 {
1354 if (image->matte != MagickFalse)
1355 break;
cristyc34241c2009-09-22 00:34:01 +00001356 if (composite_image->matte != MagickFalse)
1357 break;
cristy308b4e62009-09-21 14:40:44 +00001358 }
1359 case CopyCompositeOp:
1360 {
cristyb41560a2009-10-06 22:57:52 +00001361 if ((x_offset < 0) || (y_offset < 0))
cristy308b4e62009-09-21 14:40:44 +00001362 break;
1363 if ((x_offset+(long) composite_image->columns) >= (long) image->columns)
1364 break;
cristy308b4e62009-09-21 14:40:44 +00001365 if ((y_offset+(long) composite_image->rows) >= (long) image->rows)
1366 break;
1367 status=MagickTrue;
1368 exception=(&image->exception);
1369 image_view=AcquireCacheView(image);
1370 composite_view=AcquireCacheView(composite_image);
1371#if defined(_OPENMP) && (_OPENMP >= 200203)
cristye0f584d2009-10-11 00:59:14 +00001372#pragma omp parallel for shared(status)
cristy308b4e62009-09-21 14:40:44 +00001373#endif
1374 for (y=0; y < (long) composite_image->rows; y++)
1375 {
1376 MagickBooleanType
1377 sync;
1378
1379 register const IndexPacket
1380 *composite_indexes;
1381
1382 register const PixelPacket
1383 *p;
1384
1385 register IndexPacket
1386 *indexes;
1387
1388 register PixelPacket
1389 *q;
1390
1391 if (status == MagickFalse)
1392 continue;
1393 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1394 1,exception);
cristye1c18e92009-10-05 16:07:10 +00001395 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
cristy308b4e62009-09-21 14:40:44 +00001396 composite_image->columns,1,exception);
1397 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1398 {
1399 status=MagickFalse;
1400 continue;
1401 }
1402 composite_indexes=GetCacheViewVirtualIndexQueue(composite_view);
1403 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1404 (void) CopyMagickMemory(q,p,composite_image->columns*sizeof(*p));
1405 if ((indexes != (IndexPacket *) NULL) &&
1406 (composite_indexes != (const IndexPacket *) NULL))
1407 (void) CopyMagickMemory(indexes,composite_indexes,
1408 composite_image->columns*sizeof(*indexes));
1409 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1410 if (sync == MagickFalse)
1411 status=MagickFalse;
1412 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1413 {
1414 MagickBooleanType
1415 proceed;
1416
1417#if defined(_OPENMP) && (_OPENMP >= 200203)
1418#pragma omp critical (MagickCore_TextureImage)
1419#endif
1420 proceed=SetImageProgress(image,CompositeImageTag,y,image->rows);
1421 if (proceed == MagickFalse)
1422 status=MagickFalse;
1423 }
1424 }
1425 composite_view=DestroyCacheView(composite_view);
1426 image_view=DestroyCacheView(image_view);
1427 return(status);
1428 }
cristy3ed852e2009-09-05 21:47:34 +00001429 case CopyOpacityCompositeOp:
1430 case ChangeMaskCompositeOp:
1431 {
1432 /*
1433 Modify destination outside the overlaid region and require an alpha
1434 channel to exist, to add transparency.
1435 */
1436 if (image->matte == MagickFalse)
1437 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1438 modify_outside_overlay=MagickTrue;
1439 break;
1440 }
1441 case BlurCompositeOp:
1442 {
1443 MagickPixelPacket
1444 pixel;
1445
1446 MagickRealType
1447 blur_xu,
1448 blur_xv,
1449 blur_yu,
1450 blur_yv;
1451
1452 ResampleFilter
1453 *resample_filter;
1454
1455 CacheView
1456 *composite_view,
1457 *dest_view;
1458
1459 /*
1460 Blur Image dictated by an overlay gradient map:
1461 X = red_channel; Y = green_channel;
1462 compose:args = x_scale[,y_scale[,angle]]
1463 */
1464 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1465 &image->exception);
1466 if (destination_image == (Image *) NULL)
1467 return(MagickFalse);
1468 /*
1469 Determine the horizontal and vertical maximim blur.
1470 */
1471 SetGeometryInfo(&geometry_info);
1472 flags=NoValue;
1473 value=GetImageArtifact(composite_image,"compose:args");
1474 if (value != (char *) NULL)
1475 flags=ParseGeometry(value,&geometry_info);
1476 if ((flags & WidthValue) == 0 )
1477 {
1478 destination_image=DestroyImage(destination_image);
1479 return(MagickFalse);
1480 }
1481 blur_xu=geometry_info.rho;
1482 blur_yv=geometry_info.sigma;
1483 blur_xv=blur_yu = 0.0;
1484 if ((flags & HeightValue) == 0)
1485 blur_yv=blur_xu;
1486 if ((flags & XValue) != 0)
1487 {
1488 MagickRealType
1489 angle,
1490 x,
1491 y;
1492
1493 x=blur_xu;
1494 y=blur_yv;
1495 angle=DegreesToRadians(geometry_info.xi);
1496 blur_xu=x*cos(angle);
1497 blur_xv=x*sin(angle);
1498 blur_yu=(-y*sin(angle));
1499 blur_yu=y*cos(angle);
1500 }
1501 /*
1502 Blur Image by resampling;
1503 */
1504 pixel=zero;
1505 exception=(&image->exception);
1506 resample_filter=AcquireResampleFilter(image,&image->exception);
1507 SetResampleFilter(resample_filter,GaussianFilter,1.0);
1508 dest_view=AcquireCacheView(destination_image);
1509 composite_view=AcquireCacheView(composite_image);
1510 for (y=0; y < (long) composite_image->rows; y++)
1511 {
1512 MagickBooleanType
1513 sync;
1514
1515 register const PixelPacket
1516 *__restrict p;
1517
1518 register PixelPacket
1519 *__restrict r;
1520
1521 register IndexPacket
1522 *__restrict destination_indexes;
1523
1524 register long
1525 x;
1526
1527 if (((y+y_offset) < 0) || ((y+y_offset) >= (long) image->rows))
1528 continue;
1529 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1530 1,exception);
1531 r=QueueCacheViewAuthenticPixels(dest_view,0,y,
1532 destination_image->columns,1,&image->exception);
1533 if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
1534 break;
1535 destination_indexes=GetCacheViewAuthenticIndexQueue(dest_view);
1536 for (x=0; x < (long) composite_image->columns; x++)
1537 {
1538 if (((x_offset+x) < 0) || ((x_offset+x) >= (long) image->columns))
1539 {
1540 p++;
1541 continue;
1542 }
1543 ScaleResampleFilter(resample_filter,blur_xu*QuantumScale*p->red,
1544 blur_yu*QuantumScale*p->green,blur_xv*QuantumScale*p->red,
1545 blur_yv*QuantumScale*p->green);
1546 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1547 (double) y_offset+y,&pixel);
1548 SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
1549 p++;
1550 r++;
1551 }
1552 sync=SyncCacheViewAuthenticPixels(dest_view,exception);
1553 if (sync == MagickFalse)
1554 break;
1555 }
1556 resample_filter=DestroyResampleFilter(resample_filter);
1557 composite_view=DestroyCacheView(composite_view);
1558 dest_view=DestroyCacheView(dest_view);
1559 composite_image=destination_image;
1560 break;
1561 }
1562 case DisplaceCompositeOp:
1563 case DistortCompositeOp:
1564 {
1565 MagickPixelPacket
1566 pixel;
1567
1568 MagickRealType
1569 horizontal_scale,
1570 vertical_scale,
1571 x_center,
1572 y_center,
1573 x_lookup,
1574 y_lookup;
1575
1576 register IndexPacket
1577 *__restrict destination_indexes;
1578
1579 register PixelPacket
1580 *__restrict r;
1581
1582 ResampleFilter
1583 *resample_filter;
1584
1585 CacheView
1586 *composite_view,
1587 *dest_view;
1588
1589 /*
1590 Displace/Distort based on overlay gradient map:
1591 X = red_channel; Y = green_channel;
1592 compose:args = x_scale[,y_scale[,x_center,y_center]]
1593 */
1594 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1595 &image->exception);
1596 if (destination_image == (Image *) NULL)
1597 return(MagickFalse);
1598 SetGeometryInfo(&geometry_info);
1599 flags=NoValue;
1600 value=GetImageArtifact(composite_image,"compose:args");
1601 if (value != (char *) NULL)
1602 flags=ParseGeometry(value,&geometry_info);
1603 if ((flags & (WidthValue|HeightValue)) == 0 )
1604 if ((flags & AspectValue) == 0)
1605 {
1606 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
1607 2.0;
1608 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
1609 }
1610 else
1611 {
1612 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
1613 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
1614 }
1615 else
1616 {
1617 horizontal_scale=geometry_info.rho;
1618 vertical_scale=geometry_info.sigma;
1619 if ((flags & PercentValue) != 0)
1620 {
1621 if ((flags & AspectValue) == 0)
1622 {
1623 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1624 vertical_scale*=(composite_image->rows-1.0)/200.0;
1625 }
1626 else
1627 {
1628 horizontal_scale*=(image->columns-1.0)/200.0;
1629 vertical_scale*=(image->rows-1.0)/200.0;
1630 }
1631 }
1632 if ((flags & HeightValue) == 0)
1633 vertical_scale=horizontal_scale;
1634 }
1635 /*
1636 Determine fixed center point for absolute distortion map
1637 Absolute distort ==
1638 Displace lookup relative to a fixed absolute point
1639 Select that point according to +X+Y user inputs.
1640 default = center of overlay image
1641 flag '!' = locations/percentage relative to background image
1642 */
1643 x_center=(MagickRealType) x_offset;
1644 y_center=(MagickRealType) y_offset;
1645 if (compose == DistortCompositeOp)
1646 {
1647 if ((flags & XValue) == 0)
1648 if ((flags & AspectValue) == 0)
1649 x_center=(MagickRealType) x_offset+ (composite_image->columns-1)/
1650 2.0;
1651 else
1652 x_center=((MagickRealType) image->columns-1)/2.0;
1653 else
1654 if ((flags & AspectValue) == 0)
1655 x_center=(MagickRealType) x_offset+geometry_info.xi;
1656 else
1657 x_center=geometry_info.xi;
1658 if ((flags & YValue) == 0)
1659 if ((flags & AspectValue) == 0)
1660 y_center=(MagickRealType) y_offset+
1661 (composite_image->rows-1)/2.0;
1662 else
1663 y_center=((MagickRealType) image->rows-1)/2.0;
1664 else
1665 if ((flags & AspectValue) == 0)
1666 y_center=(MagickRealType) y_offset+geometry_info.psi;
1667 else
1668 y_center=geometry_info.psi;
1669 }
1670 /*
1671 Shift the pixel lookup point as defined by the provided,
1672 displacement/distortion map. -- Like a lens...
1673 */
1674 pixel=zero;
1675 exception=(&image->exception);
1676 resample_filter=AcquireResampleFilter(image,&image->exception);
1677 dest_view=AcquireCacheView(destination_image);
1678 composite_view=AcquireCacheView(composite_image);
1679 for (y=0; y < (long) composite_image->rows; y++)
1680 {
1681 MagickBooleanType
1682 sync;
1683
1684 register const PixelPacket
1685 *__restrict p;
1686
1687 register long
1688 x;
1689
1690 if (((y+y_offset) < 0) || ((y+y_offset) >= (long) image->rows))
1691 continue;
1692 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1693 1,exception);
1694 r=QueueCacheViewAuthenticPixels(dest_view,0,y,
1695 destination_image->columns,1,&image->exception);
1696 if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
1697 break;
1698 destination_indexes=GetCacheViewAuthenticIndexQueue(dest_view);
1699 for (x=0; x < (long) composite_image->columns; x++)
1700 {
1701 if (((x_offset+x) < 0) || ((x_offset+x) >= (long) image->columns))
1702 {
1703 p++;
1704 continue;
1705 }
1706 /*
1707 Displace the lookup.
1708 */
1709 x_lookup=(horizontal_scale*(p->red-(((MagickRealType) QuantumRange+
1710 1.0)/2.0)))/(((MagickRealType) QuantumRange+1.0)/2.0)+
1711 x_center+((compose == DisplaceCompositeOp) ? x : 0);
1712 y_lookup=(vertical_scale*(p->green-(((MagickRealType) QuantumRange+
1713 1.0)/2.0)))/(((MagickRealType) QuantumRange+1.0)/2.0)+
1714 y_center+((compose == DisplaceCompositeOp) ? y : 0);
1715 (void) ResamplePixelColor(resample_filter,(double) x_lookup,
1716 (double) y_lookup,&pixel);
1717 /*
1718 Mask with 'invalid pixel mask' in alpha channel.
1719 */
1720 pixel.opacity = (MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1721 pixel.opacity)*(1.0-QuantumScale*p->opacity));
1722 SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
1723 p++;
1724 r++;
1725 }
1726 sync=SyncCacheViewAuthenticPixels(dest_view,exception);
1727 if (sync == MagickFalse)
1728 break;
1729 }
1730 resample_filter=DestroyResampleFilter(resample_filter);
1731 composite_view=DestroyCacheView(composite_view);
1732 dest_view=DestroyCacheView(dest_view);
1733 composite_image=destination_image;
1734 break;
1735 }
1736 case DissolveCompositeOp:
1737 {
1738 /*
1739 Geometry arguments to dissolve factors.
1740 */
1741 value=GetImageArtifact(composite_image,"compose:args");
1742 if (value != (char *) NULL)
1743 {
1744 flags=ParseGeometry(value,&geometry_info);
1745 source_dissolve=geometry_info.rho/100.0;
1746 destination_dissolve=1.0;
1747 if ((source_dissolve-MagickEpsilon) < 0.0)
1748 source_dissolve=0.0;
1749 if ((source_dissolve+MagickEpsilon) > 1.0)
1750 {
1751 destination_dissolve=2.0-source_dissolve;
1752 source_dissolve=1.0;
1753 }
1754 if ((flags & SigmaValue) != 0)
1755 destination_dissolve=geometry_info.sigma/100.0;
1756 if ((destination_dissolve-MagickEpsilon) < 0.0)
1757 destination_dissolve=0.0;
1758 modify_outside_overlay=MagickTrue;
1759 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1760 {
1761 destination_dissolve=1.0;
1762 modify_outside_overlay=MagickFalse;
1763 }
1764 }
1765 break;
1766 }
1767 case BlendCompositeOp:
1768 {
1769 value=GetImageArtifact(composite_image,"compose:args");
1770 if (value != (char *) NULL)
1771 {
1772 flags=ParseGeometry(value,&geometry_info);
1773 source_dissolve=geometry_info.rho/100.0;
1774 destination_dissolve=1.0-source_dissolve;
1775 if ((flags & SigmaValue) != 0)
1776 destination_dissolve=geometry_info.sigma/100.0;
1777 modify_outside_overlay=MagickTrue;
1778 if ((destination_dissolve+MagickEpsilon) > 1.0)
1779 modify_outside_overlay=MagickFalse;
1780 }
1781 break;
1782 }
1783 case MathematicsCompositeOp:
1784 {
1785 /*
1786 Just collect the values from "compose:args", setting.
1787 Unused values are set to zero automagically.
1788
1789 Arguments are normally a comma separated list, so this probably should
1790 be changed to some 'general comma list' parser, (with a minimum
1791 number of values)
1792 */
1793 SetGeometryInfo(&geometry_info);
1794 value=GetImageArtifact(composite_image,"compose:args");
1795 if (value != (char *) NULL)
1796 (void) ParseGeometry(value,&geometry_info);
1797 break;
1798 }
1799 case ModulateCompositeOp:
1800 {
1801 /*
1802 Determine the brightness and saturation scale.
1803 */
1804 value=GetImageArtifact(composite_image,"compose:args");
1805 if (value != (char *) NULL)
1806 {
1807 flags=ParseGeometry(value,&geometry_info);
1808 percent_brightness=geometry_info.rho;
1809 if ((flags & SigmaValue) != 0)
1810 percent_saturation=geometry_info.sigma;
1811 }
1812 break;
1813 }
1814 case ThresholdCompositeOp:
1815 {
1816 /*
1817 Determine the amount and threshold.
1818 */
1819 value=GetImageArtifact(composite_image,"compose:args");
1820 if (value != (char *) NULL)
1821 {
1822 flags=ParseGeometry(value,&geometry_info);
1823 amount=geometry_info.rho;
1824 threshold=geometry_info.sigma;
1825 if ((flags & SigmaValue) == 0)
1826 threshold=0.05f;
1827 }
1828 threshold*=QuantumRange;
1829 break;
1830 }
1831 default:
1832 break;
1833 }
1834 value=GetImageArtifact(composite_image,"compose:outside-overlay");
1835 if (value != (const char *) NULL)
1836 modify_outside_overlay=IsMagickTrue(value);
1837 /*
1838 Composite image.
1839 */
1840 status=MagickTrue;
1841 progress=0;
1842 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1843 GetMagickPixelPacket(composite_image,&zero);
1844 exception=(&image->exception);
1845 image_view=AcquireCacheView(image);
1846 composite_view=AcquireCacheView(composite_image);
cristy629a6c72009-09-13 23:28:22 +00001847#if defined(_OPENMP) && (_OPENMP >= 200203)
cristye0f584d2009-10-11 00:59:14 +00001848 #pragma omp parallel for shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001849#endif
1850 for (y=0; y < (long) image->rows; y++)
1851 {
1852 const PixelPacket
1853 *pixels;
1854
1855 double
1856 brightness,
1857 hue,
1858 saturation;
1859
1860 MagickPixelPacket
1861 composite,
1862 destination,
1863 source;
1864
1865 register const IndexPacket
1866 *__restrict composite_indexes;
1867
1868 register const PixelPacket
1869 *__restrict p;
1870
1871 register IndexPacket
1872 *__restrict indexes;
1873
1874 register long
1875 x;
1876
1877 register PixelPacket
1878 *__restrict q;
1879
1880 if (status == MagickFalse)
1881 continue;
1882 if (modify_outside_overlay == MagickFalse)
1883 {
1884 if (y < y_offset)
1885 continue;
1886 if ((y-y_offset) >= (long) composite_image->rows)
1887 continue;
1888 }
1889 /*
1890 If pixels is NULL, y is outside overlay region.
1891 */
1892 pixels=(PixelPacket *) NULL;
1893 p=(PixelPacket *) NULL;
1894 if ((y >= y_offset) && ((y-y_offset) < (long) composite_image->rows))
1895 {
1896 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1897 composite_image->columns,1,exception);
1898 if (p == (const PixelPacket *) NULL)
1899 {
1900 status=MagickFalse;
1901 continue;
1902 }
1903 pixels=p;
1904 if (x_offset < 0)
1905 p-=x_offset;
1906 }
1907 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1908 exception);
1909 if (q == (PixelPacket *) NULL)
1910 {
1911 status=MagickFalse;
1912 continue;
1913 }
1914 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1915 composite_indexes=GetCacheViewVirtualIndexQueue(composite_view);
1916 source=zero;
1917 destination=zero;
1918 hue=0.0;
1919 saturation=0.0;
1920 brightness=0.0;
1921 for (x=0; x < (long) image->columns; x++)
1922 {
1923 if (modify_outside_overlay == MagickFalse)
1924 {
1925 if (x < x_offset)
1926 {
1927 q++;
1928 continue;
1929 }
1930 if ((x-x_offset) >= (long) composite_image->columns)
1931 break;
1932 }
1933 destination.red=(MagickRealType) q->red;
1934 destination.green=(MagickRealType) q->green;
1935 destination.blue=(MagickRealType) q->blue;
1936 if (image->matte != MagickFalse)
1937 destination.opacity=(MagickRealType) q->opacity;
1938 if (image->colorspace == CMYKColorspace)
1939 {
1940 destination.red=(MagickRealType) QuantumRange-destination.red;
1941 destination.green=(MagickRealType) QuantumRange-destination.green;
1942 destination.blue=(MagickRealType) QuantumRange-destination.blue;
1943 destination.index=(MagickRealType) (QuantumRange-indexes[x]);
1944 }
1945 /*
1946 Handle destination modifications outside overlaid region.
1947 */
1948 composite=destination;
1949 if ((pixels == (PixelPacket *) NULL) || (x < x_offset) ||
1950 ((x-x_offset) >= (long) composite_image->columns))
1951 {
1952 switch (compose)
1953 {
1954 case DissolveCompositeOp:
1955 case BlendCompositeOp:
1956 {
1957 composite.opacity=(MagickRealType) (QuantumRange-
1958 destination_dissolve*(QuantumRange-composite.opacity));
1959 break;
1960 }
1961 case ClearCompositeOp:
1962 case SrcCompositeOp:
1963 {
1964 CompositeClear(&destination,&composite);
1965 break;
1966 }
1967 case InCompositeOp:
1968 case SrcInCompositeOp:
1969 case OutCompositeOp:
1970 case SrcOutCompositeOp:
1971 case DstInCompositeOp:
1972 case DstAtopCompositeOp:
1973 case CopyOpacityCompositeOp:
1974 case ChangeMaskCompositeOp:
1975 {
1976 composite.opacity=(MagickRealType) TransparentOpacity;
1977 break;
1978 }
1979 default:
1980 {
1981 (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,
1982 y-y_offset,&composite,exception);
1983 break;
1984 }
1985 }
1986 if (image->colorspace == CMYKColorspace)
1987 {
1988 composite.red=(MagickRealType) QuantumRange-composite.red;
1989 composite.green=(MagickRealType) QuantumRange-composite.green;
1990 composite.blue=(MagickRealType) QuantumRange-composite.blue;
1991 composite.index=(MagickRealType) QuantumRange-composite.index;
1992 }
1993 q->red=RoundToQuantum(composite.red);
1994 q->green=RoundToQuantum(composite.green);
1995 q->blue=RoundToQuantum(composite.blue);
1996 if (image->matte != MagickFalse)
1997 q->opacity=RoundToQuantum(composite.opacity);
1998 if (image->colorspace == CMYKColorspace)
1999 indexes[x]=RoundToQuantum(composite.index);
2000 q++;
2001 continue;
2002 }
2003 /*
2004 Handle normal overlay of source onto destination.
2005 */
2006 source.red=(MagickRealType) p->red;
2007 source.green=(MagickRealType) p->green;
2008 source.blue=(MagickRealType) p->blue;
2009 if (composite_image->matte != MagickFalse)
2010 source.opacity=(MagickRealType) p->opacity;
2011 if (composite_image->colorspace == CMYKColorspace)
2012 {
2013 source.red=(MagickRealType) QuantumRange-source.red;
2014 source.green=(MagickRealType) QuantumRange-source.green;
2015 source.blue=(MagickRealType) QuantumRange-source.blue;
2016 source.index=(MagickRealType) QuantumRange-(MagickRealType)
2017 composite_indexes[x-x_offset];
2018 }
2019 switch (compose)
2020 {
2021 case AddCompositeOp:
2022 {
2023 CompositeAdd(&source,source.opacity,&destination,destination.opacity,
2024 &composite);
2025 break;
2026 }
2027 case ClearCompositeOp:
2028 {
2029 CompositeClear(&destination,&composite);
2030 break;
2031 }
2032 case SrcCompositeOp:
2033 case CopyCompositeOp:
2034 case ReplaceCompositeOp:
2035 {
2036 composite=source;
2037 break;
2038 }
2039 case ChangeMaskCompositeOp:
2040 {
2041 if ((composite.opacity > ((MagickRealType) QuantumRange/2.0)) ||
2042 (IsMagickColorSimilar(&source,&destination) != MagickFalse))
2043 composite.opacity=(MagickRealType) TransparentOpacity;
2044 else
2045 composite.opacity=(MagickRealType) OpaqueOpacity;
2046 break;
2047 }
2048 case DivideCompositeOp:
2049 {
2050 CompositeDivide(&source,source.opacity,&destination,
2051 destination.opacity,&composite);
2052 break;
2053 }
2054 case DstCompositeOp:
2055 break;
2056 case OverCompositeOp:
2057 case SrcOverCompositeOp:
2058 {
2059 CompositeOver(&source,source.opacity,&destination,destination.opacity,
2060 &composite);
2061 break;
2062 }
2063 case DstOverCompositeOp:
2064 {
2065 CompositeOver(&destination,destination.opacity,&source,source.opacity,
2066 &composite);
2067 break;
2068 }
2069 case SrcInCompositeOp:
2070 case InCompositeOp:
2071 {
2072 CompositeIn(&source,source.opacity,&destination,destination.opacity,
2073 &composite);
2074 break;
2075 }
2076 case DstInCompositeOp:
2077 {
2078 CompositeIn(&destination,destination.opacity,&source,source.opacity,
2079 &composite);
2080 break;
2081 }
2082 case OutCompositeOp:
2083 case SrcOutCompositeOp:
2084 {
2085 CompositeOut(&source,source.opacity,&destination,destination.opacity,
2086 &composite);
2087 break;
2088 }
2089 case DstOutCompositeOp:
2090 {
2091 CompositeOut(&destination,destination.opacity,&source,source.opacity,
2092 &composite);
2093 break;
2094 }
2095 case AtopCompositeOp:
2096 case SrcAtopCompositeOp:
2097 {
2098 CompositeAtop(&source,source.opacity,&destination,destination.opacity,
2099 &composite);
2100 break;
2101 }
2102 case DstAtopCompositeOp:
2103 {
2104 CompositeAtop(&destination,destination.opacity,&source,source.opacity,
2105 &composite);
2106 break;
2107 }
2108 case XorCompositeOp:
2109 {
2110 CompositeXor(&source,source.opacity,&destination,destination.opacity,
2111 &composite);
2112 break;
2113 }
2114 case PlusCompositeOp:
2115 {
2116 CompositePlus(&source,source.opacity,&destination,destination.opacity,
2117 &composite);
2118 break;
2119 }
2120 case MultiplyCompositeOp:
2121 {
2122 CompositeMultiply(&source,source.opacity,&destination,
2123 destination.opacity,&composite);
2124 break;
2125 }
2126 case ScreenCompositeOp:
2127 {
2128 CompositeScreen(&source,source.opacity,&destination,
2129 destination.opacity,&composite);
2130 break;
2131 }
2132 case DarkenCompositeOp:
2133 {
2134 CompositeDarken(&source,source.opacity,&destination,
2135 destination.opacity,&composite);
2136 break;
2137 }
2138 case LightenCompositeOp:
2139 {
2140 CompositeLighten(&source,source.opacity,&destination,
2141 destination.opacity,&composite);
2142 break;
2143 }
2144 case ColorDodgeCompositeOp:
2145 {
2146 CompositeColorDodge(&source,source.opacity,&destination,
2147 destination.opacity,&composite);
2148 break;
2149 }
2150 case ColorBurnCompositeOp:
2151 {
2152 CompositeColorBurn(&source,source.opacity,&destination,
2153 destination.opacity,&composite);
2154 break;
2155 }
2156 case LinearDodgeCompositeOp:
2157 {
2158 CompositeLinearDodge(&source,source.opacity,&destination,
2159 destination.opacity,&composite);
2160 break;
2161 }
2162 case LinearBurnCompositeOp:
2163 {
2164 CompositeLinearBurn(&source,source.opacity,&destination,
2165 destination.opacity,&composite);
2166 break;
2167 }
2168 case HardLightCompositeOp:
2169 {
2170 CompositeHardLight(&source,source.opacity,&destination,
2171 destination.opacity,&composite);
2172 break;
2173 }
2174 case OverlayCompositeOp:
2175 {
2176 /*
2177 Reversed HardLight.
2178 */
2179 CompositeHardLight(&destination,destination.opacity,&source,
2180 source.opacity,&composite);
2181 break;
2182 }
2183 case SoftLightCompositeOp:
2184 {
2185 CompositeSoftLight(&source,source.opacity,&destination,
2186 destination.opacity,&composite);
2187 break;
2188 }
2189 case LinearLightCompositeOp:
2190 {
2191 CompositeLinearLight(&source,source.opacity,&destination,
2192 destination.opacity,&composite);
2193 break;
2194 }
2195 case PegtopLightCompositeOp:
2196 {
2197 CompositePegtopLight(&source,source.opacity,&destination,
2198 destination.opacity,&composite);
2199 break;
2200 }
2201 case VividLightCompositeOp:
2202 {
2203 CompositeVividLight(&source,source.opacity,&destination,
2204 destination.opacity,&composite);
2205 break;
2206 }
2207 case PinLightCompositeOp:
2208 {
2209 CompositePinLight(&source,source.opacity,&destination,
2210 destination.opacity,&composite);
2211 break;
2212 }
2213 case DifferenceCompositeOp:
2214 {
2215 CompositeDifference(&source,source.opacity,&destination,
2216 destination.opacity,&composite);
2217 break;
2218 }
2219 case ExclusionCompositeOp:
2220 {
2221 CompositeExclusion(&source,source.opacity,&destination,
2222 destination.opacity,&composite);
2223 break;
2224 }
2225 case MinusCompositeOp:
2226 {
2227 CompositeMinus(&source,source.opacity,&destination,
2228 destination.opacity,&composite);
2229 break;
2230 }
2231 case BumpmapCompositeOp:
2232 {
2233 if (source.opacity == TransparentOpacity)
2234 break;
2235 CompositeBumpmap(&source,source.opacity,&destination,
2236 destination.opacity,&composite);
2237 break;
2238 }
2239 case DissolveCompositeOp:
2240 {
2241 CompositeOver(&source,(MagickRealType) (QuantumRange-source_dissolve*
2242 (QuantumRange-source.opacity)),&destination,(MagickRealType)
2243 (QuantumRange-destination_dissolve*(QuantumRange-
2244 destination.opacity)),&composite);
2245 break;
2246 }
2247 case BlendCompositeOp:
2248 {
2249 MagickPixelCompositeBlend(&source,source_dissolve,&destination,
2250 destination_dissolve,&composite);
2251 break;
2252 }
2253 case MathematicsCompositeOp:
2254 {
2255 CompositeMathematics(&source,&destination,&geometry_info,&composite);
2256 break;
2257 }
2258 case BlurCompositeOp:
2259 case DisplaceCompositeOp:
2260 case DistortCompositeOp:
2261 {
2262 composite=source;
2263 break;
2264 }
2265 case ThresholdCompositeOp:
2266 {
2267 CompositeThreshold(&source,source.opacity,&destination,
2268 destination.opacity,threshold,amount,&composite);
2269 break;
2270 }
2271 case ModulateCompositeOp:
2272 {
2273 long
2274 offset;
2275
2276 if (source.opacity == TransparentOpacity)
2277 break;
2278 offset=(long) (MagickPixelIntensityToQuantum(&source)-midpoint);
2279 if (offset == 0)
2280 break;
2281 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2282 &saturation,&brightness);
2283 brightness+=(0.01*percent_brightness*offset)/midpoint;
2284 saturation*=0.01*percent_saturation;
2285 HSBComposite(hue,saturation,brightness,&composite.red,
2286 &composite.green,&composite.blue);
2287 break;
2288 }
2289 case HueCompositeOp:
2290 {
2291 if (source.opacity == TransparentOpacity)
2292 break;
2293 if (destination.opacity == TransparentOpacity)
2294 {
2295 composite=source;
2296 break;
2297 }
2298 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2299 &saturation,&brightness);
2300 CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
2301 HSBComposite(hue,saturation,brightness,&composite.red,
2302 &composite.green,&composite.blue);
2303 if (source.opacity < destination.opacity)
2304 composite.opacity=source.opacity;
2305 break;
2306 }
2307 case SaturateCompositeOp:
2308 {
2309 if (source.opacity == TransparentOpacity)
2310 break;
2311 if (destination.opacity == TransparentOpacity)
2312 {
2313 composite=source;
2314 break;
2315 }
2316 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2317 &saturation,&brightness);
2318 CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
2319 &sans);
2320 HSBComposite(hue,saturation,brightness,&composite.red,
2321 &composite.green,&composite.blue);
2322 if (source.opacity < destination.opacity)
2323 composite.opacity=source.opacity;
2324 break;
2325 }
2326 case SubtractCompositeOp:
2327 {
2328 CompositeSubtract(&source,source.opacity,&destination,
2329 destination.opacity,&composite);
2330 break;
2331 }
2332 case LuminizeCompositeOp:
2333 {
2334 if (source.opacity == TransparentOpacity)
2335 break;
2336 if (destination.opacity == TransparentOpacity)
2337 {
2338 composite=source;
2339 break;
2340 }
2341 CompositeHSB(destination.red,destination.green,destination.blue,&hue,
2342 &saturation,&brightness);
2343 CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
2344 &brightness);
2345 HSBComposite(hue,saturation,brightness,&composite.red,
2346 &composite.green,&composite.blue);
2347 if (source.opacity < destination.opacity)
2348 composite.opacity=source.opacity;
2349 break;
2350 }
2351 case ColorizeCompositeOp:
2352 {
2353 if (source.opacity == TransparentOpacity)
2354 break;
2355 if (destination.opacity == TransparentOpacity)
2356 {
2357 composite=source;
2358 break;
2359 }
2360 CompositeHSB(destination.red,destination.green,destination.blue,&sans,
2361 &sans,&brightness);
2362 CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
2363 &sans);
2364 HSBComposite(hue,saturation,brightness,&composite.red,
2365 &composite.green,&composite.blue);
2366 if (source.opacity < destination.opacity)
2367 composite.opacity=source.opacity;
2368 break;
2369 }
2370 case CopyRedCompositeOp:
2371 case CopyCyanCompositeOp:
2372 {
2373 composite.red=source.red;
2374 break;
2375 }
2376 case CopyGreenCompositeOp:
2377 case CopyMagentaCompositeOp:
2378 {
2379 composite.green=source.green;
2380 break;
2381 }
2382 case CopyBlueCompositeOp:
2383 case CopyYellowCompositeOp:
2384 {
2385 composite.blue=source.blue;
2386 break;
2387 }
2388 case CopyOpacityCompositeOp:
2389 {
2390 if (source.matte == MagickFalse)
2391 {
2392 composite.opacity=(MagickRealType) (QuantumRange-
2393 MagickPixelIntensityToQuantum(&source));
2394 break;
2395 }
2396 composite.opacity=source.opacity;
2397 break;
2398 }
2399 case CopyBlackCompositeOp:
2400 {
2401 if (source.colorspace != CMYKColorspace)
2402 ConvertRGBToCMYK(&source);
2403 composite.index=source.index;
2404 break;
2405 }
2406 default:
2407 break;
2408 }
2409 if (image->colorspace == CMYKColorspace)
2410 {
2411 composite.red=(MagickRealType) QuantumRange-composite.red;
2412 composite.green=(MagickRealType) QuantumRange-composite.green;
2413 composite.blue=(MagickRealType) QuantumRange-composite.blue;
2414 composite.index=(MagickRealType) QuantumRange-composite.index;
2415 }
2416 q->red=RoundToQuantum(composite.red);
2417 q->green=RoundToQuantum(composite.green);
2418 q->blue=RoundToQuantum(composite.blue);
2419 q->opacity=RoundToQuantum(composite.opacity);
2420 if (image->colorspace == CMYKColorspace)
2421 indexes[x]=RoundToQuantum(composite.index);
2422 p++;
2423 if (p >= (pixels+composite_image->columns))
2424 p=pixels;
2425 q++;
2426 }
2427 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2428 status=MagickFalse;
2429 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2430 {
2431 MagickBooleanType
2432 proceed;
2433
cristy629a6c72009-09-13 23:28:22 +00002434#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy3ed852e2009-09-05 21:47:34 +00002435 #pragma omp critical (MagickCore_CompositeImageChannel)
2436#endif
2437 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2438 image->rows);
2439 if (proceed == MagickFalse)
2440 status=MagickFalse;
2441 }
2442 }
2443 composite_view=DestroyCacheView(composite_view);
2444 image_view=DestroyCacheView(image_view);
2445 if (destination_image != (Image * ) NULL)
2446 destination_image=DestroyImage(destination_image);
2447 return(status);
2448}
cristy316d5172009-09-17 19:31:25 +00002449
2450/*
2451%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2452% %
2453% %
2454% %
2455% T e x t u r e I m a g e %
2456% %
2457% %
2458% %
2459%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2460%
2461% TextureImage() repeatedly tiles the texture image across and down the image
2462% canvas.
2463%
2464% The format of the TextureImage method is:
2465%
2466% MagickBooleanType TextureImage(Image *image,const Image *texture)
2467%
2468% A description of each parameter follows:
2469%
2470% o image: the image.
2471%
2472% o texture: This image is the texture to layer on the background.
2473%
2474*/
2475MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
2476{
2477#define TextureImageTag "Texture/Image"
2478
cristyfcb1a662009-09-20 02:31:52 +00002479 CacheView
2480 *image_view,
2481 *texture_view;
2482
cristy316d5172009-09-17 19:31:25 +00002483 ExceptionInfo
2484 *exception;
2485
2486 long
2487 y;
2488
cristyfcb1a662009-09-20 02:31:52 +00002489 MagickBooleanType
cristy316d5172009-09-17 19:31:25 +00002490 status;
2491
2492 assert(image != (Image *) NULL);
2493 if (image->debug != MagickFalse)
2494 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2495 assert(image->signature == MagickSignature);
2496 if (texture == (const Image *) NULL)
2497 return(MagickFalse);
2498 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2499 return(MagickFalse);
cristyfcb1a662009-09-20 02:31:52 +00002500 status=MagickTrue;
2501 if ((image->compose != CopyCompositeOp) &&
cristyec678732009-09-22 11:31:10 +00002502 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2503 (texture->matte != MagickFalse)))
cristyfcb1a662009-09-20 02:31:52 +00002504 {
2505 /*
2506 Tile texture onto the image background.
2507 */
2508#if defined(_OPENMP) && (_OPENMP >= 200203)
cristye0f584d2009-10-11 00:59:14 +00002509 #pragma omp parallel for shared(status)
cristyfcb1a662009-09-20 02:31:52 +00002510#endif
2511 for (y=0; y < (long) image->rows; y+=texture->rows)
2512 {
2513 register long
2514 x;
cristy82b15832009-10-06 19:17:37 +00002515
cristyfcb1a662009-09-20 02:31:52 +00002516 if (status == MagickFalse)
2517 continue;
2518 for (x=0; x < (long) image->columns; x+=texture->columns)
2519 {
2520 MagickBooleanType
2521 thread_status;
cristy82b15832009-10-06 19:17:37 +00002522
cristyfcb1a662009-09-20 02:31:52 +00002523 thread_status=CompositeImage(image,image->compose,texture,x+
2524 texture->tile_offset.x,y+texture->tile_offset.y);
2525 if (thread_status == MagickFalse)
2526 {
2527 status=thread_status;
2528 break;
2529 }
2530 }
2531 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2532 {
2533 MagickBooleanType
2534 proceed;
cristy82b15832009-10-06 19:17:37 +00002535
cristyfcb1a662009-09-20 02:31:52 +00002536#if defined(_OPENMP) && (_OPENMP >= 200203)
2537 #pragma omp critical (MagickCore_TextureImage)
2538#endif
2539 proceed=SetImageProgress(image,TextureImageTag,y,image->rows);
2540 if (proceed == MagickFalse)
2541 status=MagickFalse;
2542 }
2543 }
2544 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2545 image->rows,image->rows);
2546 return(status);
2547 }
cristy316d5172009-09-17 19:31:25 +00002548 /*
cristyfcb1a662009-09-20 02:31:52 +00002549 Tile texture onto the image background (optimized).
cristy316d5172009-09-17 19:31:25 +00002550 */
2551 status=MagickTrue;
2552 exception=(&image->exception);
cristyfcb1a662009-09-20 02:31:52 +00002553 image_view=AcquireCacheView(image);
2554 texture_view=AcquireCacheView(texture);
2555#if defined(_OPENMP) && (_OPENMP >= 200203)
cristye0f584d2009-10-11 00:59:14 +00002556#pragma omp parallel for shared(status)
cristyfcb1a662009-09-20 02:31:52 +00002557#endif
2558 for (y=0; y < (long) image->rows; y++)
cristy316d5172009-09-17 19:31:25 +00002559 {
cristyfcb1a662009-09-20 02:31:52 +00002560 MagickBooleanType
2561 sync;
2562
2563 register const IndexPacket
2564 *texture_indexes;
2565
2566 register const PixelPacket
2567 *p;
2568
2569 register IndexPacket
2570 *indexes;
2571
cristy316d5172009-09-17 19:31:25 +00002572 register long
2573 x;
2574
cristyfcb1a662009-09-20 02:31:52 +00002575 register PixelPacket
2576 *q;
2577
2578 unsigned long
2579 width;
2580
2581 if (status == MagickFalse)
2582 continue;
2583 p=GetCacheViewVirtualPixels(texture_view,0,y % texture->rows,
2584 texture->columns,1,exception);
2585 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2586 exception);
2587 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2588 {
2589 status=MagickFalse;
2590 continue;
2591 }
2592 texture_indexes=GetCacheViewVirtualIndexQueue(texture_view);
2593 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristy316d5172009-09-17 19:31:25 +00002594 for (x=0; x < (long) image->columns; x+=texture->columns)
cristyfcb1a662009-09-20 02:31:52 +00002595 {
2596 width=texture->columns;
cristy308b4e62009-09-21 14:40:44 +00002597 if ((x+(long) width) > (long) image->columns)
cristyfcb1a662009-09-20 02:31:52 +00002598 width=image->columns-x;
2599 (void) CopyMagickMemory(q,p,width*sizeof(*p));
cristyaa67ab02009-09-22 02:24:55 +00002600 if ((image->colorspace == CMYKColorspace) &&
2601 (texture->colorspace == CMYKColorspace))
cristyfcb1a662009-09-20 02:31:52 +00002602 {
2603 (void) CopyMagickMemory(indexes,texture_indexes,width*
2604 sizeof(*indexes));
2605 indexes+=width;
2606 }
2607 q+=width;
2608 }
2609 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2610 if (sync == MagickFalse)
2611 status=MagickFalse;
cristy316d5172009-09-17 19:31:25 +00002612 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2613 {
2614 MagickBooleanType
2615 proceed;
2616
cristyfcb1a662009-09-20 02:31:52 +00002617#if defined(_OPENMP) && (_OPENMP >= 200203)
2618#pragma omp critical (MagickCore_TextureImage)
2619#endif
cristy316d5172009-09-17 19:31:25 +00002620 proceed=SetImageProgress(image,TextureImageTag,y,image->rows);
2621 if (proceed == MagickFalse)
2622 status=MagickFalse;
2623 }
2624 }
cristyfcb1a662009-09-20 02:31:52 +00002625 texture_view=DestroyCacheView(texture_view);
2626 image_view=DestroyCacheView(image_view);
2627 return(status);
cristy316d5172009-09-17 19:31:25 +00002628}