MagickCore 7.1.1
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
composite.c
1/*
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% Cristy %
17% July 1992 %
18% %
19% %
20% Copyright @ 1999 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% https://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 "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/channel.h"
49#include "MagickCore/client.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colorspace.h"
53#include "MagickCore/colorspace-private.h"
54#include "MagickCore/composite.h"
55#include "MagickCore/composite-private.h"
56#include "MagickCore/constitute.h"
57#include "MagickCore/draw.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/fx.h"
60#include "MagickCore/gem.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/monitor.h"
68#include "MagickCore/monitor-private.h"
69#include "MagickCore/morphology.h"
70#include "MagickCore/option.h"
71#include "MagickCore/pixel-accessor.h"
72#include "MagickCore/property.h"
73#include "MagickCore/quantum.h"
74#include "MagickCore/resample.h"
75#include "MagickCore/resource_.h"
76#include "MagickCore/string_.h"
77#include "MagickCore/string-private.h"
78#include "MagickCore/thread-private.h"
79#include "MagickCore/threshold.h"
80#include "MagickCore/token.h"
81#include "MagickCore/transform.h"
82#include "MagickCore/utility.h"
83#include "MagickCore/utility-private.h"
84#include "MagickCore/version.h"
85
86/*
87%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88% %
89% %
90% %
91% C o m p o s i t e I m a g e %
92% %
93% %
94% %
95%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
96%
97% CompositeImage() returns the second image composited onto the first
98% at the specified offset, using the specified composite method.
99%
100% The format of the CompositeImage method is:
101%
102% MagickBooleanType CompositeImage(Image *image,
103% const Image *source_image,const CompositeOperator compose,
104% const MagickBooleanType clip_to_self,const ssize_t x_offset,
105% const ssize_t y_offset,ExceptionInfo *exception)
106%
107% A description of each parameter follows:
108%
109% o image: the canvas image, modified by he composition
110%
111% o source_image: the source image.
112%
113% o compose: This operator affects how the composite is applied to
114% the image. The operators and how they are utilized are listed here
115% http://www.w3.org/TR/SVG12/#compositing.
116%
117% o clip_to_self: set to MagickTrue to limit composition to area composed.
118%
119% o x_offset: the column offset of the composited image.
120%
121% o y_offset: the row offset of the composited image.
122%
123% Extra Controls from Image meta-data in 'image' (artifacts)
124%
125% o "compose:args"
126% A string containing extra numerical arguments for specific compose
127% methods, generally expressed as a 'geometry' or a comma separated list
128% of numbers.
129%
130% Compose methods needing such arguments include "BlendCompositeOp" and
131% "DisplaceCompositeOp".
132%
133% o exception: return any errors or warnings in this structure.
134%
135*/
136
137/*
138 Composition based on the SVG specification:
139
140 A Composition is defined by...
141 Color Function : f(Sc,Dc) where Sc and Dc are the normalized colors
142 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
143 Y = 1 for source preserved
144 Z = 1 for canvas preserved
145
146 Conversion to transparency (then optimized)
147 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
148 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
149
150 Where...
151 Sca = Sc*Sa normalized Source color divided by Source alpha
152 Dca = Dc*Da normalized Dest color divided by Dest alpha
153 Dc' = Dca'/Da' the desired color value for this channel.
154
155 Da' in in the follow formula as 'gamma' The resulting alpha value.
156
157 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
158 the following optimizations...
159 gamma = Sa+Da-Sa*Da;
160 gamma = 1 - QuantumScale*alpha * QuantumScale*beta;
161 opacity = QuantumScale*alpha*beta; // over blend, optimized 1-Gamma
162
163 The above SVG definitions also define that Mathematical Composition
164 methods should use a 'Over' blending mode for Alpha Channel.
165 It however was not applied for composition modes of 'Plus', 'Minus',
166 the modulus versions of 'Add' and 'Subtract'.
167
168 Mathematical operator changes to be applied from IM v6.7...
169
170 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
171 'ModulusAdd' and 'ModulusSubtract' for clarity.
172
173 2) All mathematical compositions work as per the SVG specification
174 with regard to blending. This now includes 'ModulusAdd' and
175 'ModulusSubtract'.
176
177 3) When the special channel flag 'sync' (synchronize channel updates)
178 is turned off (enabled by default) then mathematical compositions are
179 only performed on the channels specified, and are applied
180 independently of each other. In other words the mathematics is
181 performed as 'pure' mathematical operations, rather than as image
182 operations.
183*/
184
185static Image *BlendConvolveImage(const Image *image,const char *kernel,
186 ExceptionInfo *exception)
187{
188 Image
189 *clone_image,
190 *convolve_image;
191
193 *kernel_info;
194
195 /*
196 Convolve image with a kernel.
197 */
198 kernel_info=AcquireKernelInfo(kernel,exception);
199 if (kernel_info == (KernelInfo *) NULL)
200 return((Image *) NULL);
201 clone_image=CloneImage(image,0,0,MagickTrue,exception);
202 if (clone_image == (Image *) NULL)
203 {
204 kernel_info=DestroyKernelInfo(kernel_info);
205 return((Image *) NULL);
206 }
207 (void) SetImageAlphaChannel(clone_image,OffAlphaChannel,exception);
208 convolve_image=ConvolveImage(clone_image,kernel_info,exception);
209 kernel_info=DestroyKernelInfo(kernel_info);
210 clone_image=DestroyImage(clone_image);
211 return(convolve_image);
212}
213
214static Image *BlendMagnitudeImage(const Image *dx_image,const Image *dy_image,
215 ExceptionInfo *exception)
216{
218 *dx_view,
219 *dy_view,
220 *magnitude_view;
221
222 Image
223 *magnitude_image;
224
225 MagickBooleanType
226 status = MagickTrue;
227
228 ssize_t
229 y;
230
231 /*
232 Generate the magnitude between two images.
233 */
234 magnitude_image=CloneImage(dx_image,0,0,MagickTrue,exception);
235 if (magnitude_image == (Image *) NULL)
236 return(magnitude_image);
237 dx_view=AcquireVirtualCacheView(dx_image,exception);
238 dy_view=AcquireVirtualCacheView(dy_image,exception);
239 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
240#if defined(MAGICKCORE_OPENMP_SUPPORT)
241 #pragma omp parallel for schedule(static) shared(status) \
242 magick_number_threads(dx_image,magnitude_image,dx_image->rows,1)
243#endif
244 for (y=0; y < (ssize_t) dx_image->rows; y++)
245 {
246 const Quantum
247 *magick_restrict p,
248 *magick_restrict q;
249
250 Quantum
251 *magick_restrict r;
252
253 ssize_t
254 x;
255
256 if (status == MagickFalse)
257 continue;
258 p=GetCacheViewVirtualPixels(dx_view,0,y,dx_image->columns,1,exception);
259 q=GetCacheViewVirtualPixels(dy_view,0,y,dx_image->columns,1,exception);
260 r=GetCacheViewAuthenticPixels(magnitude_view,0,y,dx_image->columns,1,
261 exception);
262 if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL) ||
263 (r == (Quantum *) NULL))
264 {
265 status=MagickFalse;
266 continue;
267 }
268 for (x=0; x < (ssize_t) dx_image->columns; x++)
269 {
270 ssize_t
271 i;
272
273 for (i=0; i < (ssize_t) GetPixelChannels(dx_image); i++)
274 {
275 PixelChannel channel = GetPixelChannelChannel(dx_image,i);
276 PixelTrait traits = GetPixelChannelTraits(dx_image,channel);
277 PixelTrait dy_traits = GetPixelChannelTraits(dy_image,channel);
278 if ((traits == UndefinedPixelTrait) ||
279 (dy_traits == UndefinedPixelTrait) ||
280 ((dy_traits & UpdatePixelTrait) == 0))
281 continue;
282 r[i]=ClampToQuantum(hypot((double) p[i],(double)
283 GetPixelChannel(dy_image,channel,q)));
284 }
285 p+=(ptrdiff_t) GetPixelChannels(dx_image);
286 q+=(ptrdiff_t) GetPixelChannels(dy_image);
287 r+=(ptrdiff_t) GetPixelChannels(magnitude_image);
288 }
289 if (SyncCacheViewAuthenticPixels(magnitude_view,exception) == MagickFalse)
290 status=MagickFalse;
291 }
292 magnitude_view=DestroyCacheView(magnitude_view);
293 dy_view=DestroyCacheView(dy_view);
294 dx_view=DestroyCacheView(dx_view);
295 if (status == MagickFalse)
296 magnitude_image=DestroyImage(magnitude_image);
297 return(magnitude_image);
298}
299
300static Image *BlendMaxMagnitudeImage(const Image *alpha_image,
301 const Image *beta_image,const Image *dx_image,const Image *dy_image,
302 ExceptionInfo *exception)
303{
305 *alpha_view,
306 *beta_view,
307 *dx_view,
308 *dy_view,
309 *magnitude_view;
310
311 Image
312 *magnitude_image;
313
314 MagickBooleanType
315 status = MagickTrue;
316
317 ssize_t
318 y;
319
320 /*
321 Select the larger of two magnitudes.
322 */
323 magnitude_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
324 if (magnitude_image == (Image *) NULL)
325 return(magnitude_image);
326 alpha_view=AcquireVirtualCacheView(alpha_image,exception);
327 beta_view=AcquireVirtualCacheView(beta_image,exception);
328 dx_view=AcquireVirtualCacheView(dx_image,exception);
329 dy_view=AcquireVirtualCacheView(dy_image,exception);
330 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
331#if defined(MAGICKCORE_OPENMP_SUPPORT)
332 #pragma omp parallel for schedule(static) shared(status) \
333 magick_number_threads(alpha_image,magnitude_image,alpha_image->rows,1)
334#endif
335 for (y=0; y < (ssize_t) alpha_image->rows; y++)
336 {
337 const Quantum
338 *magick_restrict p,
339 *magick_restrict q,
340 *magick_restrict r,
341 *magick_restrict s;
342
343 Quantum
344 *magick_restrict t;
345
346 ssize_t
347 x;
348
349 if (status == MagickFalse)
350 continue;
351 p=GetCacheViewVirtualPixels(alpha_view,0,y,alpha_image->columns,1,
352 exception);
353 q=GetCacheViewVirtualPixels(beta_view,0,y,alpha_image->columns,1,exception);
354 r=GetCacheViewVirtualPixels(dx_view,0,y,alpha_image->columns,1,exception);
355 s=GetCacheViewVirtualPixels(dy_view,0,y,alpha_image->columns,1,exception);
356 t=GetCacheViewAuthenticPixels(magnitude_view,0,y,alpha_image->columns,1,
357 exception);
358 if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL) ||
359 (r == (const Quantum *) NULL) || (s == (const Quantum *) NULL) ||
360 (t == (Quantum *) NULL))
361 {
362 status=MagickFalse;
363 continue;
364 }
365 for (x=0; x < (ssize_t) alpha_image->columns; x++)
366 {
367 ssize_t
368 i;
369
370 for (i=0; i < (ssize_t) GetPixelChannels(alpha_image); i++)
371 {
372 PixelChannel channel = GetPixelChannelChannel(alpha_image,i);
373 PixelTrait traits = GetPixelChannelTraits(alpha_image,channel);
374 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
375 if ((traits == UndefinedPixelTrait) ||
376 (beta_traits == UndefinedPixelTrait) ||
377 ((beta_traits & UpdatePixelTrait) == 0))
378 continue;
379 if (p[i] > GetPixelChannel(beta_image,channel,q))
380 t[i]=GetPixelChannel(dx_image,channel,r);
381 else
382 t[i]=GetPixelChannel(dy_image,channel,s);
383 }
384 p+=(ptrdiff_t) GetPixelChannels(alpha_image);
385 q+=(ptrdiff_t) GetPixelChannels(beta_image);
386 r+=(ptrdiff_t) GetPixelChannels(dx_image);
387 s+=(ptrdiff_t) GetPixelChannels(dy_image);
388 t+=(ptrdiff_t) GetPixelChannels(magnitude_image);
389 }
390 if (SyncCacheViewAuthenticPixels(magnitude_view,exception) == MagickFalse)
391 status=MagickFalse;
392 }
393 magnitude_view=DestroyCacheView(magnitude_view);
394 dy_view=DestroyCacheView(dy_view);
395 dx_view=DestroyCacheView(dx_view);
396 beta_view=DestroyCacheView(beta_view);
397 alpha_view=DestroyCacheView(alpha_view);
398 if (status == MagickFalse)
399 magnitude_image=DestroyImage(magnitude_image);
400 return(magnitude_image);
401}
402
403static Image *BlendSumImage(const Image *alpha_image,const Image *beta_image,
404 const double attenuate,const double sign,ExceptionInfo *exception)
405{
407 *alpha_view,
408 *beta_view,
409 *sum_view;
410
411 Image
412 *sum_image;
413
414 MagickBooleanType
415 status = MagickTrue;
416
417 ssize_t
418 y;
419
420 /*
421 Add or subtract and optionally attenuate two images.
422 */
423 sum_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
424 if (sum_image == (Image *) NULL)
425 return(sum_image);
426 alpha_view=AcquireVirtualCacheView(alpha_image,exception);
427 beta_view=AcquireVirtualCacheView(beta_image,exception);
428 sum_view=AcquireAuthenticCacheView(sum_image,exception);
429#if defined(MAGICKCORE_OPENMP_SUPPORT)
430 #pragma omp parallel for schedule(static) shared(status) \
431 magick_number_threads(alpha_image,sum_image,alpha_image->rows,1)
432#endif
433 for (y=0; y < (ssize_t) alpha_image->rows; y++)
434 {
435 const Quantum
436 *magick_restrict p,
437 *magick_restrict q;
438
439 Quantum
440 *magick_restrict r;
441
442 ssize_t
443 x;
444
445 if (status == MagickFalse)
446 continue;
447 p=GetCacheViewVirtualPixels(alpha_view,0,y,alpha_image->columns,1,
448 exception);
449 q=GetCacheViewVirtualPixels(beta_view,0,y,alpha_image->columns,1,exception);
450 r=GetCacheViewAuthenticPixels(sum_view,0,y,alpha_image->columns,1,
451 exception);
452 if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL) ||
453 (r == (Quantum *) NULL))
454 {
455 status=MagickFalse;
456 continue;
457 }
458 for (x=0; x < (ssize_t) alpha_image->columns; x++)
459 {
460 ssize_t
461 i;
462
463 for (i=0; i < (ssize_t) GetPixelChannels(alpha_image); i++)
464 {
465 PixelChannel channel = GetPixelChannelChannel(alpha_image,i);
466 PixelTrait traits = GetPixelChannelTraits(alpha_image,channel);
467 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
468 if ((traits == UndefinedPixelTrait) ||
469 (beta_traits == UndefinedPixelTrait) ||
470 ((beta_traits & UpdatePixelTrait) == 0))
471 continue;
472 r[i]=ClampToQuantum(attenuate*((double) p[i]+sign*
473 (double) GetPixelChannel(beta_image,channel,q)));
474 }
475 p+=(ptrdiff_t) GetPixelChannels(alpha_image);
476 q+=(ptrdiff_t) GetPixelChannels(beta_image);
477 r+=(ptrdiff_t) GetPixelChannels(sum_image);
478 }
479 if (SyncCacheViewAuthenticPixels(sum_view,exception) == MagickFalse)
480 status=MagickFalse;
481 }
482 sum_view=DestroyCacheView(sum_view);
483 beta_view=DestroyCacheView(beta_view);
484 alpha_view=DestroyCacheView(alpha_view);
485 if (status == MagickFalse)
486 sum_image=DestroyImage(sum_image);
487 return(sum_image);
488}
489
490static Image *BlendDivergentImage(const Image *alpha_image,
491 const Image *beta_image,ExceptionInfo *exception)
492{
493#define FreeDivergentResources() \
494{ \
495 if (dy_image != (Image *) NULL) \
496 dy_image=DestroyImage(dy_image); \
497 if (dx_image != (Image *) NULL) \
498 dx_image=DestroyImage(dx_image); \
499 if (magnitude_beta != (Image *) NULL) \
500 magnitude_beta=DestroyImage(magnitude_beta); \
501 if (dy_beta != (Image *) NULL) \
502 dy_beta=DestroyImage(dy_beta); \
503 if (dx_beta != (Image *) NULL) \
504 dx_beta=DestroyImage(dx_beta); \
505 if (magnitude_alpha != (Image *) NULL) \
506 magnitude_alpha=DestroyImage(magnitude_alpha); \
507 if (dy_alpha != (Image *) NULL) \
508 dy_alpha=DestroyImage(dy_alpha); \
509 if (dx_alpha != (Image *) NULL) \
510 dx_alpha=DestroyImage(dx_alpha); \
511}
512
513 Image
514 *divergent_image = (Image *) NULL,
515 *dx_alpha = (Image *) NULL,
516 *dx_beta = (Image *) NULL,
517 *dx_divergent = (Image *) NULL,
518 *dx_image = (Image *) NULL,
519 *dy_alpha = (Image *) NULL,
520 *dy_beta = (Image *) NULL,
521 *dy_divergent = (Image *) NULL,
522 *dy_image = (Image *) NULL,
523 *magnitude_alpha = (Image *) NULL,
524 *magnitude_beta = (Image *) NULL;
525
526 /*
527 Create X and Y gradient images for alpha image and the magnitude.
528 */
529 dx_alpha=BlendConvolveImage(alpha_image,"3x1:-0.5,0.0,0.5",exception);
530 if (dx_alpha == (Image *) NULL)
531 {
532 FreeDivergentResources();
533 return((Image *) NULL);
534 }
535 dy_alpha=BlendConvolveImage(alpha_image,"1x3:-0.5,0.0,0.5",exception);
536 if (dy_alpha == (Image *) NULL)
537 {
538 FreeDivergentResources();
539 return((Image *) NULL);
540 }
541 magnitude_alpha=BlendMagnitudeImage(dx_alpha,dy_alpha,exception);
542 if (magnitude_alpha == (Image *) NULL)
543 {
544 FreeDivergentResources();
545 return((Image *) NULL);
546 }
547 /*
548 Create X and Y gradient images for beta and the magnitude.
549 */
550 dx_beta=BlendConvolveImage(beta_image,"3x1:-0.5,0.0,0.5",exception);
551 if (dx_beta == (Image *) NULL)
552 {
553 FreeDivergentResources();
554 return((Image *) NULL);
555 }
556 dy_beta=BlendConvolveImage(beta_image,"1x3:-0.5,0.0,0.5",exception);
557 if (dy_beta == (Image *) NULL)
558 {
559 FreeDivergentResources();
560 return((Image *) NULL);
561 }
562 magnitude_beta=BlendMagnitudeImage(dx_beta,dy_beta,exception);
563 if (magnitude_beta == (Image *) NULL)
564 {
565 FreeDivergentResources();
566 return((Image *) NULL);
567 }
568 /*
569 Select alpha or beta gradient for larger of two magnitudes.
570 */
571 dx_image=BlendMaxMagnitudeImage(magnitude_alpha,magnitude_beta,dx_alpha,
572 dx_beta,exception);
573 if (dx_image == (Image *) NULL)
574 {
575 FreeDivergentResources();
576 return((Image *) NULL);
577 }
578 dy_image=BlendMaxMagnitudeImage(magnitude_alpha,magnitude_beta,dy_alpha,
579 dy_beta,exception);
580 if (dy_image == (Image *) NULL)
581 {
582 FreeDivergentResources();
583 return((Image *) NULL);
584 }
585 dx_beta=DestroyImage(dx_beta);
586 dx_alpha=DestroyImage(dx_alpha);
587 magnitude_beta=DestroyImage(magnitude_beta);
588 magnitude_alpha=DestroyImage(magnitude_alpha);
589 /*
590 Create divergence of gradients dx and dy and divide by 4 as guide image.
591 */
592 dx_divergent=BlendConvolveImage(dx_image,"3x1:-0.5,0.0,0.5",exception);
593 if (dx_divergent == (Image *) NULL)
594 {
595 FreeDivergentResources();
596 return((Image *) NULL);
597 }
598 dy_divergent=BlendConvolveImage(dy_image,"1x3:-0.5,0.0,0.5",exception);
599 if (dy_divergent == (Image *) NULL)
600 {
601 FreeDivergentResources();
602 return((Image *) NULL);
603 }
604 divergent_image=BlendSumImage(dx_divergent,dy_divergent,0.25,1.0,exception);
605 dy_divergent=DestroyImage(dy_divergent);
606 dx_divergent=DestroyImage(dx_divergent);
607 if (divergent_image == (Image *) NULL)
608 {
609 FreeDivergentResources();
610 return((Image *) NULL);
611 }
612 FreeDivergentResources();
613 return(divergent_image);
614}
615
616static MagickBooleanType BlendMaskAlphaChannel(Image *image,
617 const Image *mask_image,ExceptionInfo *exception)
618{
620 *image_view,
621 *mask_view;
622
623 MagickBooleanType
624 status = MagickTrue;
625
626 ssize_t
627 y;
628
629 /*
630 Threshold the alpha channel.
631 */
632 if (SetImageAlpha(image,OpaqueAlpha,exception) == MagickFalse)
633 return(MagickFalse);
634 image_view=AcquireAuthenticCacheView(image,exception);
635 mask_view=AcquireVirtualCacheView(mask_image,exception);
636#if defined(MAGICKCORE_OPENMP_SUPPORT)
637 #pragma omp parallel for schedule(static) shared(status) \
638 magick_number_threads(image,image,image->rows,2)
639#endif
640 for (y=0; y < (ssize_t) image->rows; y++)
641 {
642 const Quantum
643 *magick_restrict p;
644
645 Quantum
646 *magick_restrict q;
647
648 ssize_t
649 x;
650
651 if (status == MagickFalse)
652 continue;
653 p=GetCacheViewVirtualPixels(mask_view,0,y,image->columns,1,exception);
654 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
655 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
656 {
657 status=MagickFalse;
658 continue;
659 }
660 for (x=0; x < (ssize_t) image->columns; x++)
661 {
662 Quantum
663 alpha = GetPixelAlpha(mask_image,p);
664
665 ssize_t
666 i = GetPixelChannelOffset(image,AlphaPixelChannel);
667
668 if (fabs((double) alpha) >= MagickEpsilon)
669 q[i]=(Quantum) 0;
670 p+=(ptrdiff_t) GetPixelChannels(mask_image);
671 q+=(ptrdiff_t) GetPixelChannels(image);
672 }
673 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
674 status=MagickFalse;
675 }
676 mask_view=DestroyCacheView(mask_view);
677 image_view=DestroyCacheView(image_view);
678 return(status);
679}
680
681static Image *BlendMeanImage(Image *image,const Image *mask_image,
682 ExceptionInfo *exception)
683{
685 *alpha_view,
686 *mask_view,
687 *mean_view;
688
689 double
690 mean[MaxPixelChannels];
691
692 Image
693 *mean_image;
694
695 MagickBooleanType
696 status = MagickTrue;
697
698 ssize_t
699 j,
700 y;
701
702 /*
703 Compute the mean of the image.
704 */
705 (void) memset(mean,0,MaxPixelChannels*sizeof(*mean));
706 alpha_view=AcquireVirtualCacheView(image,exception);
707 for (y=0; y < (ssize_t) image->rows; y++)
708 {
709 const Quantum
710 *magick_restrict p;
711
712 ssize_t
713 x;
714
715 p=GetCacheViewVirtualPixels(alpha_view,0,y,image->columns,1,
716 exception);
717 if (p == (const Quantum *) NULL)
718 break;
719 for (x=0; x < (ssize_t) image->columns; x++)
720 {
721 ssize_t
722 i;
723
724 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
725 {
726 PixelChannel channel = GetPixelChannelChannel(image,i);
727 PixelTrait traits = GetPixelChannelTraits(image,channel);
728 if (traits == UndefinedPixelTrait)
729 continue;
730 mean[i]+=QuantumScale*(double) p[i];
731 }
732 p+=(ptrdiff_t) GetPixelChannels(image);
733 }
734 }
735 alpha_view=DestroyCacheView(alpha_view);
736 if (y < (ssize_t) image->rows)
737 return((Image *) NULL);
738 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
739 mean[j]=(double) QuantumRange*mean[j]/image->columns/
740 image->rows;
741 /*
742 Replace any unmasked pixels with the mean pixel.
743 */
744 mean_image=CloneImage(image,0,0,MagickTrue,exception);
745 if (mean_image == (Image *) NULL)
746 return(mean_image);
747 mask_view=AcquireVirtualCacheView(mask_image,exception);
748 mean_view=AcquireAuthenticCacheView(mean_image,exception);
749#if defined(MAGICKCORE_OPENMP_SUPPORT)
750 #pragma omp parallel for schedule(static) shared(status) \
751 magick_number_threads(mask_image,mean_image,mean_image->rows,4)
752#endif
753 for (y=0; y < (ssize_t) mean_image->rows; y++)
754 {
755 const Quantum
756 *magick_restrict p;
757
758 Quantum
759 *magick_restrict q;
760
761 ssize_t
762 x;
763
764 if (status == MagickFalse)
765 continue;
766 p=GetCacheViewVirtualPixels(mask_view,0,y,mean_image->columns,1,exception);
767 q=GetCacheViewAuthenticPixels(mean_view,0,y,mean_image->columns,1,
768 exception);
769 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
770 {
771 status=MagickFalse;
772 continue;
773 }
774 for (x=0; x < (ssize_t) mean_image->columns; x++)
775 {
776 Quantum
777 alpha = GetPixelAlpha(mask_image,p),
778 mask = GetPixelReadMask(mask_image,p);
779
780 ssize_t
781 i;
782
783 for (i=0; i < (ssize_t) GetPixelChannels(mean_image); i++)
784 {
785 PixelChannel channel = GetPixelChannelChannel(mean_image,i);
786 PixelTrait traits = GetPixelChannelTraits(mean_image,channel);
787 if (traits == UndefinedPixelTrait)
788 continue;
789 if (mask <= (QuantumRange/2))
790 q[i]=(Quantum) 0;
791 else
792 if (fabs((double) alpha) >= MagickEpsilon)
793 q[i]=ClampToQuantum(mean[i]);
794 }
795 p+=(ptrdiff_t) GetPixelChannels(mask_image);
796 q+=(ptrdiff_t) GetPixelChannels(mean_image);
797 }
798 if (SyncCacheViewAuthenticPixels(mean_view,exception) == MagickFalse)
799 status=MagickFalse;
800 }
801 mask_view=DestroyCacheView(mask_view);
802 mean_view=DestroyCacheView(mean_view);
803 if (status == MagickFalse)
804 mean_image=DestroyImage(mean_image);
805 return(mean_image);
806}
807
808static MagickBooleanType BlendRMSEResidual(const Image *alpha_image,
809 const Image *beta_image,double *residual,ExceptionInfo *exception)
810{
812 *alpha_view,
813 *beta_view;
814
815 double
816 area = 0.0;
817
818 MagickBooleanType
819 status = MagickTrue;
820
821 size_t
822 columns = MagickMax(alpha_image->columns,beta_image->columns),
823 rows = MagickMax(alpha_image->rows,beta_image->rows);
824
825 ssize_t
826 y;
827
828 *residual=0.0;
829 alpha_view=AcquireVirtualCacheView(alpha_image,exception);
830 beta_view=AcquireVirtualCacheView(beta_image,exception);
831#if defined(MAGICKCORE_OPENMP_SUPPORT)
832 #pragma omp parallel for schedule(static) shared(status) \
833 magick_number_threads(alpha_image,alpha_image,rows,1)
834#endif
835 for (y=0; y < (ssize_t) rows; y++)
836 {
837 const Quantum
838 *magick_restrict p,
839 *magick_restrict q;
840
841 double
842 channel_residual;
843
844 size_t
845 local_area = 0;
846
847 ssize_t
848 x;
849
850 if (status == MagickFalse)
851 continue;
852 p=GetCacheViewVirtualPixels(alpha_view,0,y,columns,1,exception);
853 q=GetCacheViewVirtualPixels(beta_view,0,y,columns,1,exception);
854 if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL))
855 {
856 status=MagickFalse;
857 continue;
858 }
859 channel_residual=0.0;
860 for (x=0; x < (ssize_t) columns; x++)
861 {
862 double
863 Da,
864 Sa;
865
866 ssize_t
867 i;
868
869 if ((GetPixelReadMask(alpha_image,p) <= (QuantumRange/2)) ||
870 (GetPixelReadMask(beta_image,q) <= (QuantumRange/2)))
871 {
872 p+=(ptrdiff_t) GetPixelChannels(alpha_image);
873 q+=(ptrdiff_t) GetPixelChannels(beta_image);
874 continue;
875 }
876 Sa=QuantumScale*(double) GetPixelAlpha(alpha_image,p);
877 Da=QuantumScale*(double) GetPixelAlpha(beta_image,q);
878 for (i=0; i < (ssize_t) GetPixelChannels(alpha_image); i++)
879 {
880 double
881 distance;
882
883 PixelChannel channel = GetPixelChannelChannel(alpha_image,i);
884 PixelTrait traits = GetPixelChannelTraits(alpha_image,channel);
885 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
886 if ((traits == UndefinedPixelTrait) ||
887 (beta_traits == UndefinedPixelTrait) ||
888 ((beta_traits & UpdatePixelTrait) == 0))
889 continue;
890 if (channel == AlphaPixelChannel)
891 distance=QuantumScale*((double) p[i]-(double) GetPixelChannel(
892 beta_image,channel,q));
893 else
894 distance=QuantumScale*(Sa*(double) p[i]-Da*(double) GetPixelChannel(
895 beta_image,channel,q));
896 channel_residual+=distance*distance;
897 }
898 local_area++;
899 p+=(ptrdiff_t) GetPixelChannels(alpha_image);
900 q+=(ptrdiff_t) GetPixelChannels(beta_image);
901 }
902#if defined(MAGICKCORE_OPENMP_SUPPORT)
903 #pragma omp critical (MagickCore_BlendRMSEResidual)
904#endif
905 {
906 area+=local_area;
907 *residual+=channel_residual;
908 }
909 }
910 area=PerceptibleReciprocal(area);
911 beta_view=DestroyCacheView(beta_view);
912 alpha_view=DestroyCacheView(alpha_view);
913 *residual=sqrt(*residual*area/(double) GetImageChannels(alpha_image));
914 return(status);
915}
916
917static MagickBooleanType CompositeOverImage(Image *image,
918 const Image *source_image,const MagickBooleanType clip_to_self,
919 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
920{
921#define CompositeImageTag "Composite/Image"
922
924 *image_view,
925 *source_view;
926
927 const char
928 *value;
929
930 MagickBooleanType
931 clamp,
932 status;
933
934 MagickOffsetType
935 progress;
936
937 ssize_t
938 y;
939
940 /*
941 Composite image.
942 */
943 status=MagickTrue;
944 progress=0;
945 clamp=MagickTrue;
946 value=GetImageArtifact(image,"compose:clamp");
947 if (value != (const char *) NULL)
948 clamp=IsStringTrue(value);
949 status=MagickTrue;
950 progress=0;
951 source_view=AcquireVirtualCacheView(source_image,exception);
952 image_view=AcquireAuthenticCacheView(image,exception);
953#if defined(MAGICKCORE_OPENMP_SUPPORT)
954 #pragma omp parallel for schedule(static) shared(progress,status) \
955 magick_number_threads(source_image,image,image->rows,1)
956#endif
957 for (y=0; y < (ssize_t) image->rows; y++)
958 {
959 const Quantum
960 *pixels;
961
963 canvas_pixel,
964 source_pixel;
965
966 const Quantum
967 *magick_restrict p;
968
969 Quantum
970 *magick_restrict q;
971
972 ssize_t
973 x;
974
975 if (status == MagickFalse)
976 continue;
977 if (clip_to_self != MagickFalse)
978 {
979 if (y < y_offset)
980 continue;
981 if ((y-(double) y_offset) >= (double) source_image->rows)
982 continue;
983 }
984 /*
985 If pixels is NULL, y is outside overlay region.
986 */
987 pixels=(Quantum *) NULL;
988 p=(Quantum *) NULL;
989 if ((y >= y_offset) &&
990 ((y-(double) y_offset) < (double) source_image->rows))
991 {
992 p=GetCacheViewVirtualPixels(source_view,0,
993 CastDoubleToLong(y-(double) y_offset),source_image->columns,1,
994 exception);
995 if (p == (const Quantum *) NULL)
996 {
997 status=MagickFalse;
998 continue;
999 }
1000 pixels=p;
1001 if (x_offset < 0)
1002 p-=(ptrdiff_t)CastDoubleToLong((double) x_offset*GetPixelChannels(source_image));
1003 }
1004 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1005 if (q == (Quantum *) NULL)
1006 {
1007 status=MagickFalse;
1008 continue;
1009 }
1010 GetPixelInfo(image,&canvas_pixel);
1011 GetPixelInfo(source_image,&source_pixel);
1012 for (x=0; x < (ssize_t) image->columns; x++)
1013 {
1014 double
1015 gamma;
1016
1017 MagickRealType
1018 alpha,
1019 Da,
1020 Dc,
1021 Dca,
1022 Sa,
1023 Sc,
1024 Sca;
1025
1026 ssize_t
1027 i;
1028
1029 size_t
1030 channels;
1031
1032 if (clip_to_self != MagickFalse)
1033 {
1034 if (x < x_offset)
1035 {
1036 q+=(ptrdiff_t) GetPixelChannels(image);
1037 continue;
1038 }
1039 if ((x-(double) x_offset) >= (double) source_image->columns)
1040 break;
1041 }
1042 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1043 ((x-(double) x_offset) >= (double) source_image->columns))
1044 {
1045 Quantum
1046 source[MaxPixelChannels];
1047
1048 /*
1049 Virtual composite:
1050 Sc: source color.
1051 Dc: canvas color.
1052 */
1053 (void) GetOneVirtualPixel(source_image,
1054 CastDoubleToLong(x-(double) x_offset),
1055 CastDoubleToLong(y-(double) y_offset),source,exception);
1056 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1057 {
1058 MagickRealType
1059 pixel;
1060
1061 PixelChannel channel = GetPixelChannelChannel(image,i);
1062 PixelTrait traits = GetPixelChannelTraits(image,channel);
1063 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1064 channel);
1065 if ((traits == UndefinedPixelTrait) ||
1066 (source_traits == UndefinedPixelTrait))
1067 continue;
1068 if (channel == AlphaPixelChannel)
1069 pixel=(MagickRealType) TransparentAlpha;
1070 else
1071 pixel=(MagickRealType) q[i];
1072 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1073 ClampToQuantum(pixel);
1074 }
1075 q+=(ptrdiff_t) GetPixelChannels(image);
1076 continue;
1077 }
1078 /*
1079 Authentic composite:
1080 Sa: normalized source alpha.
1081 Da: normalized canvas alpha.
1082 */
1083 Sa=QuantumScale*(double) GetPixelAlpha(source_image,p);
1084 Da=QuantumScale*(double) GetPixelAlpha(image,q);
1085 alpha=Sa+Da-Sa*Da;
1086 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1087 {
1088 MagickRealType
1089 pixel;
1090
1091 PixelChannel channel = GetPixelChannelChannel(image,i);
1092 PixelTrait traits = GetPixelChannelTraits(image,channel);
1093 PixelTrait source_traits=GetPixelChannelTraits(source_image,channel);
1094 if (traits == UndefinedPixelTrait)
1095 continue;
1096 if ((source_traits == UndefinedPixelTrait) &&
1097 (channel != AlphaPixelChannel))
1098 continue;
1099 if (channel == AlphaPixelChannel)
1100 {
1101 /*
1102 Set alpha channel.
1103 */
1104 pixel=(double) QuantumRange*alpha;
1105 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1106 ClampToQuantum(pixel);
1107 continue;
1108 }
1109 /*
1110 Sc: source color.
1111 Dc: canvas color.
1112 */
1113 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1114 Dc=(MagickRealType) q[i];
1115 if ((traits & CopyPixelTrait) != 0)
1116 {
1117 /*
1118 Copy channel.
1119 */
1120 q[i]=ClampToQuantum(Sc);
1121 continue;
1122 }
1123 /*
1124 Porter-Duff compositions:
1125 Sca: source normalized color multiplied by alpha.
1126 Dca: normalized canvas color multiplied by alpha.
1127 */
1128 Sca=QuantumScale*Sa*Sc;
1129 Dca=QuantumScale*Da*Dc;
1130 gamma=PerceptibleReciprocal(alpha);
1131 pixel=(double) QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
1132 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
1133 }
1134 p+=(ptrdiff_t) GetPixelChannels(source_image);
1135 channels=GetPixelChannels(source_image);
1136 if (p >= (pixels+channels*source_image->columns))
1137 p=pixels;
1138 q+=(ptrdiff_t) GetPixelChannels(image);
1139 }
1140 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1141 status=MagickFalse;
1142 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1143 {
1144 MagickBooleanType
1145 proceed;
1146
1147#if defined(MAGICKCORE_OPENMP_SUPPORT)
1148 #pragma omp atomic
1149#endif
1150 progress++;
1151 proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
1152 if (proceed == MagickFalse)
1153 status=MagickFalse;
1154 }
1155 }
1156 source_view=DestroyCacheView(source_view);
1157 image_view=DestroyCacheView(image_view);
1158 return(status);
1159}
1160
1161static MagickBooleanType SaliencyBlendImage(Image *image,
1162 const Image *source_image,const ssize_t x_offset,const ssize_t y_offset,
1163 const double iterations,const double residual_threshold,const size_t tick,
1164 ExceptionInfo *exception)
1165{
1166 Image
1167 *crop_image,
1168 *divergent_image,
1169 *relax_image,
1170 *residual_image = (Image *) NULL;
1171
1173 *kernel_info;
1174
1175 MagickBooleanType
1176 status = MagickTrue,
1177 verbose = MagickFalse;
1178
1180 crop_info = {
1181 source_image->columns,
1182 source_image->rows,
1183 x_offset,
1184 y_offset
1185 };
1186
1187 size_t
1188 i;
1189
1190 /*
1191 Saliency blend composite operator.
1192 */
1193 crop_image=CropImage(image,&crop_info,exception);
1194 if (crop_image == (Image *) NULL)
1195 return(MagickFalse);
1196 DisableCompositeClampUnlessSpecified(crop_image);
1197 divergent_image=BlendDivergentImage(crop_image,source_image,exception);
1198 if (divergent_image == (Image *) NULL)
1199 {
1200 crop_image=DestroyImage(crop_image);
1201 return(MagickFalse);
1202 }
1203 (void) ResetImagePage(crop_image,"0x0+0+0");
1204 relax_image=BlendMeanImage(crop_image,source_image,exception);
1205 if (relax_image == (Image *) NULL)
1206 {
1207 crop_image=DestroyImage(crop_image);
1208 divergent_image=DestroyImage(divergent_image);
1209 return(MagickFalse);
1210 }
1211 status=BlendMaskAlphaChannel(crop_image,source_image,exception);
1212 if (status == MagickFalse)
1213 {
1214 crop_image=DestroyImage(crop_image);
1215 divergent_image=DestroyImage(divergent_image);
1216 return(MagickFalse);
1217 }
1218 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1219 if (residual_image == (Image *) NULL)
1220 {
1221 crop_image=DestroyImage(crop_image);
1222 relax_image=DestroyImage(relax_image);
1223 return(MagickFalse);
1224 }
1225 /*
1226 Convolve relaxed image and blur area of interest.
1227 */
1228 kernel_info=AcquireKernelInfo("3x3:0,0.25,0,0.25,0,0.25,0,0.25,0",exception);
1229 if (kernel_info == (KernelInfo *) NULL)
1230 {
1231 crop_image=DestroyImage(crop_image);
1232 residual_image=DestroyImage(residual_image);
1233 relax_image=DestroyImage(relax_image);
1234 return(MagickFalse);
1235 }
1236 verbose=IsStringTrue(GetImageArtifact(image,"verbose"));
1237 if (verbose != MagickFalse)
1238 (void) FormatLocaleFile(stderr,"saliency blending:\n");
1239 for (i=0; i < iterations; i++)
1240 {
1241 double
1242 residual = 1.0;
1243
1244 Image
1245 *convolve_image,
1246 *sum_image;
1247
1248 convolve_image=ConvolveImage(relax_image,kernel_info,exception);
1249 if (convolve_image == (Image *) NULL)
1250 break;
1251 relax_image=DestroyImage(relax_image);
1252 relax_image=convolve_image;
1253 sum_image=BlendSumImage(relax_image,divergent_image,1.0,-1.0,exception);
1254 if (sum_image == (Image *) NULL)
1255 break;
1256 relax_image=DestroyImage(relax_image);
1257 relax_image=sum_image;
1258 status=CompositeOverImage(relax_image,crop_image,MagickTrue,0,0,exception);
1259 if (status == MagickFalse)
1260 break;
1261 status=BlendRMSEResidual(relax_image,residual_image,&residual,exception);
1262 if (status == MagickFalse)
1263 break;
1264 if ((verbose != MagickFalse) && ((i % MagickMax(tick,1)) == 0))
1265 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double) residual);
1266 if (residual < residual_threshold)
1267 {
1268 if (verbose != MagickFalse)
1269 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double)
1270 residual);
1271 break;
1272 }
1273 residual_image=DestroyImage(residual_image);
1274 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1275 if (residual_image == (Image *) NULL)
1276 break;
1277 }
1278 kernel_info=DestroyKernelInfo(kernel_info);
1279 crop_image=DestroyImage(crop_image);
1280 divergent_image=DestroyImage(divergent_image);
1281 residual_image=DestroyImage(residual_image);
1282 /*
1283 Composite relaxed over the background image.
1284 */
1285 status=CompositeOverImage(image,relax_image,MagickTrue,x_offset,y_offset,
1286 exception);
1287 relax_image=DestroyImage(relax_image);
1288 return(status);
1289}
1290
1291static MagickBooleanType SeamlessBlendImage(Image *image,
1292 const Image *source_image,const ssize_t x_offset,const ssize_t y_offset,
1293 const double iterations,const double residual_threshold,const size_t tick,
1294 ExceptionInfo *exception)
1295{
1296 Image
1297 *crop_image,
1298 *foreground_image,
1299 *mean_image,
1300 *relax_image,
1301 *residual_image,
1302 *sum_image;
1303
1305 *kernel_info;
1306
1307 MagickBooleanType
1308 status = MagickTrue,
1309 verbose = MagickFalse;
1310
1312 crop_info = {
1313 source_image->columns,
1314 source_image->rows,
1315 x_offset,
1316 y_offset
1317 };
1318
1319 size_t
1320 i;
1321
1322 /*
1323 Seamless blend composite operator.
1324 */
1325 crop_image=CropImage(image,&crop_info,exception);
1326 if (crop_image == (Image *) NULL)
1327 return(MagickFalse);
1328 DisableCompositeClampUnlessSpecified(crop_image);
1329 (void) ResetImagePage(crop_image,"0x0+0+0");
1330 sum_image=BlendSumImage(crop_image,source_image,1.0,-1.0,exception);
1331 crop_image=DestroyImage(crop_image);
1332 if (sum_image == (Image *) NULL)
1333 return(MagickFalse);
1334 mean_image=BlendMeanImage(sum_image,source_image,exception);
1335 sum_image=DestroyImage(sum_image);
1336 if (mean_image == (Image *) NULL)
1337 return(MagickFalse);
1338 relax_image=CloneImage(mean_image,0,0,MagickTrue,exception);
1339 if (relax_image == (Image *) NULL)
1340 {
1341 mean_image=DestroyImage(mean_image);
1342 return(MagickFalse);
1343 }
1344 status=BlendMaskAlphaChannel(mean_image,source_image,exception);
1345 if (status == MagickFalse)
1346 {
1347 relax_image=DestroyImage(relax_image);
1348 mean_image=DestroyImage(mean_image);
1349 return(MagickFalse);
1350 }
1351 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1352 if (residual_image == (Image *) NULL)
1353 {
1354 relax_image=DestroyImage(relax_image);
1355 mean_image=DestroyImage(mean_image);
1356 return(MagickFalse);
1357 }
1358 /*
1359 Convolve relaxed image and blur area of interest.
1360 */
1361 kernel_info=AcquireKernelInfo("3x3:0,0.25,0,0.25,0,0.25,0,0.25,0",exception);
1362 if (kernel_info == (KernelInfo *) NULL)
1363 {
1364 residual_image=DestroyImage(residual_image);
1365 relax_image=DestroyImage(relax_image);
1366 mean_image=DestroyImage(mean_image);
1367 return(MagickFalse);
1368 }
1369 verbose=IsStringTrue(GetImageArtifact(image,"verbose"));
1370 if (verbose != MagickFalse)
1371 (void) FormatLocaleFile(stderr,"seamless blending:\n");
1372 for (i=0; i < iterations; i++)
1373 {
1374 double
1375 residual = 1.0;
1376
1377 Image
1378 *convolve_image;
1379
1380 convolve_image=ConvolveImage(relax_image,kernel_info,exception);
1381 if (convolve_image == (Image *) NULL)
1382 break;
1383 relax_image=DestroyImage(relax_image);
1384 relax_image=convolve_image;
1385 status=CompositeOverImage(relax_image,mean_image,MagickTrue,0,0,exception);
1386 if (status == MagickFalse)
1387 break;
1388 status=BlendRMSEResidual(relax_image,residual_image,&residual,exception);
1389 if (status == MagickFalse)
1390 break;
1391 if ((verbose != MagickFalse) && ((i % MagickMax(tick,1)) == 0))
1392 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double) residual);
1393 if (residual < residual_threshold)
1394 {
1395 if (verbose != MagickFalse)
1396 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double)
1397 residual);
1398 break;
1399 }
1400 if (residual_image != (Image *) NULL)
1401 residual_image=DestroyImage(residual_image);
1402 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1403 if (residual_image == (Image *) NULL)
1404 break;
1405 }
1406 kernel_info=DestroyKernelInfo(kernel_info);
1407 mean_image=DestroyImage(mean_image);
1408 residual_image=DestroyImage(residual_image);
1409 /*
1410 Composite the foreground image over the background image.
1411 */
1412 foreground_image=BlendSumImage(source_image,relax_image,1.0,1.0,exception);
1413 relax_image=DestroyImage(relax_image);
1414 if (foreground_image == (Image *) NULL)
1415 return(MagickFalse);
1416 (void) SetImageMask(foreground_image,ReadPixelMask,(const Image *) NULL,
1417 exception);
1418 status=CompositeOverImage(image,foreground_image,MagickTrue,x_offset,y_offset,
1419 exception);
1420 foreground_image=DestroyImage(foreground_image);
1421 return(status);
1422}
1423
1424MagickExport MagickBooleanType CompositeImage(Image *image,
1425 const Image *composite,const CompositeOperator compose,
1426 const MagickBooleanType clip_to_self,const ssize_t x_offset,
1427 const ssize_t y_offset,ExceptionInfo *exception)
1428{
1429#define CompositeImageTag "Composite/Image"
1430
1431 CacheView
1432 *source_view,
1433 *image_view;
1434
1435 ColorspaceType
1436 colorspace = HCLColorspace;
1437
1438 const char
1439 *artifact;
1440
1441 double
1442 white_luminance = 10000.0;
1443
1445 geometry_info;
1446
1447 IlluminantType
1448 illuminant = D65Illuminant;
1449
1450 Image
1451 *canvas_image,
1452 *source_image;
1453
1454 MagickBooleanType
1455 clamp,
1456 compose_sync,
1457 status;
1458
1459 MagickOffsetType
1460 progress;
1461
1462 MagickRealType
1463 amount,
1464 canvas_dissolve,
1465 midpoint,
1466 percent_luma,
1467 percent_chroma,
1468 source_dissolve,
1469 threshold;
1470
1471 MagickStatusType
1472 flags;
1473
1474 ssize_t
1475 y;
1476
1477 assert(image != (Image *) NULL);
1478 assert(image->signature == MagickCoreSignature);
1479 assert(composite != (Image *) NULL);
1480 assert(composite->signature == MagickCoreSignature);
1481 if (IsEventLogging() != MagickFalse)
1482 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1483 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1484 return(MagickFalse);
1485 source_image=CloneImage(composite,0,0,MagickTrue,exception);
1486 if (source_image == (const Image *) NULL)
1487 return(MagickFalse);
1488 (void) SetImageColorspace(source_image,image->colorspace,exception);
1489 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
1490 {
1491 status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
1492 y_offset,exception);
1493 source_image=DestroyImage(source_image);
1494 return(status);
1495 }
1496 amount=0.5;
1497 canvas_image=(Image *) NULL;
1498 canvas_dissolve=1.0;
1499 white_luminance=10000.0;
1500 artifact=GetImageArtifact(image,"compose:white-luminance");
1501 if (artifact != (const char *) NULL)
1502 white_luminance=StringToDouble(artifact,(char **) NULL);
1503 artifact=GetImageArtifact(image,"compose:illuminant");
1504 if (artifact != (const char *) NULL)
1505 {
1506 ssize_t
1507 illuminant_type;
1508
1509 illuminant_type=ParseCommandOption(MagickIlluminantOptions,MagickFalse,
1510 artifact);
1511 if (illuminant_type < 0)
1512 illuminant=UndefinedIlluminant;
1513 else
1514 illuminant=(IlluminantType) illuminant_type;
1515 }
1516 artifact=GetImageArtifact(image,"compose:colorspace");
1517 if (artifact != (const char *) NULL)
1518 {
1519 ssize_t
1520 colorspace_type;
1521
1522 colorspace_type=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
1523 artifact);
1524 if (colorspace_type < 0)
1525 colorspace=UndefinedColorspace;
1526 else
1527 colorspace=(ColorspaceType) colorspace_type;
1528 }
1529 clamp=MagickTrue;
1530 artifact=GetImageArtifact(image,"compose:clamp");
1531 if (artifact != (const char *) NULL)
1532 clamp=IsStringTrue(artifact);
1533 compose_sync=MagickTrue;
1534 artifact=GetImageArtifact(image,"compose:sync");
1535 if (artifact != (const char *) NULL)
1536 compose_sync=IsStringTrue(artifact);
1537 SetGeometryInfo(&geometry_info);
1538 percent_luma=100.0;
1539 percent_chroma=100.0;
1540 source_dissolve=1.0;
1541 threshold=0.05f;
1542 switch (compose)
1543 {
1544 case CopyCompositeOp:
1545 {
1546 if ((x_offset < 0) || (y_offset < 0))
1547 break;
1548 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
1549 break;
1550 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
1551 break;
1552 if ((source_image->alpha_trait == UndefinedPixelTrait) &&
1553 (image->alpha_trait != UndefinedPixelTrait))
1554 (void) SetImageAlphaChannel(source_image,OpaqueAlphaChannel,exception);
1555 status=MagickTrue;
1556 source_view=AcquireVirtualCacheView(source_image,exception);
1557 image_view=AcquireAuthenticCacheView(image,exception);
1558#if defined(MAGICKCORE_OPENMP_SUPPORT)
1559 #pragma omp parallel for schedule(static) shared(status) \
1560 magick_number_threads(source_image,image,source_image->rows,4)
1561#endif
1562 for (y=0; y < (ssize_t) source_image->rows; y++)
1563 {
1564 MagickBooleanType
1565 sync;
1566
1567 const Quantum
1568 *p;
1569
1570 Quantum
1571 *q;
1572
1573 ssize_t
1574 x;
1575
1576 if (status == MagickFalse)
1577 continue;
1578 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1579 exception);
1580 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1581 source_image->columns,1,exception);
1582 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1583 {
1584 status=MagickFalse;
1585 continue;
1586 }
1587 for (x=0; x < (ssize_t) source_image->columns; x++)
1588 {
1589 ssize_t
1590 i;
1591
1592 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
1593 {
1594 p+=(ptrdiff_t) GetPixelChannels(source_image);
1595 q+=(ptrdiff_t) GetPixelChannels(image);
1596 continue;
1597 }
1598 for (i=0; i < (ssize_t) GetPixelChannels(source_image); i++)
1599 {
1600 PixelChannel channel = GetPixelChannelChannel(source_image,i);
1601 PixelTrait source_traits = GetPixelChannelTraits(source_image,
1602 channel);
1603 PixelTrait traits = GetPixelChannelTraits(image,channel);
1604 if ((source_traits == UndefinedPixelTrait) ||
1605 (traits == UndefinedPixelTrait))
1606 continue;
1607 SetPixelChannel(image,channel,p[i],q);
1608 }
1609 p+=(ptrdiff_t) GetPixelChannels(source_image);
1610 q+=(ptrdiff_t) GetPixelChannels(image);
1611 }
1612 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1613 if (sync == MagickFalse)
1614 status=MagickFalse;
1615 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1616 {
1617 MagickBooleanType
1618 proceed;
1619
1620 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
1621 y,image->rows);
1622 if (proceed == MagickFalse)
1623 status=MagickFalse;
1624 }
1625 }
1626 source_view=DestroyCacheView(source_view);
1627 image_view=DestroyCacheView(image_view);
1628 source_image=DestroyImage(source_image);
1629 return(status);
1630 }
1631 case IntensityCompositeOp:
1632 {
1633 if ((x_offset < 0) || (y_offset < 0))
1634 break;
1635 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
1636 break;
1637 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
1638 break;
1639 status=MagickTrue;
1640 source_view=AcquireVirtualCacheView(source_image,exception);
1641 image_view=AcquireAuthenticCacheView(image,exception);
1642#if defined(MAGICKCORE_OPENMP_SUPPORT)
1643 #pragma omp parallel for schedule(static) shared(status) \
1644 magick_number_threads(source_image,image,source_image->rows,4)
1645#endif
1646 for (y=0; y < (ssize_t) source_image->rows; y++)
1647 {
1648 MagickBooleanType
1649 sync;
1650
1651 const Quantum
1652 *p;
1653
1654 Quantum
1655 *q;
1656
1657 ssize_t
1658 x;
1659
1660 if (status == MagickFalse)
1661 continue;
1662 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1663 exception);
1664 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1665 source_image->columns,1,exception);
1666 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1667 {
1668 status=MagickFalse;
1669 continue;
1670 }
1671 for (x=0; x < (ssize_t) source_image->columns; x++)
1672 {
1673 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
1674 {
1675 p+=(ptrdiff_t) GetPixelChannels(source_image);
1676 q+=(ptrdiff_t) GetPixelChannels(image);
1677 continue;
1678 }
1679 SetPixelAlpha(image,clamp != MagickFalse ?
1680 ClampPixel(GetPixelIntensity(source_image,p)) :
1681 ClampToQuantum(GetPixelIntensity(source_image,p)),q);
1682 p+=(ptrdiff_t) GetPixelChannels(source_image);
1683 q+=(ptrdiff_t) GetPixelChannels(image);
1684 }
1685 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1686 if (sync == MagickFalse)
1687 status=MagickFalse;
1688 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1689 {
1690 MagickBooleanType
1691 proceed;
1692
1693 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
1694 y,image->rows);
1695 if (proceed == MagickFalse)
1696 status=MagickFalse;
1697 }
1698 }
1699 source_view=DestroyCacheView(source_view);
1700 image_view=DestroyCacheView(image_view);
1701 source_image=DestroyImage(source_image);
1702 return(status);
1703 }
1704 case CopyAlphaCompositeOp:
1705 case ChangeMaskCompositeOp:
1706 {
1707 /*
1708 Modify canvas outside the overlaid region and require an alpha
1709 channel to exist, to add transparency.
1710 */
1711 if ((image->alpha_trait & BlendPixelTrait) == 0)
1712 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1713 break;
1714 }
1715 case BlurCompositeOp:
1716 {
1717 CacheView
1718 *canvas_view;
1719
1720 double
1721 angle_range,
1722 angle_start,
1723 height,
1724 width;
1725
1726 PixelInfo
1727 pixel;
1728
1730 *resample_filter;
1731
1733 blur;
1734
1735 /*
1736 Blur Image by resampling dictated by an overlay gradient map:
1737 X = red_channel; Y = green_channel; compose:args =
1738 x_scale[,y_scale[,angle]].
1739 */
1740 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
1741 if (canvas_image == (Image *) NULL)
1742 {
1743 source_image=DestroyImage(source_image);
1744 return(MagickFalse);
1745 }
1746 /*
1747 Gather the maximum blur sigma values from user.
1748 */
1749 flags=NoValue;
1750 artifact=GetImageArtifact(image,"compose:args");
1751 if (artifact != (const char *) NULL)
1752 flags=ParseGeometry(artifact,&geometry_info);
1753 if ((flags & WidthValue) == 0)
1754 {
1755 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1756 "InvalidSetting","'%s' '%s'","compose:args",artifact);
1757 source_image=DestroyImage(source_image);
1758 canvas_image=DestroyImage(canvas_image);
1759 return(MagickFalse);
1760 }
1761 /*
1762 Users input sigma now needs to be converted to the EWA ellipse size.
1763 The filter defaults to a sigma of 0.5 so to make this match the users
1764 input the ellipse size needs to be doubled.
1765 */
1766 width=2.0*geometry_info.rho;
1767 height=width;
1768 if ((flags & HeightValue) != 0)
1769 height=2.0*geometry_info.sigma;
1770 /*
1771 Default the unrotated ellipse width and height axis vectors.
1772 */
1773 blur.x1=width;
1774 blur.x2=0.0;
1775 blur.y1=0.0;
1776 blur.y2=height;
1777 if ((flags & XValue) != 0 )
1778 {
1779 MagickRealType
1780 angle;
1781
1782 /*
1783 Rotate vectors if a rotation angle is given.
1784 */
1785 angle=DegreesToRadians(geometry_info.xi);
1786 blur.x1=width*cos(angle);
1787 blur.x2=width*sin(angle);
1788 blur.y1=(-height*sin(angle));
1789 blur.y2=height*cos(angle);
1790 }
1791 angle_start=0.0;
1792 angle_range=0.0;
1793 if ((flags & YValue) != 0 )
1794 {
1795 /*
1796 Lets set a angle range and calculate in the loop.
1797 */
1798 angle_start=DegreesToRadians(geometry_info.xi);
1799 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1800 }
1801 /*
1802 Set up a gaussian cylindrical filter for EWA Blurring.
1803
1804 As the minimum ellipse radius of support*1.0 the EWA algorithm
1805 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
1806 This means that even 'No Blur' will be still a little blurry! The
1807 solution (as well as the problem of preventing any user expert filter
1808 settings, is to set our own user settings, restore them afterwards.
1809 */
1810 resample_filter=AcquireResampleFilter(image,exception);
1811 SetResampleFilter(resample_filter,GaussianFilter);
1812 /*
1813 Perform the variable blurring of each pixel in image.
1814 */
1815 GetPixelInfo(image,&pixel);
1816 source_view=AcquireVirtualCacheView(source_image,exception);
1817 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1818 for (y=0; y < (ssize_t) source_image->rows; y++)
1819 {
1820 MagickBooleanType
1821 sync;
1822
1823 const Quantum
1824 *magick_restrict p;
1825
1826 Quantum
1827 *magick_restrict q;
1828
1829 ssize_t
1830 x;
1831
1832 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1833 continue;
1834 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1835 exception);
1836 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1837 exception);
1838 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1839 break;
1840 for (x=0; x < (ssize_t) source_image->columns; x++)
1841 {
1842 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1843 {
1844 p+=(ptrdiff_t) GetPixelChannels(source_image);
1845 continue;
1846 }
1847 if (fabs(angle_range) > MagickEpsilon)
1848 {
1849 MagickRealType
1850 angle;
1851
1852 angle=angle_start+angle_range*QuantumScale*(double)
1853 GetPixelBlue(source_image,p);
1854 blur.x1=width*cos(angle);
1855 blur.x2=width*sin(angle);
1856 blur.y1=(-height*sin(angle));
1857 blur.y2=height*cos(angle);
1858 }
1859 ScaleResampleFilter(resample_filter,
1860 blur.x1*QuantumScale*(double) GetPixelRed(source_image,p),
1861 blur.y1*QuantumScale*(double) GetPixelGreen(source_image,p),
1862 blur.x2*QuantumScale*(double) GetPixelRed(source_image,p),
1863 blur.y2*QuantumScale*(double) GetPixelGreen(source_image,p) );
1864 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1865 (double) y_offset+y,&pixel,exception);
1866 SetPixelViaPixelInfo(canvas_image,&pixel,q);
1867 p+=(ptrdiff_t) GetPixelChannels(source_image);
1868 q+=(ptrdiff_t) GetPixelChannels(canvas_image);
1869 }
1870 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1871 if (sync == MagickFalse)
1872 break;
1873 }
1874 resample_filter=DestroyResampleFilter(resample_filter);
1875 source_view=DestroyCacheView(source_view);
1876 canvas_view=DestroyCacheView(canvas_view);
1877 source_image=DestroyImage(source_image);
1878 source_image=canvas_image;
1879 break;
1880 }
1881 case DisplaceCompositeOp:
1882 case DistortCompositeOp:
1883 {
1884 CacheView
1885 *canvas_view;
1886
1887 MagickRealType
1888 horizontal_scale,
1889 vertical_scale;
1890
1891 PixelInfo
1892 pixel;
1893
1894 PointInfo
1895 center,
1896 offset;
1897
1898 /*
1899 Displace/Distort based on overlay gradient map:
1900 X = red_channel; Y = green_channel;
1901 compose:args = x_scale[,y_scale[,center.x,center.y]]
1902 */
1903 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
1904 if (canvas_image == (Image *) NULL)
1905 {
1906 source_image=DestroyImage(source_image);
1907 return(MagickFalse);
1908 }
1909 SetGeometryInfo(&geometry_info);
1910 flags=NoValue;
1911 artifact=GetImageArtifact(image,"compose:args");
1912 if (artifact != (char *) NULL)
1913 flags=ParseGeometry(artifact,&geometry_info);
1914 if ((flags & (WidthValue | HeightValue)) == 0 )
1915 {
1916 if ((flags & AspectValue) == 0)
1917 {
1918 horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
1919 vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
1920 }
1921 else
1922 {
1923 horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
1924 vertical_scale=(MagickRealType) (image->rows-1)/2.0;
1925 }
1926 }
1927 else
1928 {
1929 horizontal_scale=geometry_info.rho;
1930 vertical_scale=geometry_info.sigma;
1931 if ((flags & PercentValue) != 0)
1932 {
1933 if ((flags & AspectValue) == 0)
1934 {
1935 horizontal_scale*=(source_image->columns-1)/200.0;
1936 vertical_scale*=(source_image->rows-1)/200.0;
1937 }
1938 else
1939 {
1940 horizontal_scale*=(image->columns-1)/200.0;
1941 vertical_scale*=(image->rows-1)/200.0;
1942 }
1943 }
1944 if ((flags & HeightValue) == 0)
1945 vertical_scale=horizontal_scale;
1946 }
1947 /*
1948 Determine fixed center point for absolute distortion map
1949 Absolute distort ==
1950 Displace offset relative to a fixed absolute point
1951 Select that point according to +X+Y user inputs.
1952 default = center of overlay image
1953 arg flag '!' = locations/percentage relative to background image
1954 */
1955 center.x=(MagickRealType) x_offset;
1956 center.y=(MagickRealType) y_offset;
1957 if (compose == DistortCompositeOp)
1958 {
1959 if ((flags & XValue) == 0)
1960 if ((flags & AspectValue) != 0)
1961 center.x=(MagickRealType) ((image->columns-1)/2.0);
1962 else
1963 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1964 2.0);
1965 else
1966 if ((flags & AspectValue) != 0)
1967 center.x=geometry_info.xi;
1968 else
1969 center.x=(MagickRealType) (x_offset+geometry_info.xi);
1970 if ((flags & YValue) == 0)
1971 if ((flags & AspectValue) != 0)
1972 center.y=(MagickRealType) ((image->rows-1)/2.0);
1973 else
1974 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1975 else
1976 if ((flags & AspectValue) != 0)
1977 center.y=geometry_info.psi;
1978 else
1979 center.y=(MagickRealType) (y_offset+geometry_info.psi);
1980 }
1981 /*
1982 Shift the pixel offset point as defined by the provided,
1983 displacement/distortion map. -- Like a lens...
1984 */
1985 GetPixelInfo(image,&pixel);
1986 image_view=AcquireVirtualCacheView(image,exception);
1987 source_view=AcquireVirtualCacheView(source_image,exception);
1988 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1989 for (y=0; y < (ssize_t) source_image->rows; y++)
1990 {
1991 MagickBooleanType
1992 sync;
1993
1994 const Quantum
1995 *magick_restrict p;
1996
1997 Quantum
1998 *magick_restrict q;
1999
2000 ssize_t
2001 x;
2002
2003 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
2004 continue;
2005 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
2006 exception);
2007 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
2008 exception);
2009 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2010 break;
2011 for (x=0; x < (ssize_t) source_image->columns; x++)
2012 {
2013 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
2014 {
2015 p+=(ptrdiff_t) GetPixelChannels(source_image);
2016 continue;
2017 }
2018 /*
2019 Displace the offset.
2020 */
2021 offset.x=(double) (horizontal_scale*((double) GetPixelRed(
2022 source_image,p)-(((MagickRealType) QuantumRange+1.0)/2.0)))/
2023 (((MagickRealType) QuantumRange+1.0)/2.0)+center.x+
2024 ((compose == DisplaceCompositeOp) ? x : 0);
2025 offset.y=(double) (vertical_scale*((double) GetPixelGreen(
2026 source_image,p)-(((MagickRealType) QuantumRange+1.0)/2.0)))/
2027 (((MagickRealType) QuantumRange+1.0)/2.0)+center.y+
2028 ((compose == DisplaceCompositeOp) ? y : 0);
2029 status=InterpolatePixelInfo(image,image_view,
2030 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2031 &pixel,exception);
2032 if (status == MagickFalse)
2033 break;
2034 /*
2035 Mask with the 'invalid pixel mask' in alpha channel.
2036 */
2037 pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
2038 (QuantumScale*(double) GetPixelAlpha(source_image,p));
2039 SetPixelViaPixelInfo(canvas_image,&pixel,q);
2040 p+=(ptrdiff_t) GetPixelChannels(source_image);
2041 q+=(ptrdiff_t) GetPixelChannels(canvas_image);
2042 }
2043 if (x < (ssize_t) source_image->columns)
2044 break;
2045 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
2046 if (sync == MagickFalse)
2047 break;
2048 }
2049 canvas_view=DestroyCacheView(canvas_view);
2050 source_view=DestroyCacheView(source_view);
2051 image_view=DestroyCacheView(image_view);
2052 source_image=DestroyImage(source_image);
2053 source_image=canvas_image;
2054 break;
2055 }
2056 case DissolveCompositeOp:
2057 {
2058 /*
2059 Geometry arguments to dissolve factors.
2060 */
2061 artifact=GetImageArtifact(image,"compose:args");
2062 if (artifact != (char *) NULL)
2063 {
2064 flags=ParseGeometry(artifact,&geometry_info);
2065 source_dissolve=geometry_info.rho/100.0;
2066 canvas_dissolve=1.0;
2067 if ((source_dissolve-MagickEpsilon) < 0.0)
2068 source_dissolve=0.0;
2069 if ((source_dissolve+MagickEpsilon) > 1.0)
2070 {
2071 canvas_dissolve=2.0-source_dissolve;
2072 source_dissolve=1.0;
2073 }
2074 if ((flags & SigmaValue) != 0)
2075 canvas_dissolve=geometry_info.sigma/100.0;
2076 if ((canvas_dissolve-MagickEpsilon) < 0.0)
2077 canvas_dissolve=0.0;
2078 if ((canvas_dissolve+MagickEpsilon) > 1.0)
2079 canvas_dissolve=1.0;
2080 }
2081 break;
2082 }
2083 case BlendCompositeOp:
2084 {
2085 artifact=GetImageArtifact(image,"compose:args");
2086 if (artifact != (char *) NULL)
2087 {
2088 flags=ParseGeometry(artifact,&geometry_info);
2089 source_dissolve=geometry_info.rho/100.0;
2090 canvas_dissolve=1.0-source_dissolve;
2091 if ((flags & SigmaValue) != 0)
2092 canvas_dissolve=geometry_info.sigma/100.0;
2093 }
2094 break;
2095 }
2096 case SaliencyBlendCompositeOp:
2097 {
2098 double
2099 residual_threshold = 0.0002,
2100 iterations = 400.0;
2101
2102 size_t
2103 tick = 100;
2104
2105 artifact=GetImageArtifact(image,"compose:args");
2106 if (artifact != (char *) NULL)
2107 {
2108 flags=ParseGeometry(artifact,&geometry_info);
2109 iterations=geometry_info.rho;
2110 if ((flags & SigmaValue) != 0)
2111 residual_threshold=geometry_info.sigma;
2112 if ((flags & XiValue) != 0)
2113 tick=(size_t) geometry_info.xi;
2114 }
2115 status=SaliencyBlendImage(image,composite,x_offset,y_offset,iterations,
2116 residual_threshold,tick,exception);
2117 source_image=DestroyImage(source_image);
2118 return(status);
2119 }
2120 case SeamlessBlendCompositeOp:
2121 {
2122 double
2123 residual_threshold = 0.0002,
2124 iterations = 400.0;
2125
2126 size_t
2127 tick = 100;
2128
2129 artifact=GetImageArtifact(image,"compose:args");
2130 if (artifact != (char *) NULL)
2131 {
2132 flags=ParseGeometry(artifact,&geometry_info);
2133 iterations=geometry_info.rho;
2134 if ((flags & SigmaValue) != 0)
2135 residual_threshold=geometry_info.sigma;
2136 if ((flags & XiValue) != 0)
2137 tick=(size_t) geometry_info.xi;
2138 }
2139 status=SeamlessBlendImage(image,composite,x_offset,y_offset,iterations,
2140 residual_threshold,tick,exception);
2141 source_image=DestroyImage(source_image);
2142 return(status);
2143 }
2144 case MathematicsCompositeOp:
2145 {
2146 /*
2147 Just collect the values from "compose:args", setting.
2148 Unused values are set to zero automagically.
2149
2150 Arguments are normally a comma separated list, so this probably should
2151 be changed to some 'general comma list' parser, (with a minimum
2152 number of values)
2153 */
2154 SetGeometryInfo(&geometry_info);
2155 artifact=GetImageArtifact(image,"compose:args");
2156 if (artifact != (char *) NULL)
2157 {
2158 flags=ParseGeometry(artifact,&geometry_info);
2159 if (flags == NoValue)
2160 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2161 "InvalidGeometry","`%s'",artifact);
2162 }
2163 break;
2164 }
2165 case ModulateCompositeOp:
2166 {
2167 /*
2168 Determine the luma and chroma scale.
2169 */
2170 artifact=GetImageArtifact(image,"compose:args");
2171 if (artifact != (char *) NULL)
2172 {
2173 flags=ParseGeometry(artifact,&geometry_info);
2174 percent_luma=geometry_info.rho;
2175 if ((flags & SigmaValue) != 0)
2176 percent_chroma=geometry_info.sigma;
2177 }
2178 break;
2179 }
2180 case ThresholdCompositeOp:
2181 {
2182 /*
2183 Determine the amount and threshold.
2184 */
2185 artifact=GetImageArtifact(image,"compose:args");
2186 if (artifact != (char *) NULL)
2187 {
2188 flags=ParseGeometry(artifact,&geometry_info);
2189 amount=geometry_info.rho;
2190 threshold=geometry_info.sigma;
2191 if ((flags & SigmaValue) == 0)
2192 threshold=0.05f;
2193 }
2194 threshold*=(double) QuantumRange;
2195 break;
2196 }
2197 default:
2198 break;
2199 }
2200 /*
2201 Composite image.
2202 */
2203 status=MagickTrue;
2204 progress=0;
2205 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2206 source_view=AcquireVirtualCacheView(source_image,exception);
2207 image_view=AcquireAuthenticCacheView(image,exception);
2208#if defined(MAGICKCORE_OPENMP_SUPPORT)
2209 #pragma omp parallel for schedule(static) shared(progress,status) \
2210 magick_number_threads(source_image,image,image->rows,1)
2211#endif
2212 for (y=0; y < (ssize_t) image->rows; y++)
2213 {
2214 const Quantum
2215 *pixels;
2216
2217 MagickRealType
2218 blue = 0.0,
2219 chroma = 0.0,
2220 green = 0.0,
2221 hue = 0.0,
2222 luma = 0.0,
2223 red = 0.0;
2224
2225 PixelInfo
2226 canvas_pixel,
2227 source_pixel;
2228
2229 const Quantum
2230 *magick_restrict p;
2231
2232 Quantum
2233 *magick_restrict q;
2234
2235 ssize_t
2236 x;
2237
2238 if (status == MagickFalse)
2239 continue;
2240 if (clip_to_self != MagickFalse)
2241 {
2242 if (y < y_offset)
2243 continue;
2244 if ((y-(double) y_offset) >= (double) source_image->rows)
2245 continue;
2246 }
2247 /*
2248 If pixels is NULL, y is outside overlay region.
2249 */
2250 pixels=(Quantum *) NULL;
2251 p=(Quantum *) NULL;
2252 if ((y >= y_offset) &&
2253 ((y-(double) y_offset) < (double) source_image->rows))
2254 {
2255 p=GetCacheViewVirtualPixels(source_view,0,
2256 CastDoubleToLong(y-(double) y_offset),source_image->columns,1,
2257 exception);
2258 if (p == (const Quantum *) NULL)
2259 {
2260 status=MagickFalse;
2261 continue;
2262 }
2263 pixels=p;
2264 if (x_offset < 0)
2265 p-=(ptrdiff_t)CastDoubleToLong((double) x_offset*GetPixelChannels(source_image));
2266 }
2267 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2268 if (q == (Quantum *) NULL)
2269 {
2270 status=MagickFalse;
2271 continue;
2272 }
2273 GetPixelInfo(image,&canvas_pixel);
2274 GetPixelInfo(source_image,&source_pixel);
2275 for (x=0; x < (ssize_t) image->columns; x++)
2276 {
2277 double
2278 gamma = 0.0;
2279
2280 MagickRealType
2281 alpha = 0.0,
2282 Da = 0.0,
2283 Dc = 0.0,
2284 Dca = 0.0,
2285 DcaDa = 0.0,
2286 Di = 0.0,
2287 Sa = 0.0,
2288 SaSca = 0.0,
2289 Sc = 0.0,
2290 Sca = 0.0,
2291 Si = 0.0;
2292
2293 size_t
2294 channels;
2295
2296 ssize_t
2297 i;
2298
2299 if (clip_to_self != MagickFalse)
2300 {
2301 if (x < x_offset)
2302 {
2303 q+=(ptrdiff_t) GetPixelChannels(image);
2304 continue;
2305 }
2306 if ((x-(double) x_offset) >= (double) source_image->columns)
2307 break;
2308 }
2309 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2310 ((x-(double) x_offset) >= (double) source_image->columns))
2311 {
2312 Quantum
2313 source[MaxPixelChannels];
2314
2315 /*
2316 Virtual composite:
2317 Sc: source color.
2318 Dc: canvas color.
2319 */
2320 (void) GetOneVirtualPixel(source_image,
2321 CastDoubleToLong(x-(double) x_offset),
2322 CastDoubleToLong(y-(double) y_offset),source,exception);
2323 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2324 {
2325 MagickRealType
2326 pixel = 0.0;
2327
2328 PixelChannel channel = GetPixelChannelChannel(image,i);
2329 PixelTrait traits = GetPixelChannelTraits(image,channel);
2330 PixelTrait source_traits = GetPixelChannelTraits(source_image,
2331 channel);
2332 if ((traits == UndefinedPixelTrait) ||
2333 (source_traits == UndefinedPixelTrait))
2334 continue;
2335 switch (compose)
2336 {
2337 case AlphaCompositeOp:
2338 case ChangeMaskCompositeOp:
2339 case CopyAlphaCompositeOp:
2340 case DstAtopCompositeOp:
2341 case DstInCompositeOp:
2342 case InCompositeOp:
2343 case OutCompositeOp:
2344 case SrcInCompositeOp:
2345 case SrcOutCompositeOp:
2346 {
2347 if (channel == AlphaPixelChannel)
2348 pixel=(MagickRealType) TransparentAlpha;
2349 else
2350 pixel=(MagickRealType) q[i];
2351 break;
2352 }
2353 case ClearCompositeOp:
2354 case CopyCompositeOp:
2355 case ReplaceCompositeOp:
2356 case SrcCompositeOp:
2357 {
2358 if (channel == AlphaPixelChannel)
2359 pixel=(MagickRealType) TransparentAlpha;
2360 else
2361 pixel=0.0;
2362 break;
2363 }
2364 case BlendCompositeOp:
2365 case DissolveCompositeOp:
2366 {
2367 if (channel == AlphaPixelChannel)
2368 pixel=canvas_dissolve*(double) GetPixelAlpha(source_image,
2369 source);
2370 else
2371 pixel=(MagickRealType) source[channel];
2372 break;
2373 }
2374 default:
2375 {
2376 pixel=(MagickRealType) source[channel];
2377 break;
2378 }
2379 }
2380 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
2381 ClampToQuantum(pixel);
2382 }
2383 q+=(ptrdiff_t) GetPixelChannels(image);
2384 continue;
2385 }
2386 /*
2387 Authentic composite:
2388 Sa: normalized source alpha.
2389 Da: normalized canvas alpha.
2390 */
2391 Sa=QuantumScale*(double) GetPixelAlpha(source_image,p);
2392 Da=QuantumScale*(double) GetPixelAlpha(image,q);
2393 switch (compose)
2394 {
2395 case BumpmapCompositeOp:
2396 case ColorBurnCompositeOp:
2397 case ColorDodgeCompositeOp:
2398 case DarkenCompositeOp:
2399 case DifferenceCompositeOp:
2400 case DivideDstCompositeOp:
2401 case DivideSrcCompositeOp:
2402 case ExclusionCompositeOp:
2403 case FreezeCompositeOp:
2404 case HardLightCompositeOp:
2405 case HardMixCompositeOp:
2406 case InterpolateCompositeOp:
2407 case LightenCompositeOp:
2408 case LinearBurnCompositeOp:
2409 case LinearDodgeCompositeOp:
2410 case LinearLightCompositeOp:
2411 case MathematicsCompositeOp:
2412 case MinusDstCompositeOp:
2413 case MinusSrcCompositeOp:
2414 case MultiplyCompositeOp:
2415 case NegateCompositeOp:
2416 case OverlayCompositeOp:
2417 case PegtopLightCompositeOp:
2418 case PinLightCompositeOp:
2419 case ReflectCompositeOp:
2420 case ScreenCompositeOp:
2421 case SoftBurnCompositeOp:
2422 case SoftDodgeCompositeOp:
2423 case SoftLightCompositeOp:
2424 case StampCompositeOp:
2425 case VividLightCompositeOp:
2426 {
2427 alpha=RoundToUnity(Sa+Da-Sa*Da);
2428 break;
2429 }
2430 case DstAtopCompositeOp:
2431 case DstInCompositeOp:
2432 case InCompositeOp:
2433 case SrcInCompositeOp:
2434 {
2435 alpha=Sa*Da;
2436 break;
2437 }
2438 case DissolveCompositeOp:
2439 {
2440 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
2441 canvas_dissolve*Da;
2442 break;
2443 }
2444 case DstOverCompositeOp:
2445 case OverCompositeOp:
2446 case SrcOverCompositeOp:
2447 {
2448 alpha=Sa+Da-Sa*Da;
2449 break;
2450 }
2451 case DstOutCompositeOp:
2452 {
2453 alpha=Da*(1.0-Sa);
2454 break;
2455 }
2456 case OutCompositeOp:
2457 case SrcOutCompositeOp:
2458 {
2459 alpha=Sa*(1.0-Da);
2460 break;
2461 }
2462 case BlendCompositeOp:
2463 case PlusCompositeOp:
2464 {
2465 alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
2466 break;
2467 }
2468 case XorCompositeOp:
2469 {
2470 alpha=Sa+Da-2.0*Sa*Da;
2471 break;
2472 }
2473 case ModulusAddCompositeOp:
2474 {
2475 if ((Sa+Da) <= 1.0)
2476 {
2477 alpha=(Sa+Da);
2478 break;
2479 }
2480 alpha=((Sa+Da)-1.0);
2481 break;
2482 }
2483 case ModulusSubtractCompositeOp:
2484 {
2485 if ((Sa-Da) >= 0.0)
2486 {
2487 alpha=(Sa-Da);
2488 break;
2489 }
2490 alpha=((Sa-Da)+1.0);
2491 break;
2492 }
2493 default:
2494 {
2495 alpha=1.0;
2496 break;
2497 }
2498 }
2499 switch (compose)
2500 {
2501 case ColorizeCompositeOp:
2502 case HueCompositeOp:
2503 case LuminizeCompositeOp:
2504 case ModulateCompositeOp:
2505 case RMSECompositeOp:
2506 case SaturateCompositeOp:
2507 {
2508 Si=GetPixelIntensity(source_image,p);
2509 GetPixelInfoPixel(source_image,p,&source_pixel);
2510 GetPixelInfoPixel(image,q,&canvas_pixel);
2511 break;
2512 }
2513 case BumpmapCompositeOp:
2514 case CopyAlphaCompositeOp:
2515 case DarkenIntensityCompositeOp:
2516 case LightenIntensityCompositeOp:
2517 {
2518 Si=GetPixelIntensity(source_image,p);
2519 Di=GetPixelIntensity(image,q);
2520 break;
2521 }
2522 default:
2523 break;
2524 }
2525 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2526 {
2527 MagickRealType
2528 pixel = 0.0,
2529 sans = 0.0;
2530
2531 PixelChannel channel = GetPixelChannelChannel(image,i);
2532 PixelTrait traits = GetPixelChannelTraits(image,channel);
2533 PixelTrait source_traits = GetPixelChannelTraits(source_image,channel);
2534 if (traits == UndefinedPixelTrait)
2535 continue;
2536 if ((channel == AlphaPixelChannel) &&
2537 ((traits & UpdatePixelTrait) != 0))
2538 {
2539 /*
2540 Set alpha channel.
2541 */
2542 switch (compose)
2543 {
2544 case AlphaCompositeOp:
2545 {
2546 pixel=(double) QuantumRange*Sa;
2547 break;
2548 }
2549 case AtopCompositeOp:
2550 case CopyBlackCompositeOp:
2551 case CopyBlueCompositeOp:
2552 case CopyCyanCompositeOp:
2553 case CopyGreenCompositeOp:
2554 case CopyMagentaCompositeOp:
2555 case CopyRedCompositeOp:
2556 case CopyYellowCompositeOp:
2557 case SrcAtopCompositeOp:
2558 case DstCompositeOp:
2559 case NoCompositeOp:
2560 {
2561 pixel=(double) QuantumRange*Da;
2562 break;
2563 }
2564 case BumpmapCompositeOp:
2565 {
2566 pixel=Si*Da;
2567 break;
2568 }
2569 case ChangeMaskCompositeOp:
2570 {
2571 if (IsFuzzyEquivalencePixel(source_image,p,image,q) != MagickFalse)
2572 pixel=(MagickRealType) TransparentAlpha;
2573 else
2574 pixel=(double) QuantumRange*Da;
2575 break;
2576 }
2577 case ClearCompositeOp:
2578 {
2579 pixel=(MagickRealType) TransparentAlpha;
2580 break;
2581 }
2582 case ColorizeCompositeOp:
2583 case HueCompositeOp:
2584 case LuminizeCompositeOp:
2585 case RMSECompositeOp:
2586 case SaturateCompositeOp:
2587 {
2588 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2589 {
2590 pixel=(double) QuantumRange*Da;
2591 break;
2592 }
2593 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
2594 {
2595 pixel=(double) QuantumRange*Sa;
2596 break;
2597 }
2598 if (Sa < Da)
2599 {
2600 pixel=(double) QuantumRange*Da;
2601 break;
2602 }
2603 pixel=(double) QuantumRange*Sa;
2604 break;
2605 }
2606 case CopyAlphaCompositeOp:
2607 {
2608 if (source_image->alpha_trait == UndefinedPixelTrait)
2609 pixel=Si;
2610 else
2611 pixel=(double) QuantumRange*Sa;
2612 break;
2613 }
2614 case BlurCompositeOp:
2615 case CopyCompositeOp:
2616 case DisplaceCompositeOp:
2617 case DistortCompositeOp:
2618 case DstAtopCompositeOp:
2619 case ReplaceCompositeOp:
2620 case SrcCompositeOp:
2621 {
2622 pixel=(double) QuantumRange*Sa;
2623 break;
2624 }
2625 case DarkenIntensityCompositeOp:
2626 {
2627 if (compose_sync == MagickFalse)
2628 {
2629 pixel=Si < Di? Sa : Da;
2630 break;
2631 }
2632 pixel=Sa*Si < Da*Di ? Sa : Da;
2633 break;
2634 }
2635 case DifferenceCompositeOp:
2636 {
2637 pixel=(double) QuantumRange*fabs((double) (Sa-Da));
2638 break;
2639 }
2640 case FreezeCompositeOp:
2641 {
2642 pixel=(double) QuantumRange*(1.0-(1.0-Sa)*(1.0-Sa)*
2643 PerceptibleReciprocal(Da));
2644 if (pixel < 0.0)
2645 pixel=0.0;
2646 break;
2647 }
2648 case InterpolateCompositeOp:
2649 {
2650 pixel=(double) QuantumRange*(0.5-0.25*cos(MagickPI*Sa)-0.25*
2651 cos(MagickPI*Da));
2652 break;
2653 }
2654 case LightenIntensityCompositeOp:
2655 {
2656 if (compose_sync == MagickFalse)
2657 {
2658 pixel=Si > Di ? Sa : Da;
2659 break;
2660 }
2661 pixel=Sa*Si > Da*Di ? Sa : Da;
2662 break;
2663 }
2664 case ModulateCompositeOp:
2665 {
2666 pixel=(double) QuantumRange*Da;
2667 break;
2668 }
2669 case MultiplyCompositeOp:
2670 {
2671 if (compose_sync == MagickFalse)
2672 {
2673 pixel=(double) QuantumRange*Sa*Da;
2674 break;
2675 }
2676 pixel=(double) QuantumRange*alpha;
2677 break;
2678 }
2679 case NegateCompositeOp:
2680 {
2681 pixel=(double) QuantumRange*((1.0-Sa-Da));
2682 break;
2683 }
2684 case ReflectCompositeOp:
2685 {
2686 pixel=(double) QuantumRange*(Sa*Sa*
2687 PerceptibleReciprocal(1.0-Da));
2688 if (pixel > (double) QuantumRange)
2689 pixel=(double) QuantumRange;
2690 break;
2691 }
2692 case StampCompositeOp:
2693 {
2694 pixel=(double) QuantumRange*(Sa+Da*Da-1.0);
2695 break;
2696 }
2697 case StereoCompositeOp:
2698 {
2699 pixel=(double) QuantumRange*(Sa+Da)/2;
2700 break;
2701 }
2702 default:
2703 {
2704 pixel=(double) QuantumRange*alpha;
2705 break;
2706 }
2707 }
2708 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
2709 ClampToQuantum(pixel);
2710 continue;
2711 }
2712 if (source_traits == UndefinedPixelTrait)
2713 continue;
2714 /*
2715 Sc: source color.
2716 Dc: canvas color.
2717 */
2718 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
2719 Dc=(MagickRealType) q[i];
2720 if ((traits & CopyPixelTrait) != 0)
2721 {
2722 /*
2723 Copy channel.
2724 */
2725 q[i]=ClampToQuantum(Dc);
2726 continue;
2727 }
2728 /*
2729 Porter-Duff compositions:
2730 Sca: source normalized color multiplied by alpha.
2731 Dca: normalized canvas color multiplied by alpha.
2732 */
2733 Sca=QuantumScale*Sa*Sc;
2734 Dca=QuantumScale*Da*Dc;
2735 SaSca=Sa*PerceptibleReciprocal(Sca);
2736 DcaDa=Dca*PerceptibleReciprocal(Da);
2737 switch (compose)
2738 {
2739 case DarkenCompositeOp:
2740 case LightenCompositeOp:
2741 case ModulusSubtractCompositeOp:
2742 {
2743 gamma=PerceptibleReciprocal(1.0-alpha);
2744 break;
2745 }
2746 default:
2747 {
2748 gamma=PerceptibleReciprocal(alpha);
2749 break;
2750 }
2751 }
2752 pixel=Dc;
2753 switch (compose)
2754 {
2755 case AlphaCompositeOp:
2756 {
2757 pixel=(double) QuantumRange*Sa;
2758 break;
2759 }
2760 case AtopCompositeOp:
2761 case SrcAtopCompositeOp:
2762 {
2763 pixel=(double) QuantumRange*(Sca*Da+Dca*(1.0-Sa));
2764 break;
2765 }
2766 case BlendCompositeOp:
2767 {
2768 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
2769 break;
2770 }
2771 case CopyCompositeOp:
2772 case ReplaceCompositeOp:
2773 case SrcCompositeOp:
2774 {
2775 pixel=(double) QuantumRange*Sca;
2776 break;
2777 }
2778 case BlurCompositeOp:
2779 case DisplaceCompositeOp:
2780 case DistortCompositeOp:
2781 {
2782 pixel=Sc;
2783 break;
2784 }
2785 case BumpmapCompositeOp:
2786 {
2787 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2788 {
2789 pixel=Dc;
2790 break;
2791 }
2792 pixel=(double) QuantumScale*Si*Dc;
2793 break;
2794 }
2795 case ChangeMaskCompositeOp:
2796 {
2797 pixel=Dc;
2798 break;
2799 }
2800 case ClearCompositeOp:
2801 {
2802 pixel=0.0;
2803 break;
2804 }
2805 case ColorBurnCompositeOp:
2806 {
2807 if ((Sca == 0.0) && (Dca == Da))
2808 {
2809 pixel=(double) QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
2810 break;
2811 }
2812 if (Sca == 0.0)
2813 {
2814 pixel=(double) QuantumRange*gamma*(Dca*(1.0-Sa));
2815 break;
2816 }
2817 pixel=(double) QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,
2818 (1.0-DcaDa)*SaSca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
2819 break;
2820 }
2821 case ColorDodgeCompositeOp:
2822 {
2823 if ((Sca*Da+Dca*Sa) >= Sa*Da)
2824 pixel=(double) QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*
2825 (1.0-Sa));
2826 else
2827 pixel=(double) QuantumRange*gamma*(Dca*Sa*Sa*
2828 PerceptibleReciprocal(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
2829 break;
2830 }
2831 case ColorizeCompositeOp:
2832 {
2833 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2834 {
2835 pixel=Dc;
2836 break;
2837 }
2838 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
2839 {
2840 pixel=Sc;
2841 break;
2842 }
2843 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
2844 (double) canvas_pixel.green,(double) canvas_pixel.blue,
2845 white_luminance,illuminant,&sans,&sans,&luma);
2846 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
2847 (double) source_pixel.green,(double) source_pixel.blue,
2848 white_luminance,illuminant,&hue,&chroma,&sans);
2849 ConvertGenericToRGB(colorspace,hue,chroma,luma,
2850 white_luminance,illuminant,&red,&green,&blue);
2851 switch (channel)
2852 {
2853 case RedPixelChannel: pixel=red; break;
2854 case GreenPixelChannel: pixel=green; break;
2855 case BluePixelChannel: pixel=blue; break;
2856 default: pixel=Dc; break;
2857 }
2858 break;
2859 }
2860 case CopyAlphaCompositeOp:
2861 {
2862 pixel=Dc;
2863 break;
2864 }
2865 case CopyBlackCompositeOp:
2866 {
2867 if (channel == BlackPixelChannel)
2868 pixel=(MagickRealType) GetPixelBlack(source_image,p);
2869 break;
2870 }
2871 case CopyBlueCompositeOp:
2872 case CopyYellowCompositeOp:
2873 {
2874 if (channel == BluePixelChannel)
2875 pixel=(MagickRealType) GetPixelBlue(source_image,p);
2876 break;
2877 }
2878 case CopyGreenCompositeOp:
2879 case CopyMagentaCompositeOp:
2880 {
2881 if (channel == GreenPixelChannel)
2882 pixel=(MagickRealType) GetPixelGreen(source_image,p);
2883 break;
2884 }
2885 case CopyRedCompositeOp:
2886 case CopyCyanCompositeOp:
2887 {
2888 if (channel == RedPixelChannel)
2889 pixel=(MagickRealType) GetPixelRed(source_image,p);
2890 break;
2891 }
2892 case DarkenCompositeOp:
2893 {
2894 /*
2895 Darken is equivalent to a 'Minimum' method
2896 OR a greyscale version of a binary 'Or'
2897 OR the 'Intersection' of pixel sets.
2898 */
2899 if (compose_sync == MagickFalse)
2900 {
2901 pixel=MagickMin(Sc,Dc);
2902 break;
2903 }
2904 if ((Sca*Da) < (Dca*Sa))
2905 {
2906 pixel=(double) QuantumRange*(Sca+Dca*(1.0-Sa));
2907 break;
2908 }
2909 pixel=(double) QuantumRange*(Dca+Sca*(1.0-Da));
2910 break;
2911 }
2912 case DarkenIntensityCompositeOp:
2913 {
2914 if (compose_sync == MagickFalse)
2915 {
2916 pixel=Si < Di ? Sc : Dc;
2917 break;
2918 }
2919 pixel=Sa*Si < Da*Di ? Sc : Dc;
2920 break;
2921 }
2922 case DifferenceCompositeOp:
2923 {
2924 if (compose_sync == MagickFalse)
2925 {
2926 pixel=fabs((double) Sc-Dc);
2927 break;
2928 }
2929 pixel=(double) QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,
2930 Dca*Sa));
2931 break;
2932 }
2933 case DissolveCompositeOp:
2934 {
2935 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
2936 canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
2937 break;
2938 }
2939 case DivideDstCompositeOp:
2940 {
2941 if (compose_sync == MagickFalse)
2942 {
2943 pixel=(double) QuantumRange*(Sc/PerceptibleReciprocal(Dc));
2944 break;
2945 }
2946 if ((fabs((double) Sca) < MagickEpsilon) &&
2947 (fabs((double) Dca) < MagickEpsilon))
2948 {
2949 pixel=(double) QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2950 break;
2951 }
2952 if (fabs((double) Dca) < MagickEpsilon)
2953 {
2954 pixel=(double) QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*
2955 (1.0-Sa));
2956 break;
2957 }
2958 pixel=(double) QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*
2959 (1.0-Sa));
2960 break;
2961 }
2962 case DivideSrcCompositeOp:
2963 {
2964 if (compose_sync == MagickFalse)
2965 {
2966 pixel=(double) QuantumRange*(Dc/PerceptibleReciprocal(Sc));
2967 break;
2968 }
2969 if ((fabs((double) Dca) < MagickEpsilon) &&
2970 (fabs((double) Sca) < MagickEpsilon))
2971 {
2972 pixel=(double) QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
2973 break;
2974 }
2975 if (fabs((double) Sca) < MagickEpsilon)
2976 {
2977 pixel=(double) QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*
2978 (1.0-Da));
2979 break;
2980 }
2981 pixel=(double) QuantumRange*gamma*(Dca*Sa*SaSca+Dca*(1.0-Sa)+Sca*
2982 (1.0-Da));
2983 break;
2984 }
2985 case DstAtopCompositeOp:
2986 {
2987 pixel=(double) QuantumRange*(Dca*Sa+Sca*(1.0-Da));
2988 break;
2989 }
2990 case DstCompositeOp:
2991 case NoCompositeOp:
2992 {
2993 pixel=(double) QuantumRange*Dca;
2994 break;
2995 }
2996 case DstInCompositeOp:
2997 {
2998 pixel=(double) QuantumRange*gamma*(Dca*Sa);
2999 break;
3000 }
3001 case DstOutCompositeOp:
3002 {
3003 pixel=(double) QuantumRange*gamma*(Dca*(1.0-Sa));
3004 break;
3005 }
3006 case DstOverCompositeOp:
3007 {
3008 pixel=(double) QuantumRange*gamma*(Dca+Sca*(1.0-Da));
3009 break;
3010 }
3011 case ExclusionCompositeOp:
3012 {
3013 pixel=(double) QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*
3014 (1.0-Da)+Dca*(1.0-Sa));
3015 break;
3016 }
3017 case FreezeCompositeOp:
3018 {
3019 pixel=(double) QuantumRange*gamma*(1.0-(1.0-Sca)*(1.0-Sca)*
3020 PerceptibleReciprocal(Dca));
3021 if (pixel < 0.0)
3022 pixel=0.0;
3023 break;
3024 }
3025 case HardLightCompositeOp:
3026 {
3027 if ((2.0*Sca) < Sa)
3028 {
3029 pixel=(double) QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
3030 (1.0-Sa));
3031 break;
3032 }
3033 pixel=(double) QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*
3034 (1.0-Da)+Dca*(1.0-Sa));
3035 break;
3036 }
3037 case HardMixCompositeOp:
3038 {
3039 pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : (double) QuantumRange);
3040 break;
3041 }
3042 case HueCompositeOp:
3043 {
3044 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3045 {
3046 pixel=Dc;
3047 break;
3048 }
3049 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3050 {
3051 pixel=Sc;
3052 break;
3053 }
3054 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3055 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3056 white_luminance,illuminant,&hue,&chroma,&luma);
3057 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3058 (double) source_pixel.green,(double) source_pixel.blue,
3059 white_luminance,illuminant,&hue,&sans,&sans);
3060 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3061 white_luminance,illuminant,&red,&green,&blue);
3062 switch (channel)
3063 {
3064 case RedPixelChannel: pixel=red; break;
3065 case GreenPixelChannel: pixel=green; break;
3066 case BluePixelChannel: pixel=blue; break;
3067 default: pixel=Dc; break;
3068 }
3069 break;
3070 }
3071 case InCompositeOp:
3072 case SrcInCompositeOp:
3073 {
3074 pixel=(double) QuantumRange*(Sca*Da);
3075 break;
3076 }
3077 case InterpolateCompositeOp:
3078 {
3079 pixel=(double) QuantumRange*(0.5-0.25*cos(MagickPI*Sca)-0.25*
3080 cos(MagickPI*Dca));
3081 break;
3082 }
3083 case LinearBurnCompositeOp:
3084 {
3085 /*
3086 LinearBurn: as defined by Abode Photoshop, according to
3087 http://www.simplefilter.de/en/basics/mixmods.html is:
3088
3089 f(Sc,Dc) = Sc + Dc - 1
3090 */
3091 pixel=(double) QuantumRange*gamma*(Sca+Dca-Sa*Da);
3092 break;
3093 }
3094 case LinearDodgeCompositeOp:
3095 {
3096 pixel=gamma*(Sa*Sc+Da*Dc);
3097 break;
3098 }
3099 case LinearLightCompositeOp:
3100 {
3101 /*
3102 LinearLight: as defined by Abode Photoshop, according to
3103 http://www.simplefilter.de/en/basics/mixmods.html is:
3104
3105 f(Sc,Dc) = Dc + 2*Sc - 1
3106 */
3107 pixel=(double) QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
3108 break;
3109 }
3110 case LightenCompositeOp:
3111 {
3112 if (compose_sync == MagickFalse)
3113 {
3114 pixel=MagickMax(Sc,Dc);
3115 break;
3116 }
3117 if ((Sca*Da) > (Dca*Sa))
3118 {
3119 pixel=(double) QuantumRange*(Sca+Dca*(1.0-Sa));
3120 break;
3121 }
3122 pixel=(double) QuantumRange*(Dca+Sca*(1.0-Da));
3123 break;
3124 }
3125 case LightenIntensityCompositeOp:
3126 {
3127 /*
3128 Lighten is equivalent to a 'Maximum' method
3129 OR a greyscale version of a binary 'And'
3130 OR the 'Union' of pixel sets.
3131 */
3132 if (compose_sync == MagickFalse)
3133 {
3134 pixel=Si > Di ? Sc : Dc;
3135 break;
3136 }
3137 pixel=Sa*Si > Da*Di ? Sc : Dc;
3138 break;
3139 }
3140 case LuminizeCompositeOp:
3141 {
3142 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3143 {
3144 pixel=Dc;
3145 break;
3146 }
3147 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3148 {
3149 pixel=Sc;
3150 break;
3151 }
3152 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3153 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3154 white_luminance,illuminant,&hue,&chroma,&luma);
3155 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3156 (double) source_pixel.green,(double) source_pixel.blue,
3157 white_luminance,illuminant,&sans,&sans,&luma);
3158 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3159 white_luminance,illuminant,&red,&green,&blue);
3160 switch (channel)
3161 {
3162 case RedPixelChannel: pixel=red; break;
3163 case GreenPixelChannel: pixel=green; break;
3164 case BluePixelChannel: pixel=blue; break;
3165 default: pixel=Dc; break;
3166 }
3167 break;
3168 }
3169 case MathematicsCompositeOp:
3170 {
3171 /*
3172 'Mathematics' a free form user control mathematical composition
3173 is defined as...
3174
3175 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
3176
3177 Where the arguments A,B,C,D are (currently) passed to composite
3178 as a command separated 'geometry' string in "compose:args" image
3179 artifact.
3180
3181 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
3182
3183 Applying the SVG transparency formula (see above), we get...
3184
3185 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
3186
3187 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
3188 Dca*(1.0-Sa)
3189 */
3190 if (compose_sync == MagickFalse)
3191 {
3192 pixel=geometry_info.rho*Sc*Dc+geometry_info.sigma*Sc+
3193 geometry_info.xi*Dc+geometry_info.psi;
3194 break;
3195 }
3196 pixel=(double) QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
3197 geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
3198 geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
3199 break;
3200 }
3201 case MinusDstCompositeOp:
3202 {
3203 if (compose_sync == MagickFalse)
3204 {
3205 pixel=Dc-Sc;
3206 break;
3207 }
3208 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
3209 break;
3210 }
3211 case MinusSrcCompositeOp:
3212 {
3213 /*
3214 Minus source from canvas.
3215
3216 f(Sc,Dc) = Sc - Dc
3217 */
3218 if (compose_sync == MagickFalse)
3219 {
3220 pixel=Sc-Dc;
3221 break;
3222 }
3223 pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
3224 break;
3225 }
3226 case ModulateCompositeOp:
3227 {
3228 ssize_t
3229 offset;
3230
3231 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3232 {
3233 pixel=Dc;
3234 break;
3235 }
3236 offset=(ssize_t) (Si-midpoint);
3237 if (offset == 0)
3238 {
3239 pixel=Dc;
3240 break;
3241 }
3242 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3243 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3244 white_luminance,illuminant,&hue,&chroma,&luma);
3245 luma+=(0.01*percent_luma*offset)/midpoint;
3246 chroma*=0.01*percent_chroma;
3247 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3248 white_luminance,illuminant,&red,&green,&blue);
3249 switch (channel)
3250 {
3251 case RedPixelChannel: pixel=red; break;
3252 case GreenPixelChannel: pixel=green; break;
3253 case BluePixelChannel: pixel=blue; break;
3254 default: pixel=Dc; break;
3255 }
3256 break;
3257 }
3258 case ModulusAddCompositeOp:
3259 {
3260 if (compose_sync == MagickFalse)
3261 {
3262 pixel=(Sc+Dc);
3263 break;
3264 }
3265 if ((Sca+Dca) <= 1.0)
3266 {
3267 pixel=(double) QuantumRange*(Sca+Dca);
3268 break;
3269 }
3270 pixel=(double) QuantumRange*((Sca+Dca)-1.0);
3271 break;
3272 }
3273 case ModulusSubtractCompositeOp:
3274 {
3275 if (compose_sync == MagickFalse)
3276 {
3277 pixel=(Sc-Dc);
3278 break;
3279 }
3280 if ((Sca-Dca) >= 0.0)
3281 {
3282 pixel=(double) QuantumRange*(Sca-Dca);
3283 break;
3284 }
3285 pixel=(double) QuantumRange*((Sca-Dca)+1.0);
3286 break;
3287 }
3288 case MultiplyCompositeOp:
3289 {
3290 if (compose_sync == MagickFalse)
3291 {
3292 pixel=(double) QuantumScale*Dc*Sc;
3293 break;
3294 }
3295 pixel=(double) QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*
3296 (1.0-Sa));
3297 break;
3298 }
3299 case NegateCompositeOp:
3300 {
3301 pixel=(double) QuantumRange*(1.0-fabs(1.0-Sca-Dca));
3302 break;
3303 }
3304 case OutCompositeOp:
3305 case SrcOutCompositeOp:
3306 {
3307 pixel=(double) QuantumRange*(Sca*(1.0-Da));
3308 break;
3309 }
3310 case OverCompositeOp:
3311 case SrcOverCompositeOp:
3312 {
3313 pixel=(double) QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
3314 break;
3315 }
3316 case OverlayCompositeOp:
3317 {
3318 if ((2.0*Dca) < Da)
3319 {
3320 pixel=(double) QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+
3321 Sca*(1.0-Da));
3322 break;
3323 }
3324 pixel=(double) QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*
3325 (1.0-Sa)+Sca*(1.0-Da));
3326 break;
3327 }
3328 case PegtopLightCompositeOp:
3329 {
3330 /*
3331 PegTop: A Soft-Light alternative: A continuous version of the
3332 Softlight function, producing very similar results.
3333
3334 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
3335
3336 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
3337 */
3338 if (fabs((double) Da) < MagickEpsilon)
3339 {
3340 pixel=(double) QuantumRange*gamma*Sca;
3341 break;
3342 }
3343 pixel=(double) QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*
3344 (2.0*Dca+1.0-Da)+Dca*(1.0-Sa));
3345 break;
3346 }
3347 case PinLightCompositeOp:
3348 {
3349 /*
3350 PinLight: A Photoshop 7 composition method
3351 http://www.simplefilter.de/en/basics/mixmods.html
3352
3353 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
3354 */
3355 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
3356 {
3357 pixel=(double) QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*
3358 (1.0-Sa));
3359 break;
3360 }
3361 if ((Dca*Sa) > (2.0*Sca*Da))
3362 {
3363 pixel=(double) QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
3364 break;
3365 }
3366 pixel=(double) QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
3367 break;
3368 }
3369 case PlusCompositeOp:
3370 {
3371 if (compose_sync == MagickFalse)
3372 {
3373 pixel=(Dc+Sc);
3374 break;
3375 }
3376 pixel=(double) QuantumRange*(Sca+Dca);
3377 break;
3378 }
3379 case ReflectCompositeOp:
3380 {
3381 pixel=(double) QuantumRange*gamma*(Sca*Sca*
3382 PerceptibleReciprocal(1.0-Dca));
3383 if (pixel > (double) QuantumRange)
3384 pixel=(double) QuantumRange;
3385 break;
3386 }
3387 case RMSECompositeOp:
3388 {
3389 double
3390 gray;
3391
3392 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3393 {
3394 pixel=Dc;
3395 break;
3396 }
3397 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3398 {
3399 pixel=Sc;
3400 break;
3401 }
3402 gray=sqrt(
3403 (canvas_pixel.red-source_pixel.red)*
3404 (canvas_pixel.red-source_pixel.red)+
3405 (canvas_pixel.green-source_pixel.green)*
3406 (canvas_pixel.green-source_pixel.green)+
3407 (canvas_pixel.blue-source_pixel.blue)*
3408 (canvas_pixel.blue-source_pixel.blue)/3.0);
3409 switch (channel)
3410 {
3411 case RedPixelChannel: pixel=gray; break;
3412 case GreenPixelChannel: pixel=gray; break;
3413 case BluePixelChannel: pixel=gray; break;
3414 default: pixel=Dc; break;
3415 }
3416 break;
3417 }
3418 case SaturateCompositeOp:
3419 {
3420 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3421 {
3422 pixel=Dc;
3423 break;
3424 }
3425 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3426 {
3427 pixel=Sc;
3428 break;
3429 }
3430 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3431 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3432 white_luminance,illuminant,&hue,&chroma,&luma);
3433 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3434 (double) source_pixel.green,(double) source_pixel.blue,
3435 white_luminance,illuminant,&sans,&chroma,&sans);
3436 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3437 white_luminance,illuminant,&red,&green,&blue);
3438 switch (channel)
3439 {
3440 case RedPixelChannel: pixel=red; break;
3441 case GreenPixelChannel: pixel=green; break;
3442 case BluePixelChannel: pixel=blue; break;
3443 default: pixel=Dc; break;
3444 }
3445 break;
3446 }
3447 case ScreenCompositeOp:
3448 {
3449 /*
3450 Screen: a negated multiply:
3451
3452 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
3453 */
3454 if (compose_sync == MagickFalse)
3455 {
3456 pixel=Sc+Dc-Sc*Dc;
3457 break;
3458 }
3459 pixel=(double) QuantumRange*gamma*(Sca+Dca-Sca*Dca);
3460 break;
3461 }
3462 case SoftBurnCompositeOp:
3463 {
3464 if ((Sca+Dca) < 1.0)
3465 pixel=(double) QuantumRange*gamma*(0.5*Dca*
3466 PerceptibleReciprocal(1.0-Sca));
3467 else
3468 pixel=(double) QuantumRange*gamma*(1.0-0.5*(1.0-Sca)*
3469 PerceptibleReciprocal(Dca));
3470 break;
3471 }
3472 case SoftDodgeCompositeOp:
3473 {
3474 if ((Sca+Dca) < 1.0)
3475 pixel=(double) QuantumRange*gamma*(0.5*Sca*
3476 PerceptibleReciprocal(1.0-Dca));
3477 else
3478 pixel=(double) QuantumRange*gamma*(1.0-0.5*(1.0-Dca)*
3479 PerceptibleReciprocal(Sca));
3480 break;
3481 }
3482 case SoftLightCompositeOp:
3483 {
3484 if ((2.0*Sca) < Sa)
3485 {
3486 pixel=(double) QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*
3487 (1.0-DcaDa))+Sca*(1.0-Da)+Dca*(1.0-Sa));
3488 break;
3489 }
3490 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
3491 {
3492 pixel=(double) QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*
3493 (4.0*DcaDa*(4.0*DcaDa+1.0)*(DcaDa-1.0)+7.0*DcaDa)+Sca*
3494 (1.0-Da)+Dca*(1.0-Sa));
3495 break;
3496 }
3497 pixel=(double) QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*
3498 (pow(DcaDa,0.5)-DcaDa)+Sca*(1.0-Da)+Dca*(1.0-Sa));
3499 break;
3500 }
3501 case StampCompositeOp:
3502 {
3503 pixel=(double) QuantumRange*(Sca+Dca*Dca-1.0);
3504 break;
3505 }
3506 case StereoCompositeOp:
3507 {
3508 if (channel == RedPixelChannel)
3509 pixel=(MagickRealType) GetPixelRed(source_image,p);
3510 break;
3511 }
3512 case ThresholdCompositeOp:
3513 {
3514 MagickRealType
3515 delta;
3516
3517 delta=Sc-Dc;
3518 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
3519 {
3520 pixel=gamma*Dc;
3521 break;
3522 }
3523 pixel=gamma*(Dc+delta*amount);
3524 break;
3525 }
3526 case VividLightCompositeOp:
3527 {
3528 /*
3529 VividLight: A Photoshop 7 composition method. See
3530 http://www.simplefilter.de/en/basics/mixmods.html.
3531
3532 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
3533 */
3534 if ((fabs((double) Sa) < MagickEpsilon) ||
3535 (fabs((double) (Sca-Sa)) < MagickEpsilon))
3536 {
3537 pixel=(double) QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*
3538 (1.0-Sa));
3539 break;
3540 }
3541 if ((2.0*Sca) <= Sa)
3542 {
3543 pixel=(double) QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)*
3544 PerceptibleReciprocal(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
3545 break;
3546 }
3547 pixel=(double) QuantumRange*gamma*(Dca*Sa*Sa*
3548 PerceptibleReciprocal(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
3549 break;
3550 }
3551 case XorCompositeOp:
3552 {
3553 pixel=(double) QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
3554 break;
3555 }
3556 default:
3557 {
3558 pixel=Sc;
3559 break;
3560 }
3561 }
3562 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
3563 }
3564 p+=(ptrdiff_t) GetPixelChannels(source_image);
3565 channels=GetPixelChannels(source_image);
3566 if (p >= (pixels+channels*source_image->columns))
3567 p=pixels;
3568 q+=(ptrdiff_t) GetPixelChannels(image);
3569 }
3570 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3571 status=MagickFalse;
3572 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3573 {
3574 MagickBooleanType
3575 proceed;
3576
3577#if defined(MAGICKCORE_OPENMP_SUPPORT)
3578 #pragma omp atomic
3579#endif
3580 progress++;
3581 proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
3582 if (proceed == MagickFalse)
3583 status=MagickFalse;
3584 }
3585 }
3586 source_view=DestroyCacheView(source_view);
3587 image_view=DestroyCacheView(image_view);
3588 if (canvas_image != (Image * ) NULL)
3589 canvas_image=DestroyImage(canvas_image);
3590 else
3591 source_image=DestroyImage(source_image);
3592 return(status);
3593}
3594
3595/*
3596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3597% %
3598% %
3599% %
3600% T e x t u r e I m a g e %
3601% %
3602% %
3603% %
3604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3605%
3606% TextureImage() repeatedly tiles the texture image across and down the image
3607% canvas.
3608%
3609% The format of the TextureImage method is:
3610%
3611% MagickBooleanType TextureImage(Image *image,const Image *texture,
3612% ExceptionInfo *exception)
3613%
3614% A description of each parameter follows:
3615%
3616% o image: the image.
3617%
3618% o texture_image: This image is the texture to layer on the background.
3619%
3620*/
3621MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
3622 ExceptionInfo *exception)
3623{
3624#define TextureImageTag "Texture/Image"
3625
3626 CacheView
3627 *image_view,
3628 *texture_view;
3629
3630 Image
3631 *texture_image;
3632
3633 MagickBooleanType
3634 status;
3635
3636 ssize_t
3637 y;
3638
3639 assert(image != (Image *) NULL);
3640 assert(image->signature == MagickCoreSignature);
3641 if (IsEventLogging() != MagickFalse)
3642 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3643 if (texture == (const Image *) NULL)
3644 return(MagickFalse);
3645 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
3646 return(MagickFalse);
3647 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
3648 if (texture_image == (const Image *) NULL)
3649 return(MagickFalse);
3650 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
3651 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
3652 exception);
3653 status=MagickTrue;
3654 if ((image->compose != CopyCompositeOp) &&
3655 ((image->compose != OverCompositeOp) ||
3656 (image->alpha_trait != UndefinedPixelTrait) ||
3657 (texture_image->alpha_trait != UndefinedPixelTrait)))
3658 {
3659 /*
3660 Tile texture onto the image background.
3661 */
3662 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
3663 {
3664 ssize_t
3665 x;
3666
3667 if (status == MagickFalse)
3668 continue;
3669 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
3670 {
3671 MagickBooleanType
3672 thread_status;
3673
3674 thread_status=CompositeImage(image,texture_image,image->compose,
3675 MagickTrue,x+texture_image->tile_offset.x,y+
3676 texture_image->tile_offset.y,exception);
3677 if (thread_status == MagickFalse)
3678 {
3679 status=thread_status;
3680 break;
3681 }
3682 }
3683 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3684 {
3685 MagickBooleanType
3686 proceed;
3687
3688 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3689 image->rows);
3690 if (proceed == MagickFalse)
3691 status=MagickFalse;
3692 }
3693 }
3694 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
3695 image->rows,image->rows);
3696 texture_image=DestroyImage(texture_image);
3697 return(status);
3698 }
3699 /*
3700 Tile texture onto the image background (optimized).
3701 */
3702 status=MagickTrue;
3703 texture_view=AcquireVirtualCacheView(texture_image,exception);
3704 image_view=AcquireAuthenticCacheView(image,exception);
3705#if defined(MAGICKCORE_OPENMP_SUPPORT)
3706 #pragma omp parallel for schedule(static) shared(status) \
3707 magick_number_threads(texture_image,image,image->rows,2)
3708#endif
3709 for (y=0; y < (ssize_t) image->rows; y++)
3710 {
3711 MagickBooleanType
3712 sync;
3713
3714 const Quantum
3715 *p,
3716 *pixels;
3717
3718 ssize_t
3719 x;
3720
3721 Quantum
3722 *q;
3723
3724 size_t
3725 width;
3726
3727 if (status == MagickFalse)
3728 continue;
3729 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
3730 (y+texture_image->tile_offset.y) % (ssize_t) texture_image->rows,
3731 texture_image->columns,1,exception);
3732 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3733 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3734 {
3735 status=MagickFalse;
3736 continue;
3737 }
3738 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
3739 {
3740 ssize_t
3741 j;
3742
3743 p=pixels;
3744 width=texture_image->columns;
3745 if ((x+(ssize_t) width) > (ssize_t) image->columns)
3746 width=image->columns-(size_t) x;
3747 for (j=0; j < (ssize_t) width; j++)
3748 {
3749 ssize_t
3750 i;
3751
3752 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
3753 {
3754 PixelChannel channel = GetPixelChannelChannel(texture_image,i);
3755 PixelTrait traits = GetPixelChannelTraits(image,channel);
3756 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
3757 channel);
3758 if ((traits == UndefinedPixelTrait) ||
3759 (texture_traits == UndefinedPixelTrait))
3760 continue;
3761 SetPixelChannel(image,channel,p[i],q);
3762 }
3763 p+=(ptrdiff_t) GetPixelChannels(texture_image);
3764 q+=(ptrdiff_t) GetPixelChannels(image);
3765 }
3766 }
3767 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3768 if (sync == MagickFalse)
3769 status=MagickFalse;
3770 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3771 {
3772 MagickBooleanType
3773 proceed;
3774
3775 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3776 image->rows);
3777 if (proceed == MagickFalse)
3778 status=MagickFalse;
3779 }
3780 }
3781 texture_view=DestroyCacheView(texture_view);
3782 image_view=DestroyCacheView(image_view);
3783 texture_image=DestroyImage(texture_image);
3784 return(status);
3785}